aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlana <none@none>2009-03-04 10:57:24 -0800
committerlana <none@none>2009-03-04 10:57:24 -0800
commit5598a3e508844f1ccc48350752f8fe2ac0efb724 (patch)
treea03f3b8b765e6d273a64465476ffd400dde8f2c5 /src
parent5c4846db15d50c672bb6f918ea8cb3eb9b3b242c (diff)
parent2240499432eb0e7faf148a47438326faef704f23 (diff)
Merge
Diffstat (limited to 'src')
-rw-r--r--src/share/classes/com/sun/awt/AWTUtilities.java104
-rw-r--r--src/share/classes/com/sun/beans/ObjectHandler.java479
-rw-r--r--src/share/classes/com/sun/beans/decoder/AccessorElementHandler.java105
-rw-r--r--src/share/classes/com/sun/beans/decoder/ArrayElementHandler.java133
-rw-r--r--src/share/classes/com/sun/beans/decoder/BooleanElementHandler.java69
-rw-r--r--src/share/classes/com/sun/beans/decoder/ByteElementHandler.java63
-rw-r--r--src/share/classes/com/sun/beans/decoder/CharElementHandler.java92
-rw-r--r--src/share/classes/com/sun/beans/decoder/ClassElementHandler.java62
-rw-r--r--src/share/classes/com/sun/beans/decoder/DocumentHandler.java389
-rw-r--r--src/share/classes/com/sun/beans/decoder/DoubleElementHandler.java63
-rw-r--r--src/share/classes/com/sun/beans/decoder/ElementHandler.java224
-rw-r--r--src/share/classes/com/sun/beans/decoder/FalseElementHandler.java56
-rw-r--r--src/share/classes/com/sun/beans/decoder/FieldElementHandler.java189
-rw-r--r--src/share/classes/com/sun/beans/decoder/FloatElementHandler.java63
-rw-r--r--src/share/classes/com/sun/beans/decoder/IntElementHandler.java63
-rw-r--r--src/share/classes/com/sun/beans/decoder/JavaElementHandler.java151
-rw-r--r--src/share/classes/com/sun/beans/decoder/LongElementHandler.java63
-rw-r--r--src/share/classes/com/sun/beans/decoder/MethodElementHandler.java109
-rw-r--r--src/share/classes/com/sun/beans/decoder/NewElementHandler.java205
-rw-r--r--src/share/classes/com/sun/beans/decoder/NullElementHandler.java76
-rw-r--r--src/share/classes/com/sun/beans/decoder/ObjectElementHandler.java168
-rw-r--r--src/share/classes/com/sun/beans/decoder/PropertyElementHandler.java287
-rw-r--r--src/share/classes/com/sun/beans/decoder/ShortElementHandler.java63
-rw-r--r--src/share/classes/com/sun/beans/decoder/StringElementHandler.java116
-rw-r--r--src/share/classes/com/sun/beans/decoder/TrueElementHandler.java56
-rw-r--r--src/share/classes/com/sun/beans/decoder/ValueObject.java50
-rw-r--r--src/share/classes/com/sun/beans/decoder/ValueObjectImpl.java88
-rw-r--r--src/share/classes/com/sun/beans/decoder/VarElementHandler.java82
-rw-r--r--src/share/classes/com/sun/beans/decoder/VoidElementHandler.java68
-rw-r--r--src/share/classes/com/sun/beans/finder/AbstractFinder.java213
-rw-r--r--src/share/classes/com/sun/beans/finder/ClassFinder.java99
-rw-r--r--src/share/classes/com/sun/beans/finder/ConstructorFinder.java127
-rw-r--r--src/share/classes/com/sun/beans/finder/FieldFinder.java106
-rw-r--r--src/share/classes/com/sun/beans/finder/MethodFinder.java231
-rw-r--r--src/share/classes/com/sun/beans/finder/PrimitiveTypeMap.java29
-rw-r--r--src/share/classes/com/sun/beans/finder/PrimitiveWrapperMap.java86
-rw-r--r--src/share/classes/com/sun/beans/finder/Signature.java169
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/gtk/GTKColorChooserPanel.java22
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java4
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java33
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java8
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java2
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java20
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java16
-rw-r--r--src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java24
-rw-r--r--src/share/classes/com/sun/media/sound/AbstractMidiDevice.java29
-rw-r--r--src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java131
-rw-r--r--src/share/classes/com/sun/media/sound/AudioFloatConverter.java1058
-rw-r--r--src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java617
-rw-r--r--src/share/classes/com/sun/media/sound/AudioFloatInputStream.java281
-rw-r--r--src/share/classes/com/sun/media/sound/AudioSynthesizer.java128
-rw-r--r--src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java76
-rw-r--r--src/share/classes/com/sun/media/sound/DLSInfo.java109
-rw-r--r--src/share/classes/com/sun/media/sound/DLSInstrument.java448
-rw-r--r--src/share/classes/com/sun/media/sound/DLSModulator.java351
-rw-r--r--src/share/classes/com/sun/media/sound/DLSRegion.java150
-rw-r--r--src/share/classes/com/sun/media/sound/DLSSample.java122
-rw-r--r--src/share/classes/com/sun/media/sound/DLSSampleLoop.java63
-rw-r--r--src/share/classes/com/sun/media/sound/DLSSampleOptions.java80
-rw-r--r--src/share/classes/com/sun/media/sound/DLSSoundbank.java1287
-rw-r--r--src/share/classes/com/sun/media/sound/DLSSoundbankReader.java74
-rw-r--r--src/share/classes/com/sun/media/sound/DirectAudioDevice.java108
-rw-r--r--src/share/classes/com/sun/media/sound/EmergencySoundbank.java2695
-rw-r--r--src/share/classes/com/sun/media/sound/FFT.java748
-rw-r--r--src/share/classes/com/sun/media/sound/InvalidDataException.java45
-rw-r--r--src/share/classes/com/sun/media/sound/InvalidFormatException.java44
-rw-r--r--src/share/classes/com/sun/media/sound/JARSoundbankReader.java120
-rw-r--r--src/share/classes/com/sun/media/sound/ModelAbstractChannelMixer.java126
-rw-r--r--src/share/classes/com/sun/media/sound/ModelAbstractOscillator.java200
-rw-r--r--src/share/classes/com/sun/media/sound/ModelByteBuffer.java329
-rw-r--r--src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java281
-rw-r--r--src/share/classes/com/sun/media/sound/ModelChannelMixer.java50
-rw-r--r--src/share/classes/com/sun/media/sound/ModelConnectionBlock.java135
-rw-r--r--src/share/classes/com/sun/media/sound/ModelDestination.java117
-rw-r--r--src/share/classes/com/sun/media/sound/ModelDirectedPlayer.java36
-rw-r--r--src/share/classes/com/sun/media/sound/ModelDirector.java46
-rw-r--r--src/share/classes/com/sun/media/sound/ModelIdentifier.java169
-rw-r--r--src/share/classes/com/sun/media/sound/ModelInstrument.java136
-rw-r--r--src/share/classes/com/sun/media/sound/ModelInstrumentComparator.java52
-rw-r--r--src/share/classes/com/sun/media/sound/ModelMappedInstrument.java62
-rw-r--r--src/share/classes/com/sun/media/sound/ModelOscillator.java44
-rw-r--r--src/share/classes/com/sun/media/sound/ModelOscillatorStream.java48
-rw-r--r--src/share/classes/com/sun/media/sound/ModelPatch.java52
-rw-r--r--src/share/classes/com/sun/media/sound/ModelPerformer.java143
-rw-r--r--src/share/classes/com/sun/media/sound/ModelSource.java109
-rw-r--r--src/share/classes/com/sun/media/sound/ModelStandardDirector.java86
-rw-r--r--src/share/classes/com/sun/media/sound/ModelStandardTransform.java139
-rw-r--r--src/share/classes/com/sun/media/sound/ModelTransform.java35
-rw-r--r--src/share/classes/com/sun/media/sound/ModelWavetable.java49
-rw-r--r--src/share/classes/com/sun/media/sound/Platform.java5
-rw-r--r--src/share/classes/com/sun/media/sound/RIFFInvalidDataException.java43
-rw-r--r--src/share/classes/com/sun/media/sound/RIFFInvalidFormatException.java44
-rw-r--r--src/share/classes/com/sun/media/sound/RIFFReader.java332
-rw-r--r--src/share/classes/com/sun/media/sound/RIFFWriter.java365
-rw-r--r--src/share/classes/com/sun/media/sound/RealTimeSequencer.java337
-rw-r--r--src/share/classes/com/sun/media/sound/SF2GlobalRegion.java33
-rw-r--r--src/share/classes/com/sun/media/sound/SF2Instrument.java911
-rw-r--r--src/share/classes/com/sun/media/sound/SF2InstrumentRegion.java43
-rw-r--r--src/share/classes/com/sun/media/sound/SF2Layer.java78
-rw-r--r--src/share/classes/com/sun/media/sound/SF2LayerRegion.java43
-rw-r--r--src/share/classes/com/sun/media/sound/SF2Modulator.java97
-rw-r--r--src/share/classes/com/sun/media/sound/SF2Region.java167
-rw-r--r--src/share/classes/com/sun/media/sound/SF2Sample.java216
-rw-r--r--src/share/classes/com/sun/media/sound/SF2Soundbank.java973
-rw-r--r--src/share/classes/com/sun/media/sound/SF2SoundbankReader.java73
-rw-r--r--src/share/classes/com/sun/media/sound/SimpleInstrument.java196
-rw-r--r--src/share/classes/com/sun/media/sound/SimpleSoundbank.java145
-rw-r--r--src/share/classes/com/sun/media/sound/SoftAbstractResampler.java390
-rw-r--r--src/share/classes/com/sun/media/sound/SoftAudioBuffer.java104
-rw-r--r--src/share/classes/com/sun/media/sound/SoftAudioProcessor.java48
-rw-r--r--src/share/classes/com/sun/media/sound/SoftAudioPusher.java91
-rw-r--r--src/share/classes/com/sun/media/sound/SoftChannel.java1548
-rw-r--r--src/share/classes/com/sun/media/sound/SoftChannelProxy.java202
-rw-r--r--src/share/classes/com/sun/media/sound/SoftChorus.java341
-rw-r--r--src/share/classes/com/sun/media/sound/SoftControl.java36
-rw-r--r--src/share/classes/com/sun/media/sound/SoftCubicResampler.java87
-rw-r--r--src/share/classes/com/sun/media/sound/SoftEnvelopeGenerator.java298
-rw-r--r--src/share/classes/com/sun/media/sound/SoftFilter.java614
-rw-r--r--src/share/classes/com/sun/media/sound/SoftInstrument.java82
-rw-r--r--src/share/classes/com/sun/media/sound/SoftJitterCorrector.java276
-rw-r--r--src/share/classes/com/sun/media/sound/SoftLanczosResampler.java118
-rw-r--r--src/share/classes/com/sun/media/sound/SoftLimiter.java191
-rw-r--r--src/share/classes/com/sun/media/sound/SoftLinearResampler.java70
-rw-r--r--src/share/classes/com/sun/media/sound/SoftLinearResampler2.java108
-rw-r--r--src/share/classes/com/sun/media/sound/SoftLowFrequencyOscillator.java122
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMainMixer.java982
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java214
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMixingClip.java539
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMixingDataLine.java522
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMixingMainMixer.java259
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMixingMixer.java529
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMixingMixerProvider.java66
-rw-r--r--src/share/classes/com/sun/media/sound/SoftMixingSourceDataLine.java519
-rw-r--r--src/share/classes/com/sun/media/sound/SoftPerformer.java775
-rw-r--r--src/share/classes/com/sun/media/sound/SoftPointResampler.java63
-rw-r--r--src/share/classes/com/sun/media/sound/SoftProcess.java41
-rw-r--r--src/share/classes/com/sun/media/sound/SoftProvider.java51
-rw-r--r--src/share/classes/com/sun/media/sound/SoftReceiver.java83
-rw-r--r--src/share/classes/com/sun/media/sound/SoftResampler.java35
-rw-r--r--src/share/classes/com/sun/media/sound/SoftResamplerStreamer.java38
-rw-r--r--src/share/classes/com/sun/media/sound/SoftReverb.java515
-rw-r--r--src/share/classes/com/sun/media/sound/SoftShortMessage.java58
-rw-r--r--src/share/classes/com/sun/media/sound/SoftSincResampler.java139
-rw-r--r--src/share/classes/com/sun/media/sound/SoftSynthesizer.java1179
-rw-r--r--src/share/classes/com/sun/media/sound/SoftTuning.java256
-rw-r--r--src/share/classes/com/sun/media/sound/SoftVoice.java841
-rw-r--r--src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java339
-rw-r--r--src/share/classes/com/sun/media/sound/WaveFloatFileReader.java166
-rw-r--r--src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java147
-rw-r--r--src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiDeviceProvider2
-rw-r--r--src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiFileReader1
-rw-r--r--src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.SoundbankReader5
-rw-r--r--src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.AudioFileReader2
-rw-r--r--src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider1
-rw-r--r--src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.MixerProvider2
-rw-r--r--src/share/classes/com/sun/nio/file/ExtendedCopyOption.java43
-rw-r--r--src/share/classes/com/sun/nio/file/ExtendedOpenOption.java (renamed from src/windows/native/sun/windows/awt_Multimon.h)50
-rw-r--r--src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java43
-rw-r--r--src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java62
-rw-r--r--src/share/classes/java/awt/Choice.java2
-rw-r--r--src/share/classes/java/awt/Component.java268
-rw-r--r--src/share/classes/java/awt/Container.java80
-rw-r--r--src/share/classes/java/awt/Dialog.java2
-rw-r--r--src/share/classes/java/awt/EventDispatchThread.java112
-rw-r--r--src/share/classes/java/awt/List.java10
-rw-r--r--src/share/classes/java/awt/MenuItem.java4
-rw-r--r--src/share/classes/java/awt/Robot.java122
-rw-r--r--src/share/classes/java/awt/TextArea.java8
-rw-r--r--src/share/classes/java/awt/TextField.java6
-rw-r--r--src/share/classes/java/awt/Toolkit.java35
-rw-r--r--src/share/classes/java/awt/Window.java7
-rw-r--r--src/share/classes/java/awt/doc-files/DesktopProperties.html52
-rw-r--r--src/share/classes/java/awt/event/InputEvent.java106
-rw-r--r--src/share/classes/java/awt/event/MouseEvent.java246
-rw-r--r--src/share/classes/java/awt/peer/ButtonPeer.java13
-rw-r--r--src/share/classes/java/awt/peer/CanvasPeer.java4
-rw-r--r--src/share/classes/java/awt/peer/CheckboxMenuItemPeer.java13
-rw-r--r--src/share/classes/java/awt/peer/CheckboxPeer.java31
-rw-r--r--src/share/classes/java/awt/peer/ChoicePeer.java40
-rw-r--r--src/share/classes/java/awt/peer/ComponentPeer.java534
-rw-r--r--src/share/classes/java/awt/peer/ContainerPeer.java53
-rw-r--r--src/share/classes/java/awt/peer/DesktopPeer.java19
-rw-r--r--src/share/classes/java/awt/peer/DialogPeer.java29
-rw-r--r--src/share/classes/java/awt/peer/FileDialogPeer.java28
-rw-r--r--src/share/classes/java/awt/peer/FontPeer.java3
-rw-r--r--src/share/classes/java/awt/peer/FramePeer.java86
-rw-r--r--src/share/classes/java/awt/peer/KeyboardFocusManagerPeer.java34
-rw-r--r--src/share/classes/java/awt/peer/LabelPeer.java23
-rw-r--r--src/share/classes/java/awt/peer/ListPeer.java96
-rw-r--r--src/share/classes/java/awt/peer/MenuBarPeer.java27
-rw-r--r--src/share/classes/java/awt/peer/MenuComponentPeer.java18
-rw-r--r--src/share/classes/java/awt/peer/MenuItemPeer.java20
-rw-r--r--src/share/classes/java/awt/peer/MenuPeer.java25
-rw-r--r--src/share/classes/java/awt/peer/MouseInfoPeer.java3
-rw-r--r--src/share/classes/java/awt/peer/PanelPeer.java4
-rw-r--r--src/share/classes/java/awt/peer/PopupMenuPeer.java12
-rw-r--r--src/share/classes/java/awt/peer/RobotPeer.java96
-rw-r--r--src/share/classes/java/awt/peer/ScrollPanePeer.java54
-rw-r--r--src/share/classes/java/awt/peer/ScrollbarPeer.java31
-rw-r--r--src/share/classes/java/awt/peer/SystemTrayPeer.java13
-rw-r--r--src/share/classes/java/awt/peer/TextAreaPeer.java51
-rw-r--r--src/share/classes/java/awt/peer/TextComponentPeer.java79
-rw-r--r--src/share/classes/java/awt/peer/TextFieldPeer.java37
-rw-r--r--src/share/classes/java/awt/peer/TrayIconPeer.java46
-rw-r--r--src/share/classes/java/awt/peer/WindowPeer.java51
-rw-r--r--src/share/classes/java/beans/EventHandler.java12
-rw-r--r--src/share/classes/java/beans/MetaData.java4
-rw-r--r--src/share/classes/java/beans/ReflectionUtils.java361
-rw-r--r--src/share/classes/java/beans/Statement.java26
-rw-r--r--src/share/classes/java/beans/XMLDecoder.java153
-rw-r--r--src/share/classes/java/io/File.java347
-rw-r--r--src/share/classes/java/io/FilePermission.java58
-rw-r--r--src/share/classes/java/io/InputStream.java2
-rw-r--r--src/share/classes/java/lang/Double.java17
-rw-r--r--src/share/classes/java/lang/Enum.java24
-rw-r--r--src/share/classes/java/lang/Float.java17
-rw-r--r--src/share/classes/java/lang/Object.java192
-rw-r--r--src/share/classes/java/lang/RuntimePermission.java7
-rw-r--r--src/share/classes/java/lang/Thread.java101
-rw-r--r--src/share/classes/java/lang/annotation/Annotation.java6
-rw-r--r--src/share/classes/java/lang/instrument/package.html25
-rw-r--r--src/share/classes/java/net/CookieManager.java3
-rw-r--r--src/share/classes/java/net/DatagramSocket.java24
-rw-r--r--src/share/classes/java/net/HttpCookie.java6
-rw-r--r--src/share/classes/java/net/StandardProtocolFamily.java4
-rw-r--r--src/share/classes/java/net/StandardSocketOption.java39
-rw-r--r--src/share/classes/java/net/URLClassLoader.java69
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousByteChannel.java205
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousChannel.java116
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousChannelGroup.java344
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java718
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousFileChannel.java774
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java303
-rw-r--r--src/share/classes/java/nio/channels/AsynchronousSocketChannel.java670
-rw-r--r--src/share/classes/java/nio/channels/Channels.java157
-rw-r--r--src/share/classes/java/nio/channels/CompletionHandler.java77
-rw-r--r--src/share/classes/java/nio/channels/DatagramChannel.java18
-rw-r--r--src/share/classes/java/nio/channels/FileChannel.java252
-rw-r--r--src/share/classes/java/nio/channels/FileLock.java77
-rw-r--r--src/share/classes/java/nio/channels/MembershipKey.java22
-rw-r--r--src/share/classes/java/nio/channels/MulticastChannel.java27
-rw-r--r--src/share/classes/java/nio/channels/NetworkChannel.java16
-rw-r--r--src/share/classes/java/nio/channels/SeekableByteChannel.java168
-rw-r--r--src/share/classes/java/nio/channels/ServerSocketChannel.java6
-rw-r--r--src/share/classes/java/nio/channels/SocketChannel.java16
-rw-r--r--src/share/classes/java/nio/channels/exceptions37
-rw-r--r--src/share/classes/java/nio/channels/package-info.java64
-rw-r--r--src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java264
-rw-r--r--src/share/classes/java/nio/channels/spi/SelectorProvider.java6
-rw-r--r--src/share/classes/java/nio/channels/spi/package.html6
-rw-r--r--src/share/classes/java/nio/file/AccessDeniedException.java68
-rw-r--r--src/share/classes/java/nio/file/AccessMode.java49
-rw-r--r--src/share/classes/java/nio/file/AtomicMoveNotSupportedException.java56
-rw-r--r--src/share/classes/java/nio/file/ClosedDirectoryStreamException.java (renamed from src/windows/native/sun/windows/awt_Unicode.cpp)35
-rw-r--r--src/share/classes/java/nio/file/ClosedFileSystemException.java43
-rw-r--r--src/share/classes/java/nio/file/ClosedWatchServiceException.java43
-rw-r--r--src/share/classes/java/nio/file/CopyOption.java41
-rw-r--r--src/share/classes/java/nio/file/DirectoryNotEmptyException.java49
-rw-r--r--src/share/classes/java/nio/file/DirectoryStream.java138
-rw-r--r--src/share/classes/java/nio/file/DirectoryStreamFilters.java210
-rw-r--r--src/share/classes/java/nio/file/FileAction.java64
-rw-r--r--src/share/classes/java/nio/file/FileAlreadyExistsException.java63
-rw-r--r--src/share/classes/java/nio/file/FileRef.java424
-rw-r--r--src/share/classes/java/nio/file/FileStore.java169
-rw-r--r--src/share/classes/java/nio/file/FileSystem.java453
-rw-r--r--src/share/classes/java/nio/file/FileSystemAlreadyExistsException.java53
-rw-r--r--src/share/classes/java/nio/file/FileSystemException.java125
-rw-r--r--src/share/classes/java/nio/file/FileSystemNotFoundException.java52
-rw-r--r--src/share/classes/java/nio/file/FileSystems.java413
-rw-r--r--src/share/classes/java/nio/file/FileTreeWalker.java255
-rw-r--r--src/share/classes/java/nio/file/FileVisitOption.java45
-rw-r--r--src/share/classes/java/nio/file/FileVisitResult.java62
-rw-r--r--src/share/classes/java/nio/file/FileVisitor.java175
-rw-r--r--src/share/classes/java/nio/file/Files.java406
-rw-r--r--src/share/classes/java/nio/file/InvalidPathException.java130
-rw-r--r--src/share/classes/java/nio/file/LinkOption.java43
-rw-r--r--src/share/classes/java/nio/file/LinkPermission.java107
-rw-r--r--src/share/classes/java/nio/file/NoSuchFileException.java63
-rw-r--r--src/share/classes/java/nio/file/NotDirectoryException.java49
-rw-r--r--src/share/classes/java/nio/file/NotLinkException.java63
-rw-r--r--src/share/classes/java/nio/file/OpenOption.java45
-rw-r--r--src/share/classes/java/nio/file/Path.java1613
-rw-r--r--src/share/classes/java/nio/file/PathMatcher.java49
-rw-r--r--src/share/classes/java/nio/file/Paths.java133
-rw-r--r--src/share/classes/java/nio/file/ProviderMismatchException.java53
-rw-r--r--src/share/classes/java/nio/file/ProviderNotFoundException.java52
-rw-r--r--src/share/classes/java/nio/file/ReadOnlyFileSystemException.java43
-rw-r--r--src/share/classes/java/nio/file/SecureDirectoryStream.java324
-rw-r--r--src/share/classes/java/nio/file/SimpleFileVisitor.java121
-rw-r--r--src/share/classes/java/nio/file/StandardCopyOption.java47
-rw-r--r--src/share/classes/java/nio/file/StandardOpenOption.java125
-rw-r--r--src/share/classes/java/nio/file/StandardWatchEventKind.java94
-rw-r--r--src/share/classes/java/nio/file/WatchEvent.java116
-rw-r--r--src/share/classes/java/nio/file/WatchKey.java138
-rw-r--r--src/share/classes/java/nio/file/WatchService.java178
-rw-r--r--src/share/classes/java/nio/file/Watchable.java127
-rw-r--r--src/share/classes/java/nio/file/attribute/AclEntry.java394
-rw-r--r--src/share/classes/java/nio/file/attribute/AclEntryFlag.java65
-rw-r--r--src/share/classes/java/nio/file/attribute/AclEntryPermission.java130
-rw-r--r--src/share/classes/java/nio/file/attribute/AclEntryType.java (renamed from src/windows/native/sun/windows/awt_Unicode.h)48
-rw-r--r--src/share/classes/java/nio/file/attribute/AclFileAttributeView.java211
-rw-r--r--src/share/classes/java/nio/file/attribute/AttributeView.java118
-rw-r--r--src/share/classes/java/nio/file/attribute/Attributes.java703
-rw-r--r--src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java186
-rw-r--r--src/share/classes/java/nio/file/attribute/BasicFileAttributes.java163
-rw-r--r--src/share/classes/java/nio/file/attribute/DosFileAttributeView.java179
-rw-r--r--src/share/classes/java/nio/file/attribute/DosFileAttributes.java84
-rw-r--r--src/share/classes/java/nio/file/attribute/FileAttribute.java50
-rw-r--r--src/share/classes/java/nio/file/attribute/FileAttributeView.java43
-rw-r--r--src/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java101
-rw-r--r--src/share/classes/java/nio/file/attribute/FileStoreAttributeView.java38
-rw-r--r--src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributeView.java85
-rw-r--r--src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributes.java66
-rw-r--r--src/share/classes/java/nio/file/attribute/GroupPrincipal.java42
-rw-r--r--src/share/classes/java/nio/file/attribute/PosixFileAttributeView.java196
-rw-r--r--src/share/classes/java/nio/file/attribute/PosixFileAttributes.java77
-rw-r--r--src/share/classes/java/nio/file/attribute/PosixFilePermission.java86
-rw-r--r--src/share/classes/java/nio/file/attribute/PosixFilePermissions.java180
-rw-r--r--src/share/classes/java/nio/file/attribute/UserDefinedFileAttributeView.java232
-rw-r--r--src/share/classes/java/nio/file/attribute/UserPrincipal.java54
-rw-r--r--src/share/classes/java/nio/file/attribute/UserPrincipalLookupService.java104
-rw-r--r--src/share/classes/java/nio/file/attribute/UserPrincipalNotFoundException.java64
-rw-r--r--src/share/classes/java/nio/file/attribute/package-info.java119
-rw-r--r--src/share/classes/java/nio/file/package-info.java116
-rw-r--r--src/share/classes/java/nio/file/spi/AbstractPath.java568
-rw-r--r--src/share/classes/java/nio/file/spi/FileSystemProvider.java434
-rw-r--r--src/share/classes/java/nio/file/spi/FileTypeDetector.java106
-rw-r--r--src/share/classes/java/nio/file/spi/package-info.java39
-rw-r--r--src/share/classes/java/util/Calendar.java4
-rw-r--r--src/share/classes/java/util/Collection.java2
-rw-r--r--src/share/classes/java/util/CurrencyData.properties4
-rw-r--r--src/share/classes/java/util/Formatter.java919
-rw-r--r--src/share/classes/java/util/LocaleISOData.java4
-rw-r--r--src/share/classes/java/util/Random.java2
-rw-r--r--src/share/classes/java/util/Scanner.java46
-rw-r--r--src/share/classes/java/util/logging/Logger.java61
-rw-r--r--src/share/classes/java/util/regex/Matcher.java128
-rw-r--r--src/share/classes/java/util/regex/Pattern.java93
-rw-r--r--src/share/classes/javax/swing/ImageIcon.java2
-rw-r--r--src/share/classes/javax/swing/JRootPane.java4
-rw-r--r--src/share/classes/javax/swing/ProgressMonitor.java13
-rw-r--r--src/share/classes/javax/swing/RepaintManager.java22
-rw-r--r--src/share/classes/javax/swing/SwingWorker.java4
-rw-r--r--src/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java14
-rw-r--r--src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java17
-rw-r--r--src/share/classes/javax/swing/colorchooser/DefaultColorSelectionModel.java3
-rw-r--r--src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java15
-rw-r--r--src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java17
-rw-r--r--src/share/classes/javax/swing/plaf/basic/BasicSliderUI.java52
-rw-r--r--src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java24
-rw-r--r--src/share/classes/javax/swing/plaf/synth/SynthParser.java105
-rw-r--r--src/share/classes/javax/swing/table/DefaultTableColumnModel.java7
-rw-r--r--src/share/classes/javax/swing/tree/DefaultMutableTreeNode.java21
-rw-r--r--src/share/classes/javax/swing/undo/CompoundEdit.java6
-rw-r--r--src/share/classes/sun/awt/AWTAccessor.java77
-rw-r--r--src/share/classes/sun/awt/HeadlessToolkit.java10
-rw-r--r--src/share/classes/sun/awt/SunToolkit.java41
-rw-r--r--src/share/classes/sun/beans/editors/EnumEditor.java2
-rw-r--r--src/share/classes/sun/java2d/pipe/Region.java39
-rw-r--r--src/share/classes/sun/misc/URLClassPath.java61
-rw-r--r--src/share/classes/sun/nio/ch/AbstractFuture.java63
-rw-r--r--src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java341
-rw-r--r--src/share/classes/sun/nio/ch/AsynchronousFileChannelImpl.java164
-rw-r--r--src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java219
-rw-r--r--src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java542
-rw-r--r--src/share/classes/sun/nio/ch/Cancellable.java39
-rw-r--r--src/share/classes/sun/nio/ch/CompletedFuture.java113
-rw-r--r--src/share/classes/sun/nio/ch/DatagramChannelImpl.java102
-rw-r--r--src/share/classes/sun/nio/ch/ExtendedSocketOption.java2
-rw-r--r--src/share/classes/sun/nio/ch/FileChannelImpl.java404
-rw-r--r--src/share/classes/sun/nio/ch/FileDispatcher.java48
-rw-r--r--src/share/classes/sun/nio/ch/FileLockImpl.java21
-rw-r--r--src/share/classes/sun/nio/ch/FileLockTable.java282
-rw-r--r--src/share/classes/sun/nio/ch/Groupable.java35
-rw-r--r--src/share/classes/sun/nio/ch/IOUtil.java243
-rw-r--r--src/share/classes/sun/nio/ch/Invoker.java261
-rw-r--r--src/share/classes/sun/nio/ch/MembershipKeyImpl.java20
-rw-r--r--src/share/classes/sun/nio/ch/MembershipRegistry.java22
-rw-r--r--src/share/classes/sun/nio/ch/NativeThreadSet.java14
-rw-r--r--src/share/classes/sun/nio/ch/Net.java6
-rw-r--r--src/share/classes/sun/nio/ch/OptionKey.java2
-rw-r--r--src/share/classes/sun/nio/ch/PendingFuture.java257
-rw-r--r--src/share/classes/sun/nio/ch/Reflect.java6
-rw-r--r--src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java23
-rw-r--r--src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java612
-rw-r--r--src/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java426
-rw-r--r--src/share/classes/sun/nio/ch/SocketChannelImpl.java24
-rw-r--r--src/share/classes/sun/nio/ch/ThreadPool.java176
-rw-r--r--src/share/classes/sun/nio/ch/Util.java6
-rw-r--r--src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java110
-rw-r--r--src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java208
-rw-r--r--src/share/classes/sun/nio/fs/AbstractFileStoreSpaceAttributeView.java119
-rw-r--r--src/share/classes/sun/nio/fs/AbstractFileTypeDetector.java69
-rw-r--r--src/share/classes/sun/nio/fs/AbstractPoller.java290
-rw-r--r--src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java124
-rw-r--r--src/share/classes/sun/nio/fs/AbstractWatchKey.java180
-rw-r--r--src/share/classes/sun/nio/fs/AbstractWatchService.java161
-rw-r--r--src/share/classes/sun/nio/fs/BasicFileAttributesHolder.java46
-rw-r--r--src/share/classes/sun/nio/fs/Cancellable.java137
-rw-r--r--src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java111
-rw-r--r--src/share/classes/sun/nio/fs/Globs.java217
-rw-r--r--src/share/classes/sun/nio/fs/MimeType.java73
-rw-r--r--src/share/classes/sun/nio/fs/NativeBuffer.java87
-rw-r--r--src/share/classes/sun/nio/fs/NativeBuffers.java140
-rw-r--r--src/share/classes/sun/nio/fs/PollingWatchService.java429
-rw-r--r--src/share/classes/sun/nio/fs/Reflect.java63
-rw-r--r--src/share/classes/sun/security/krb5/Config.java164
-rw-r--r--src/share/classes/sun/security/krb5/KrbServiceLocator.java4
-rw-r--r--src/share/classes/sun/security/krb5/Realm.java43
-rw-r--r--src/share/classes/sun/security/provider/X509Factory.java30
-rw-r--r--src/share/classes/sun/security/ssl/AppInputStream.java10
-rw-r--r--src/share/classes/sun/security/ssl/AppOutputStream.java15
-rw-r--r--src/share/classes/sun/security/ssl/ByteBufferInputStream.java5
-rw-r--r--src/share/classes/sun/security/ssl/SSLSessionContextImpl.java254
-rw-r--r--src/share/classes/sun/security/ssl/SSLSocketImpl.java13
-rw-r--r--src/share/classes/sun/security/tools/KeyTool.java873
-rw-r--r--src/share/classes/sun/security/util/Cache.java88
-rw-r--r--src/share/classes/sun/security/util/DerValue.java11
-rw-r--r--src/share/classes/sun/security/util/Resources.java27
-rw-r--r--src/share/classes/sun/security/util/SecurityConstants.java3
-rw-r--r--src/share/classes/sun/security/x509/AccessDescription.java29
-rw-r--r--src/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java5
-rw-r--r--src/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java4
-rw-r--r--src/share/classes/sun/security/x509/BasicConstraintsExtension.java15
-rw-r--r--src/share/classes/sun/security/x509/CertAndKeyGen.java8
-rw-r--r--src/share/classes/sun/security/x509/CertificateExtensions.java11
-rw-r--r--src/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java18
-rw-r--r--src/share/classes/sun/security/x509/OIDMap.java6
-rw-r--r--src/share/classes/sun/security/x509/SubjectInfoAccessExtension.java244
-rw-r--r--src/share/classes/sun/swing/FilePane.java15
-rw-r--r--src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java24
-rw-r--r--src/share/classes/sun/text/resources/FormatData_th.java29
-rw-r--r--src/share/classes/sun/tools/jar/Main.java156
-rw-r--r--src/share/classes/sun/util/resources/LocaleNames.properties2
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_de.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_es.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_fr.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_it.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_ja.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_ko.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_sv.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java11
-rw-r--r--src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java11
-rw-r--r--src/share/lib/audio/soundbank.gmbin493589 -> 0 bytes
-rw-r--r--src/share/native/java/util/zip/zip_util.c13
-rw-r--r--src/share/native/java/util/zip/zip_util.h2
-rw-r--r--src/share/native/sun/nio/ch/genSocketOptionRegistry.c8
-rw-r--r--src/share/sample/nio/file/AclEdit.java296
-rw-r--r--src/share/sample/nio/file/Chmod.java347
-rw-r--r--src/share/sample/nio/file/Copy.java217
-rw-r--r--src/share/sample/nio/file/DiskUsage.java74
-rw-r--r--src/share/sample/nio/file/FileType.java57
-rw-r--r--src/share/sample/nio/file/WatchDir.java196
-rw-r--r--src/share/sample/nio/file/Xdd.java112
-rw-r--r--src/solaris/classes/sun/awt/X11/WindowDimensions.java16
-rw-r--r--src/solaris/classes/sun/awt/X11/XBaseWindow.java25
-rw-r--r--src/solaris/classes/sun/awt/X11/XComponentPeer.java24
-rw-r--r--src/solaris/classes/sun/awt/X11/XConstants.java36
-rw-r--r--src/solaris/classes/sun/awt/X11/XDecoratedPeer.java9
-rw-r--r--src/solaris/classes/sun/awt/X11/XDragSourceContextPeer.java4
-rw-r--r--src/solaris/classes/sun/awt/X11/XEmbedClientHelper.java60
-rw-r--r--src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java22
-rw-r--r--src/solaris/classes/sun/awt/X11/XKeysym.java14
-rw-r--r--src/solaris/classes/sun/awt/X11/XRobotPeer.java7
-rw-r--r--src/solaris/classes/sun/awt/X11/XToolkit.java123
-rw-r--r--src/solaris/classes/sun/awt/X11/XWindow.java87
-rw-r--r--src/solaris/classes/sun/awt/X11/XWindowPeer.java12
-rw-r--r--src/solaris/classes/sun/awt/X11/XlibWrapper.java1
-rw-r--r--src/solaris/classes/sun/awt/X11/keysym2ucs.h14
-rw-r--r--src/solaris/classes/sun/nio/ch/DatagramDispatcher.java6
-rw-r--r--src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java56
-rw-r--r--src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java4
-rw-r--r--src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java6
-rw-r--r--src/solaris/classes/sun/nio/ch/EPoll.java121
-rw-r--r--src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java4
-rw-r--r--src/solaris/classes/sun/nio/ch/EPollPort.java322
-rw-r--r--src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java6
-rw-r--r--src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java (renamed from src/solaris/classes/sun/nio/ch/FileDispatcher.java)45
-rw-r--r--src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java99
-rw-r--r--src/solaris/classes/sun/nio/ch/PollSelectorImpl.java6
-rw-r--r--src/solaris/classes/sun/nio/ch/Port.java168
-rw-r--r--src/solaris/classes/sun/nio/ch/SinkChannelImpl.java4
-rw-r--r--src/solaris/classes/sun/nio/ch/SocketDispatcher.java14
-rw-r--r--src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java102
-rw-r--r--src/solaris/classes/sun/nio/ch/SolarisEventPort.java244
-rw-r--r--src/solaris/classes/sun/nio/ch/SourceChannelImpl.java4
-rw-r--r--src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java327
-rw-r--r--src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java673
-rw-r--r--src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java73
-rw-r--r--src/solaris/classes/sun/nio/fs/DefaultFileTypeDetector.java36
-rw-r--r--src/solaris/classes/sun/nio/fs/GnomeFileTypeDetector.java101
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java297
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxFileStore.java152
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxFileSystem.java175
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java41
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxNativeDispatcher.java126
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java350
-rw-r--r--src/solaris/classes/sun/nio/fs/LinuxWatchService.java466
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java408
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisFileStore.java103
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisFileSystem.java164
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java41
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java56
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java293
-rw-r--r--src/solaris/classes/sun/nio/fs/SolarisWatchService.java770
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixChannelFactory.java286
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixCopyFile.java608
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java267
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixException.java113
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java392
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileAttributes.java307
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileKey.java56
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileModeAttribute.java84
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileStore.java290
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileStoreAttributes.java59
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileSystem.java380
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java139
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixMountEntry.java85
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java556
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixPath.java1228
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java643
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixUriUtils.java216
-rw-r--r--src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java175
-rw-r--r--src/solaris/native/java/lang/UNIXProcess_md.c8
-rw-r--r--src/solaris/native/java/net/Inet4AddressImpl.c7
-rw-r--r--src/solaris/native/java/net/Inet6AddressImpl.c7
-rw-r--r--src/solaris/native/java/net/NetworkInterface.c32
-rw-r--r--src/solaris/native/sun/awt/awt_Robot.c105
-rw-r--r--src/solaris/native/sun/nio/ch/EPoll.c151
-rw-r--r--src/solaris/native/sun/nio/ch/EPollPort.c76
-rw-r--r--src/solaris/native/sun/nio/ch/FileChannelImpl.c102
-rw-r--r--src/solaris/native/sun/nio/ch/FileDispatcherImpl.c (renamed from src/solaris/native/sun/nio/ch/FileDispatcher.c)133
-rw-r--r--src/solaris/native/sun/nio/ch/SocketDispatcher.c3
-rw-r--r--src/solaris/native/sun/nio/ch/SolarisEventPort.c120
-rw-r--r--src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c47
-rw-r--r--src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c53
-rw-r--r--src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c205
-rw-r--r--src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c160
-rw-r--r--src/solaris/native/sun/nio/fs/LinuxWatchService.c195
-rw-r--r--src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c61
-rw-r--r--src/solaris/native/sun/nio/fs/SolarisWatchService.c104
-rw-r--r--src/solaris/native/sun/nio/fs/UnixCopyFile.c85
-rw-r--r--src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c1080
-rw-r--r--src/solaris/native/sun/nio/fs/genSolarisConstants.c105
-rw-r--r--src/solaris/native/sun/nio/fs/genUnixConstants.c125
-rw-r--r--src/solaris/native/sun/xawt/XlibWrapper.c36
-rw-r--r--src/windows/classes/sun/awt/windows/WComponentPeer.java15
-rw-r--r--src/windows/classes/sun/awt/windows/WInputMethod.java10
-rw-r--r--src/windows/classes/sun/awt/windows/WRobotPeer.java4
-rw-r--r--src/windows/classes/sun/awt/windows/WToolkit.java14
-rw-r--r--src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java43
-rw-r--r--src/windows/classes/sun/nio/ch/FileDispatcherImpl.java (renamed from src/windows/classes/sun/nio/ch/FileDispatcher.java)46
-rw-r--r--src/windows/classes/sun/nio/ch/Iocp.java437
-rw-r--r--src/windows/classes/sun/nio/ch/PendingIoCache.java161
-rw-r--r--src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java101
-rw-r--r--src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java741
-rw-r--r--src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java367
-rw-r--r--src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java911
-rw-r--r--src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java38
-rw-r--r--src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java36
-rw-r--r--src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java82
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java226
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsChannelFactory.java341
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsConstants.java192
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java255
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsException.java109
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java296
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsFileAttributes.java461
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsFileCopy.java519
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsFileStore.java337
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsFileSystem.java317
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java146
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsLinkSupport.java446
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java1134
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsPath.java1375
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsPathParser.java225
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsPathType.java38
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsSecurity.java123
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java392
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsUriSupport.java167
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java342
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java169
-rw-r--r--src/windows/classes/sun/nio/fs/WindowsWatchService.java582
-rw-r--r--src/windows/native/sun/awt/splashscreen/splashscreen_sys.c45
-rw-r--r--src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp22
-rw-r--r--src/windows/native/sun/java2d/d3d/D3DRenderQueue.cpp2
-rw-r--r--src/windows/native/sun/java2d/d3d/D3DRenderer.cpp2
-rw-r--r--src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp3
-rw-r--r--src/windows/native/sun/java2d/windows/GDIBlitLoops.cpp1
-rw-r--r--src/windows/native/sun/java2d/windows/GDIRenderer.cpp4
-rw-r--r--src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp4
-rw-r--r--src/windows/native/sun/java2d/windows/WindowsFlags.cpp2
-rw-r--r--src/windows/native/sun/nio/ch/FileChannelImpl.c175
-rw-r--r--src/windows/native/sun/nio/ch/FileDispatcherImpl.c (renamed from src/windows/native/sun/nio/ch/FileDispatcher.c)146
-rw-r--r--src/windows/native/sun/nio/ch/Iocp.c147
-rw-r--r--src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c132
-rw-r--r--src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c142
-rw-r--r--src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c222
-rw-r--r--src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c62
-rw-r--r--src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c1345
-rw-r--r--src/windows/native/sun/security/krb5/WindowsDirectory.c31
-rw-r--r--src/windows/native/sun/windows/ComCtl32Util.cpp66
-rw-r--r--src/windows/native/sun/windows/ComCtl32Util.h40
-rw-r--r--src/windows/native/sun/windows/Devices.cpp82
-rw-r--r--src/windows/native/sun/windows/Devices.h6
-rw-r--r--src/windows/native/sun/windows/GDIHashtable.cpp53
-rw-r--r--src/windows/native/sun/windows/GDIHashtable.h8
-rw-r--r--src/windows/native/sun/windows/ShellFolder2.cpp171
-rw-r--r--src/windows/native/sun/windows/UnicowsLoader.cpp430
-rw-r--r--src/windows/native/sun/windows/UnicowsLoader.h198
-rw-r--r--src/windows/native/sun/windows/WPrinterJob.cpp116
-rw-r--r--src/windows/native/sun/windows/awt.h73
-rw-r--r--src/windows/native/sun/windows/awt_Button.cpp8
-rw-r--r--src/windows/native/sun/windows/awt_Checkbox.cpp5
-rw-r--r--src/windows/native/sun/windows/awt_Choice.cpp7
-rw-r--r--src/windows/native/sun/windows/awt_Color.cpp4
-rw-r--r--src/windows/native/sun/windows/awt_Component.cpp568
-rw-r--r--src/windows/native/sun/windows/awt_Component.h26
-rw-r--r--src/windows/native/sun/windows/awt_Cursor.cpp36
-rw-r--r--src/windows/native/sun/windows/awt_Cursor.h4
-rw-r--r--src/windows/native/sun/windows/awt_DataTransferer.cpp20
-rw-r--r--src/windows/native/sun/windows/awt_Desktop.cpp20
-rw-r--r--src/windows/native/sun/windows/awt_DesktopProperties.cpp128
-rw-r--r--src/windows/native/sun/windows/awt_Dialog.cpp7
-rw-r--r--src/windows/native/sun/windows/awt_DnDDS.cpp5
-rw-r--r--src/windows/native/sun/windows/awt_DnDDT.cpp8
-rw-r--r--src/windows/native/sun/windows/awt_DrawingSurface.cpp2
-rw-r--r--src/windows/native/sun/windows/awt_FileDialog.cpp35
-rw-r--r--src/windows/native/sun/windows/awt_FileDialog.h43
-rw-r--r--src/windows/native/sun/windows/awt_Font.cpp125
-rw-r--r--src/windows/native/sun/windows/awt_Font.h1
-rw-r--r--src/windows/native/sun/windows/awt_Frame.cpp2
-rw-r--r--src/windows/native/sun/windows/awt_InputMethod.cpp14
-rw-r--r--src/windows/native/sun/windows/awt_InputTextInfor.cpp27
-rw-r--r--src/windows/native/sun/windows/awt_List.cpp16
-rw-r--r--src/windows/native/sun/windows/awt_MMStub.cpp573
-rw-r--r--src/windows/native/sun/windows/awt_MMStub.h131
-rw-r--r--src/windows/native/sun/windows/awt_MenuItem.cpp20
-rw-r--r--src/windows/native/sun/windows/awt_Object.cpp15
-rw-r--r--src/windows/native/sun/windows/awt_Palette.cpp4
-rw-r--r--src/windows/native/sun/windows/awt_PopupMenu.cpp8
-rw-r--r--src/windows/native/sun/windows/awt_PrintControl.cpp148
-rw-r--r--src/windows/native/sun/windows/awt_PrintDialog.cpp8
-rw-r--r--src/windows/native/sun/windows/awt_PrintJob.cpp82
-rw-r--r--src/windows/native/sun/windows/awt_Robot.cpp110
-rw-r--r--src/windows/native/sun/windows/awt_Robot.h4
-rw-r--r--src/windows/native/sun/windows/awt_ScrollPane.cpp22
-rw-r--r--src/windows/native/sun/windows/awt_TextArea.cpp71
-rw-r--r--src/windows/native/sun/windows/awt_TextArea.h8
-rw-r--r--src/windows/native/sun/windows/awt_TextComponent.cpp6
-rw-r--r--src/windows/native/sun/windows/awt_TextComponent.h11
-rw-r--r--src/windows/native/sun/windows/awt_TextField.cpp8
-rw-r--r--src/windows/native/sun/windows/awt_Toolkit.cpp102
-rw-r--r--src/windows/native/sun/windows/awt_Toolkit.h53
-rw-r--r--src/windows/native/sun/windows/awt_TrayIcon.cpp95
-rw-r--r--src/windows/native/sun/windows/awt_TrayIcon.h50
-rw-r--r--src/windows/native/sun/windows/awt_Win32GraphicsConfig.cpp8
-rw-r--r--src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp121
-rw-r--r--src/windows/native/sun/windows/awt_Win32GraphicsDevice.h23
-rw-r--r--src/windows/native/sun/windows/awt_Win32GraphicsEnv.cpp54
-rw-r--r--src/windows/native/sun/windows/awt_Window.cpp101
-rw-r--r--src/windows/native/sun/windows/awt_dlls.cpp422
-rw-r--r--src/windows/native/sun/windows/awt_dlls.h173
-rw-r--r--src/windows/native/sun/windows/awtmsg.h12
-rw-r--r--src/windows/native/sun/windows/jawt.cpp3
666 files changed, 92029 insertions, 8336 deletions
diff --git a/src/share/classes/com/sun/awt/AWTUtilities.java b/src/share/classes/com/sun/awt/AWTUtilities.java
new file mode 100644
index 000000000..818ac6f53
--- /dev/null
+++ b/src/share/classes/com/sun/awt/AWTUtilities.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.awt;
+
+import java.awt.*;
+import sun.awt.AWTAccessor;
+
+
+/**
+ * A collection of utility methods for AWT.
+ *
+ * The functionality provided by the static methods of the class includes:
+ * <ul>
+ * <li>Setting a 'mixing-cutout' shape for a component.
+ * </ul>
+ * <p>
+ * <b>WARNING</b>: This class is an implementation detail and only meant
+ * for limited use outside of the core platform. This API may change
+ * drastically between update release, and it may even be
+ * removed or be moved in some other package(s)/class(es).
+ */
+public final class AWTUtilities {
+
+ /**
+ * The AWTUtilities class should not be instantiated
+ */
+ private AWTUtilities() {
+ }
+
+ /**
+ * Sets a 'mixing-cutout' shape for the given component.
+ *
+ * By default a lightweight component is treated as an opaque rectangle for
+ * the purposes of the Heavyweight/Lightweight Components Mixing feature.
+ * This method enables developers to set an arbitrary shape to be cut out
+ * from heavyweight components positioned underneath the lightweight
+ * component in the z-order.
+ * <p>
+ * The {@code shape} argument may have the following values:
+ * <ul>
+ * <li>{@code null} - reverts the default cutout shape (the rectangle equal
+ * to the component's {@code getBounds()})
+ * <li><i>empty-shape</i> - does not cut out anything from heavyweight
+ * components. This makes the given lightweight component effectively
+ * transparent. Note that descendants of the lightweight component still
+ * affect the shapes of heavyweight components. An example of an
+ * <i>empty-shape</i> is {@code new Rectangle()}.
+ * <li><i>non-empty-shape</i> - the given shape will be cut out from
+ * heavyweight components.
+ * </ul>
+ * <p>
+ * The most common example when the 'mixing-cutout' shape is needed is a
+ * glass pane component. The {@link JRootPane#setGlassPane()} method
+ * automatically sets the <i>empty-shape</i> as the 'mixing-cutout' shape
+ * for the given glass pane component. If a developer needs some other
+ * 'mixing-cutout' shape for the glass pane (which is rare), this must be
+ * changed manually after installing the glass pane to the root pane.
+ * <p>
+ * Note that the 'mixing-cutout' shape neither affects painting, nor the
+ * mouse events handling for the given component. It is used exclusively
+ * for the purposes of the Heavyweight/Lightweight Components Mixing
+ * feature.
+ *
+ * @param component the component that needs non-default
+ * 'mixing-cutout' shape
+ * @param shape the new 'mixing-cutout' shape
+ * @throws NullPointerException if the component argument is {@code null}
+ */
+ public static void setComponentMixingCutoutShape(Component component,
+ Shape shape)
+ {
+ if (component == null) {
+ throw new NullPointerException(
+ "The component argument should not be null.");
+ }
+
+ AWTAccessor.getComponentAccessor().setMixingCutoutShape(component,
+ shape);
+ }
+}
+
diff --git a/src/share/classes/com/sun/beans/ObjectHandler.java b/src/share/classes/com/sun/beans/ObjectHandler.java
deleted file mode 100644
index 6daeab953..000000000
--- a/src/share/classes/com/sun/beans/ObjectHandler.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-package com.sun.beans;
-
-import com.sun.beans.finder.ClassFinder;
-
-import java.beans.*;
-import java.util.*;
-
-import org.xml.sax.*;
-
-import static java.util.Locale.ENGLISH;
-
-/**
- * <b>WARNING</b>: This class is an implementation detail and only meant
- * for use within the core platform. You should NOT depend upon it! This
- * API may change drastically between dot dot release, and it may even be
- * removed.
- *
- * @see java.beans.XMLEncoder
- * @see java.io.ObjectInputStream
- *
- * @since 1.4
- *
- * @author Philip Milne
- */
-public class ObjectHandler extends HandlerBase {
-
- public static Class typeNameToClass(String typeName) {
- typeName = typeName.intern();
- if (typeName == "boolean") return Boolean.class;
- if (typeName == "byte") return Byte.class;
- if (typeName == "char") return Character.class;
- if (typeName == "short") return Short.class;
- if (typeName == "int") return Integer.class;
- if (typeName == "long") return Long.class;
- if (typeName == "float") return Float.class;
- if (typeName == "double") return Double.class;
- if (typeName == "void") return Void.class;
- return null;
- }
-
- public static Class typeNameToPrimitiveClass(String typeName) {
- typeName = typeName.intern();
- if (typeName == "boolean") return boolean.class;
- if (typeName == "byte") return byte.class;
- if (typeName == "char") return char.class;
- if (typeName == "short") return short.class;
- if (typeName == "int") return int.class;
- if (typeName == "long") return long.class;
- if (typeName == "float") return float.class;
- if (typeName == "double") return double.class;
- if (typeName == "void") return void.class;
- return null;
- }
-
- /**
- * Returns the <code>Class</code> object associated with
- * the class or interface with the given string name,
- * using the default class loader.
- *
- * @param name fully qualified name of the desired class
- * @param cl class loader from which the class must be loaded
- * @return class object representing the desired class
- *
- * @exception ClassNotFoundException if the class cannot be located
- * by the specified class loader
- *
- * @deprecated As of JDK version 7, replaced by
- * {@link ClassFinder#resolveClass(String)}.
- */
- @Deprecated
- public static Class classForName(String name) throws ClassNotFoundException {
- return ClassFinder.resolveClass(name);
- }
-
- /**
- * Returns the <code>Class</code> object associated with
- * the class or interface with the given string name,
- * using the given class loader.
- *
- * @param name fully qualified name of the desired class
- * @param cl class loader from which the class must be loaded
- * @return class object representing the desired class
- *
- * @exception ClassNotFoundException if the class cannot be located
- * by the specified class loader
- *
- * @deprecated As of JDK version 7, replaced by
- * {@link ClassFinder#resolveClass(String,ClassLoader)}.
- */
- @Deprecated
- public static Class classForName(String name, ClassLoader cl)
- throws ClassNotFoundException {
- return ClassFinder.resolveClass(name, cl);
- }
-
- private Hashtable environment;
- private Vector expStack;
- private StringBuffer chars;
- private XMLDecoder is;
- private ClassLoader ldr;
- private int itemsRead = 0;
- private boolean isString;
-
- public ObjectHandler() {
- environment = new Hashtable();
- expStack = new Vector();
- chars = new StringBuffer();
- }
-
- public ObjectHandler(XMLDecoder is) {
- this();
- this.is = is;
- }
-
- /* loader can be null */
- public ObjectHandler(XMLDecoder is, ClassLoader loader) {
- this(is);
- this.ldr = loader;
- }
-
-
- public void reset() {
- expStack.clear();
- chars.setLength(0);
- MutableExpression e = new MutableExpression();
- e.setTarget(classForName2("java.lang.Object"));
- e.setMethodName("null");
- expStack.add(e);
- }
-
- private Object getValue(Expression exp) {
- try {
- return exp.getValue();
- }
- catch (Exception e) {
- if (is != null) {
- is.getExceptionListener().exceptionThrown(e);
- }
- return null;
- }
- }
-
- private void addArg(Object arg) {
- lastExp().addArg(arg);
- }
-
- private Object pop(Vector v) {
- int last = v.size()-1;
- Object result = v.get(last);
- v.remove(last);
- return result;
- }
-
- private Object eval() {
- return getValue(lastExp());
- }
-
- private MutableExpression lastExp() {
- return (MutableExpression)expStack.lastElement();
- }
-
- public Object dequeueResult() {
- Object[] results = lastExp().getArguments();
- return results[itemsRead++];
- }
-
- private boolean isPrimitive(String name) {
- return name != "void" && typeNameToClass(name) != null;
- }
-
- private void simulateException(String message) {
- Exception e = new Exception(message);
- e.fillInStackTrace();
- if (is != null) {
- is.getExceptionListener().exceptionThrown(e);
- }
- }
-
- private Class classForName2(String name) {
- try {
- return ClassFinder.resolveClass(name, this.ldr);
- }
- catch (ClassNotFoundException e) {
- if (is != null) {
- is.getExceptionListener().exceptionThrown(e);
- }
- }
- return null;
- }
-
- private HashMap getAttributes(AttributeList attrs) {
- HashMap attributes = new HashMap();
- if (attrs != null && attrs.getLength() > 0) {
- for(int i = 0; i < attrs.getLength(); i++) {
- attributes.put(attrs.getName(i), attrs.getValue(i));
- }
- }
- return attributes;
- }
-
- public void startElement(String name, AttributeList attrs) throws SAXException {
- name = name.intern(); // Xerces parser does not supply unique tag names.
- if (this.isString) {
- parseCharCode(name, getAttributes(attrs));
- return;
- }
- chars.setLength(0);
-
- HashMap attributes = getAttributes(attrs);
- MutableExpression e = new MutableExpression();
-
- // Target
- String className = (String)attributes.get("class");
- if (className != null) {
- e.setTarget(classForName2(className));
- }
-
- // Property
- Object property = attributes.get("property");
- String index = (String)attributes.get("index");
- if (index != null) {
- property = new Integer(index);
- e.addArg(property);
- }
- e.setProperty(property);
-
- // Method
- String methodName = (String)attributes.get("method");
- if (methodName == null && property == null) {
- methodName = "new";
- }
- e.setMethodName(methodName);
-
- // Tags
- if (name == "string") {
- e.setTarget(String.class);
- e.setMethodName("new");
- this.isString = true;
- }
- else if (isPrimitive(name)){
- Class wrapper = typeNameToClass(name);
- e.setTarget(wrapper);
- e.setMethodName("new");
- parseCharCode(name, attributes);
- }
- else if (name == "class") {
- e.setTarget(Class.class);
- e.setMethodName("forName");
- }
- else if (name == "null") {
- // Create an arbitrary expression that has a value of null - for
- // consistency.
- e.setTarget(Object.class);
- e.setMethodName("getSuperclass");
- e.setValue(null);
- }
- else if (name == "void") {
- if (e.getTarget() == null) { // this check is for "void class="foo" method= ..."
- e.setTarget(eval());
- }
- }
- else if (name == "array") {
- // The class attribute means sub-type for arrays.
- String subtypeName = (String)attributes.get("class");
- Class subtype = (subtypeName == null) ? Object.class : classForName2(subtypeName);
- String length = (String)attributes.get("length");
- if (length != null) {
- e.setTarget(java.lang.reflect.Array.class);
- e.addArg(subtype);
- e.addArg(new Integer(length));
- }
- else {
- Class arrayClass = java.lang.reflect.Array.newInstance(subtype, 0).getClass();
- e.setTarget(arrayClass);
- }
- }
- else if (name == "java") {
- e.setValue(is); // The outermost scope is the stream itself.
- }
- else if (name == "object") {
- }
- else {
- simulateException("Unrecognized opening tag: " + name + " " + attrsToString(attrs));
- return;
- }
-
- // ids
- String idName = (String)attributes.get("id");
- if (idName != null) {
- environment.put(idName, e);
- }
-
- // idrefs
- String idrefName = (String)attributes.get("idref");
- if (idrefName != null) {
- e.setValue(lookup(idrefName));
- }
-
- // fields
- String fieldName = (String)attributes.get("field");
- if (fieldName != null) {
- e.setValue(getFieldValue(e.getTarget(), fieldName));
- }
- expStack.add(e);
- }
-
- private Object getFieldValue(Object target, String fieldName) {
- try {
- Class type = target.getClass();
- if (type == Class.class) {
- type = (Class)target;
- }
- java.lang.reflect.Field f = sun.reflect.misc.FieldUtil.getField(type, fieldName);
- return f.get(target);
- }
- catch (Exception e) {
- if (is != null) {
- is.getExceptionListener().exceptionThrown(e);
- }
- return null;
- }
- }
-
- private String attrsToString(AttributeList attrs) {
- StringBuffer b = new StringBuffer();
- for (int i = 0; i < attrs.getLength (); i++) {
- b.append(attrs.getName(i)+"=\""+attrs.getValue(i)+"\" ");
- }
- return b.toString();
- }
-
- public void characters(char buf [], int offset, int len) throws SAXException {
- chars.append(new String(buf, offset, len));
- }
-
- private void parseCharCode(String name, Map map) {
- if (name == "char") {
- String value = (String) map.get("code");
- if (value != null) {
- int code = Integer.decode(value);
- for (char ch : Character.toChars(code)) {
- this.chars.append(ch);
- }
- }
- }
- }
-
- public Object lookup(String s) {
- Expression e = (Expression)environment.get(s);
- if (e == null) {
- simulateException("Unbound variable: " + s);
- }
- return getValue(e);
- }
-
- public void register(String id, Object value) {
- Expression e = new MutableExpression();
- e.setValue(value);
- environment.put(id, e);
- }
-
- public void endElement(String name) throws SAXException {
- name = name.intern(); // Xerces parser does not supply unique tag names.
- if (name == "string") {
- this.isString = false;
- } else if (this.isString) {
- return;
- }
- if (name == "java") {
- return;
- }
- if (isPrimitive(name) || name == "string" || name == "class") {
- addArg(chars.toString());
- }
- if (name == "object" || name == "array" || name == "void" ||
- isPrimitive(name) || name == "string" || name == "class" ||
- name == "null") {
- Expression e = (Expression)pop(expStack);
- Object value = getValue(e);
- if (name != "void") {
- addArg(value);
- }
- }
- else {
- simulateException("Unrecognized closing tag: " + name);
- }
- }
-}
-
-
-class MutableExpression extends Expression {
- private Object target;
- private String methodName;
-
- private Object property;
- private Vector argV = new Vector();
-
- private String capitalize(String propertyName) {
- if (propertyName.length() == 0) {
- return propertyName;
- }
- return propertyName.substring(0, 1).toUpperCase(ENGLISH) + propertyName.substring(1);
- }
-
- public MutableExpression() {
- super(null, null, null);
- }
-
- public Object[] getArguments() {
- return argV.toArray();
- }
-
- public String getMethodName() {
- if (property == null) {
- return methodName;
- }
- int setterArgs = (property instanceof String) ? 1 : 2;
- String methodName = (argV.size() == setterArgs) ? "set" : "get";
- if (property instanceof String) {
- return methodName + capitalize((String)property);
- }
- else {
- return methodName;
- }
- }
-
- public void addArg(Object arg) {
- argV.add(arg);
- }
-
- public void setTarget(Object target) {
- this.target = target;
- }
-
- public Object getTarget() {
- return target;
- }
-
- public void setMethodName(String methodName) {
- this.methodName = methodName;
- }
-
- public void setProperty(Object property) {
- this.property = property;
- }
-
- public void setValue(Object value) {
- super.setValue(value);
- }
-
- public Object getValue() throws Exception {
- return super.getValue();
- }
-}
diff --git a/src/share/classes/com/sun/beans/decoder/AccessorElementHandler.java b/src/share/classes/com/sun/beans/decoder/AccessorElementHandler.java
new file mode 100644
index 000000000..d34fce456
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/AccessorElementHandler.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This is base class that simplifies access to entities (fields or properties).
+ * The {@code name} attribute specifies the name of the accessible entity.
+ * The element defines getter if it contains no argument
+ * or setter if it contains one argument.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+abstract class AccessorElementHandler extends ElementHandler {
+ private String name;
+ private ValueObject value;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the name of the accessible entity
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("name")) { // NON-NLS: the attribute name
+ this.name = value;
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Adds the argument that is used to set the value of this element.
+ *
+ * @param argument the value of the element that contained in this one
+ */
+ @Override
+ protected final void addArgument(Object argument) {
+ if (this.value != null) {
+ throw new IllegalStateException("Could not add argument to evaluated element");
+ }
+ setValue(this.name, argument);
+ this.value = ValueObjectImpl.VOID;
+ }
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ @Override
+ protected final ValueObject getValueObject() {
+ if (this.value == null) {
+ this.value = ValueObjectImpl.create(getValue(this.name));
+ }
+ return this.value;
+ }
+
+ /**
+ * Returns the value of the entity with specified {@code name}.
+ *
+ * @param name the name of the accessible entity
+ * @return the value of the specified entity
+ */
+ protected abstract Object getValue(String name);
+
+ /**
+ * Sets the new value for the entity with specified {@code name}.
+ *
+ * @param name the name of the accessible entity
+ * @param value the new value for the specified entity
+ */
+ protected abstract void setValue(String name, Object value);
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ArrayElementHandler.java b/src/share/classes/com/sun/beans/decoder/ArrayElementHandler.java
new file mode 100644
index 000000000..0bfcec6e4
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ArrayElementHandler.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import java.lang.reflect.Array;
+
+/**
+ * This class is intended to handle &lt;array&gt; element,
+ * that is used to array creation.
+ * The {@code length} attribute specifies the length of the array.
+ * The {@code class} attribute specifies the elements type.
+ * The {@link Object} type is used by default.
+ * For example:<pre>
+ * &lt;array length="10"/&gt;</pre>
+ * is equivalent to {@code new Component[10]} in Java code.
+ * The {@code set} and {@code get} methods,
+ * as defined in the {@link java.util.List} interface,
+ * can be used as if they could be applied to array instances.
+ * The {@code index} attribute can thus be used with arrays.
+ * For example:<pre>
+ * &lt;array length="3" class="java.lang.String"&gt;
+ * &lt;void index="1"&gt;
+ * &lt;string&gt;Hello, world&lt;/string&gt;
+ * &lt;/void&gt;
+ * &lt;/array&gt;</pre>
+ * is equivalent to the following Java code:<pre>
+ * String[] s = new String[3];
+ * s[1] = "Hello, world";</pre>
+ * It is possible to omit the {@code length} attribute and
+ * specify the values directly, without using {@code void} tags.
+ * The length of the array is equal to the number of values specified.
+ * For example:<pre>
+ * &lt;array id="array" class="int"&gt;
+ * &lt;int&gt;123&lt;/int&gt;
+ * &lt;int&gt;456&lt;/int&gt;
+ * &lt;/array&gt;</pre>
+ * is equivalent to {@code int[] array = {123, 456}} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>length
+ * <dd>the array length
+ * <dt>class
+ * <dd>the type of object for instantiation
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class ArrayElementHandler extends NewElementHandler {
+ private Integer length;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>length
+ * <dd>the array length
+ * <dt>class
+ * <dd>the type of object for instantiation
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("length")) { // NON-NLS: the attribute name
+ this.length = Integer.valueOf(value);
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Calculates the value of this element
+ * if the lentgh attribute is set.
+ */
+ @Override
+ public void startElement() {
+ if (this.length != null) {
+ getValueObject();
+ }
+ }
+
+ /**
+ * Creates an instance of the array.
+ *
+ * @param type the base class
+ * @param args the array of arguments
+ * @return the value of this element
+ */
+ @Override
+ protected ValueObject getValueObject(Class<?> type, Object[] args) {
+ if (type == null) {
+ type = Object.class;
+ }
+ if (this.length != null) {
+ return ValueObjectImpl.create(Array.newInstance(type, this.length));
+ }
+ Object array = Array.newInstance(type, args.length);
+ for (int i = 0; i < args.length; i++) {
+ Array.set(array, i, args[i]);
+ }
+ return ValueObjectImpl.create(array);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/BooleanElementHandler.java b/src/share/classes/com/sun/beans/decoder/BooleanElementHandler.java
new file mode 100644
index 000000000..a5f401b5f
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/BooleanElementHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;boolean&gt; element.
+ * This element specifies {@code boolean} values.
+ * The class {@link Boolean} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;boolean&gt;true&lt;/boolean&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="valueOf" class="java.lang.Boolean"&gt;
+ * &lt;string&gt;true&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Boolean.valueOf("true")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class BooleanElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code boolean} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code boolean} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ if (Boolean.TRUE.toString().equalsIgnoreCase(argument)) {
+ return Boolean.TRUE;
+ }
+ if (Boolean.FALSE.toString().equalsIgnoreCase(argument)) {
+ return Boolean.FALSE;
+ }
+ throw new IllegalArgumentException("Unsupported boolean argument: " + argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ByteElementHandler.java b/src/share/classes/com/sun/beans/decoder/ByteElementHandler.java
new file mode 100644
index 000000000..d7d458f01
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ByteElementHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;byte&gt; element.
+ * This element specifies {@code byte} values.
+ * The class {@link Byte} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;byte&gt;127&lt;/byte&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="decode" class="java.lang.Byte"&gt;
+ * &lt;string&gt;127&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Byte.decode("127")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class ByteElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code byte} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code byte} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return Byte.decode(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/CharElementHandler.java b/src/share/classes/com/sun/beans/decoder/CharElementHandler.java
new file mode 100644
index 000000000..910b5a63f
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/CharElementHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;char&gt; element.
+ * This element specifies {@code char} values.
+ * The class {@link Character} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;char&gt;X&lt;/char&gt;</pre>
+ * which is equivalent to {@code Character.valueOf('X')} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>code
+ * <dd>this attribute specifies character code
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ * The {@code code} attribute can be used for characters
+ * that are illegal in XML document, for example:<pre>
+ * &lt;char code="0"/&gt;</pre>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class CharElementHandler extends StringElementHandler {
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>code
+ * <dd>this attribute specifies character code
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("code")) { // NON-NLS: the attribute name
+ int code = Integer.decode(value);
+ for (char ch : Character.toChars(code)) {
+ addCharacter(ch);
+ }
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Creates {@code char} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code char} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ if (argument.length() != 1) {
+ throw new IllegalArgumentException("Wrong characters count");
+ }
+ return Character.valueOf(argument.charAt(0));
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ClassElementHandler.java b/src/share/classes/com/sun/beans/decoder/ClassElementHandler.java
new file mode 100644
index 000000000..c6ca98564
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ClassElementHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;class&gt; element.
+ * This element specifies {@link Class} values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;class&gt;java.lang.Class&lt;/class&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="forName" class="java.lang.Class"&gt;
+ * &lt;string&gt;java.lang.Class&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Class.forName("java.lang.Class")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class ClassElementHandler extends StringElementHandler {
+
+ /**
+ * Creates class by the name from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code Class} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return getOwner().findClass(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/DocumentHandler.java b/src/share/classes/com/sun/beans/decoder/DocumentHandler.java
new file mode 100644
index 000000000..4c409a12a
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/DocumentHandler.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import com.sun.beans.finder.ClassFinder;
+
+import java.beans.ExceptionListener;
+
+import java.io.IOException;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * The main class to parse JavaBeans XML archive.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ *
+ * @see ElementHandler
+ */
+public final class DocumentHandler extends DefaultHandler {
+ private final Map<String, Class<? extends ElementHandler>> handlers = new HashMap<String, Class<? extends ElementHandler>>();
+
+ private final Map<String, Object> environment = new HashMap<String, Object>();
+
+ private final List<Object> objects = new ArrayList<Object>();
+
+ private Reference<ClassLoader> loader;
+ private ExceptionListener listener;
+ private Object owner;
+
+ private ElementHandler handler;
+
+ /**
+ * Creates new instance of document handler.
+ */
+ public DocumentHandler() {
+ setElementHandler("java", JavaElementHandler.class); // NON-NLS: the element name
+ setElementHandler("null", NullElementHandler.class); // NON-NLS: the element name
+ setElementHandler("array", ArrayElementHandler.class); // NON-NLS: the element name
+ setElementHandler("class", ClassElementHandler.class); // NON-NLS: the element name
+ setElementHandler("string", StringElementHandler.class); // NON-NLS: the element name
+ setElementHandler("object", ObjectElementHandler.class); // NON-NLS: the element name
+
+ setElementHandler("void", VoidElementHandler.class); // NON-NLS: the element name
+ setElementHandler("char", CharElementHandler.class); // NON-NLS: the element name
+ setElementHandler("byte", ByteElementHandler.class); // NON-NLS: the element name
+ setElementHandler("short", ShortElementHandler.class); // NON-NLS: the element name
+ setElementHandler("int", IntElementHandler.class); // NON-NLS: the element name
+ setElementHandler("long", LongElementHandler.class); // NON-NLS: the element name
+ setElementHandler("float", FloatElementHandler.class); // NON-NLS: the element name
+ setElementHandler("double", DoubleElementHandler.class); // NON-NLS: the element name
+ setElementHandler("boolean", BooleanElementHandler.class); // NON-NLS: the element name
+
+ // some handlers for new elements
+ setElementHandler("new", NewElementHandler.class); // NON-NLS: the element name
+ setElementHandler("var", VarElementHandler.class); // NON-NLS: the element name
+ setElementHandler("true", TrueElementHandler.class); // NON-NLS: the element name
+ setElementHandler("false", FalseElementHandler.class); // NON-NLS: the element name
+ setElementHandler("field", FieldElementHandler.class); // NON-NLS: the element name
+ setElementHandler("method", MethodElementHandler.class); // NON-NLS: the element name
+ setElementHandler("property", PropertyElementHandler.class); // NON-NLS: the element name
+ }
+
+ /**
+ * Returns the class loader used to instantiate objects.
+ * If the class loader has not been explicitly set
+ * then {@code null} is returned.
+ *
+ * @return the class loader used to instantiate objects
+ */
+ public ClassLoader getClassLoader() {
+ return (this.loader != null)
+ ? this.loader.get()
+ : null;
+ }
+
+ /**
+ * Sets the class loader used to instantiate objects.
+ * If the class loader is not set
+ * then default class loader will be used.
+ *
+ * @param loader a classloader to use
+ */
+ public void setClassLoader(ClassLoader loader) {
+ this.loader = new WeakReference<ClassLoader>(loader);
+ }
+
+ /**
+ * Returns the exception listener for parsing.
+ * The exception listener is notified
+ * when handler catches recoverable exceptions.
+ * If the exception listener has not been explicitly set
+ * then default exception listener is returned.
+ *
+ * @return the exception listener for parsing
+ */
+ public ExceptionListener getExceptionListener() {
+ return this.listener;
+ }
+
+ /**
+ * Sets the exception listener for parsing.
+ * The exception listener is notified
+ * when handler catches recoverable exceptions.
+ *
+ * @param listener the exception listener for parsing
+ */
+ public void setExceptionListener(ExceptionListener listener) {
+ this.listener = listener;
+ }
+
+ /**
+ * Returns the owner of this document handler.
+ *
+ * @return the owner of this document handler
+ */
+ public Object getOwner() {
+ return this.owner;
+ }
+
+ /**
+ * Sets the owner of this document handler.
+ *
+ * @param owner the owner of this document handler
+ */
+ public void setOwner(Object owner) {
+ this.owner = owner;
+ }
+
+ /**
+ * Returns the handler for the element with specified name.
+ *
+ * @param name the name of the element
+ * @return the corresponding element handler
+ */
+ public Class<? extends ElementHandler> getElementHandler(String name) {
+ Class<? extends ElementHandler> type = this.handlers.get(name);
+ if (type == null) {
+ throw new IllegalArgumentException("Unsupported element: " + name);
+ }
+ return type;
+ }
+
+ /**
+ * Sets the handler for the element with specified name.
+ *
+ * @param name the name of the element
+ * @param handler the corresponding element handler
+ */
+ public void setElementHandler(String name, Class<? extends ElementHandler> handler) {
+ this.handlers.put(name, handler);
+ }
+
+ /**
+ * Indicates whether the variable with specified identifier is defined.
+ *
+ * @param id the identifier
+ * @return @{code true} if the variable is defined;
+ * @{code false} otherwise
+ */
+ public boolean hasVariable(String id) {
+ return this.environment.containsKey(id);
+ }
+
+ /**
+ * Returns the value of the variable with specified identifier.
+ *
+ * @param id the identifier
+ * @return the value of the variable
+ */
+ public Object getVariable(String id) {
+ if (!this.environment.containsKey(id)) {
+ throw new IllegalArgumentException("Unbound variable: " + id);
+ }
+ return this.environment.get(id);
+ }
+
+ /**
+ * Sets new value of the variable with specified identifier.
+ *
+ * @param id the identifier
+ * @param value new value of the variable
+ */
+ public void setVariable(String id, Object value) {
+ this.environment.put(id, value);
+ }
+
+ /**
+ * Returns the array of readed objects.
+ *
+ * @return the array of readed objects
+ */
+ public Object[] getObjects() {
+ return this.objects.toArray();
+ }
+
+ /**
+ * Adds the object to the list of readed objects.
+ *
+ * @param object the object that is readed from XML document
+ */
+ void addObject(Object object) {
+ this.objects.add(object);
+ }
+
+ /**
+ * Prepares this handler to read objects from XML document.
+ */
+ @Override
+ public void startDocument() {
+ this.objects.clear();
+ this.handler = null;
+ }
+
+ /**
+ * Parses opening tag of XML element
+ * using corresponding element handler.
+ *
+ * @param uri the namespace URI, or the empty string
+ * if the element has no namespace URI or
+ * if namespace processing is not being performed
+ * @param localName the local name (without prefix), or the empty string
+ * if namespace processing is not being performed
+ * @param qName the qualified name (with prefix), or the empty string
+ * if qualified names are not available
+ * @param attributes the attributes attached to the element
+ */
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ ElementHandler parent = this.handler;
+ try {
+ this.handler = getElementHandler(qName).newInstance();
+ this.handler.setOwner(this);
+ this.handler.setParent(parent);
+ }
+ catch (Exception exception) {
+ throw new SAXException(exception);
+ }
+ for (int i = 0; i < attributes.getLength(); i++)
+ try {
+ String name = attributes.getQName(i);
+ String value = attributes.getValue(i);
+ this.handler.addAttribute(name, value);
+ }
+ catch (RuntimeException exception) {
+ handleException(exception);
+ }
+
+ this.handler.startElement();
+ }
+
+ /**
+ * Parses closing tag of XML element
+ * using corresponding element handler.
+ *
+ * @param uri the namespace URI, or the empty string
+ * if the element has no namespace URI or
+ * if namespace processing is not being performed
+ * @param localName the local name (without prefix), or the empty string
+ * if namespace processing is not being performed
+ * @param qName the qualified name (with prefix), or the empty string
+ * if qualified names are not available
+ */
+ @Override
+ public void endElement(String uri, String localName, String qName) {
+ try {
+ this.handler.endElement();
+ }
+ catch (RuntimeException exception) {
+ handleException(exception);
+ }
+ finally {
+ this.handler = this.handler.getParent();
+ }
+ }
+
+ /**
+ * Parses character data inside XML element.
+ *
+ * @param chars the array of characters
+ * @param start the start position in the character array
+ * @param length the number of characters to use
+ */
+ @Override
+ public void characters(char[] chars, int start, int length) {
+ if (this.handler != null) {
+ try {
+ while (0 < length--) {
+ this.handler.addCharacter(chars[start++]);
+ }
+ }
+ catch (RuntimeException exception) {
+ handleException(exception);
+ }
+ }
+ }
+
+ /**
+ * Handles an exception using current exception listener.
+ *
+ * @param exception an exception to handle
+ * @see #setExceptionListener
+ */
+ public void handleException(Exception exception) {
+ if (this.listener == null) {
+ throw new IllegalStateException(exception);
+ }
+ this.listener.exceptionThrown(exception);
+ }
+
+ /**
+ * Starts parsing of the specified input source.
+ *
+ * @param input the input source to parse
+ */
+ public void parse(InputSource input) {
+ try {
+ SAXParserFactory.newInstance().newSAXParser().parse(input, this);
+ }
+ catch (ParserConfigurationException exception) {
+ handleException(exception);
+ }
+ catch (SAXException wrapper) {
+ Exception exception = wrapper.getException();
+ if (exception == null) {
+ exception = wrapper;
+ }
+ handleException(exception);
+ }
+ catch (IOException exception) {
+ handleException(exception);
+ }
+ }
+
+ /**
+ * Resolves class by name using current class loader.
+ * This method handles exception using current exception listener.
+ *
+ * @param name the name of the class
+ * @return the object that represents the class
+ */
+ public Class<?> findClass(String name) {
+ try {
+ return ClassFinder.resolveClass(name, getClassLoader());
+ }
+ catch (ClassNotFoundException exception) {
+ handleException(exception);
+ return null;
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/DoubleElementHandler.java b/src/share/classes/com/sun/beans/decoder/DoubleElementHandler.java
new file mode 100644
index 000000000..e0f586776
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/DoubleElementHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;double&gt; element.
+ * This element specifies {@code double} values.
+ * The class {@link Double} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;double&gt;1.23e45&lt;/double&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="valueOf" class="java.lang.Double"&gt;
+ * &lt;string&gt;1.23e45&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Double.valueOf("1.23e45")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class DoubleElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code double} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code double} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return Double.valueOf(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ElementHandler.java b/src/share/classes/com/sun/beans/decoder/ElementHandler.java
new file mode 100644
index 000000000..ca85cd6ed
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ElementHandler.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * The base class for element handlers.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ *
+ * @see DocumentHandler
+ */
+public abstract class ElementHandler {
+ private DocumentHandler owner;
+ private ElementHandler parent;
+
+ private String id;
+
+ /**
+ * Returns the document handler that creates this element handler.
+ *
+ * @return the owner document handler
+ */
+ public final DocumentHandler getOwner() {
+ return this.owner;
+ }
+
+ /**
+ * Sets the document handler that creates this element handler.
+ * The owner document handler should be set after instantiation.
+ * Such approach is used to simplify the extensibility.
+ *
+ * @param owner the owner document handler
+ * @see DocumentHandler#startElement
+ */
+ final void setOwner(DocumentHandler owner) {
+ if (owner == null) {
+ throw new IllegalArgumentException("Every element should have owner");
+ }
+ this.owner = owner;
+ }
+
+ /**
+ * Returns the element handler that contains this one.
+ *
+ * @return the parent element handler
+ */
+ public final ElementHandler getParent() {
+ return this.parent;
+ }
+
+ /**
+ * Sets the element handler that contains this one.
+ * The parent element handler should be set after instantiation.
+ * Such approach is used to simplify the extensibility.
+ *
+ * @param parent the parent element handler
+ * @see DocumentHandler#startElement
+ */
+ final void setParent(ElementHandler parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the value of the variable with specified identifier.
+ *
+ * @param id the identifier
+ * @return the value of the variable
+ */
+ protected final Object getVariable(String id) {
+ if (id.equals(this.id)) {
+ ValueObject value = getValueObject();
+ if (value.isVoid()) {
+ throw new IllegalStateException("The element does not return value");
+ }
+ return value.getValue();
+ }
+ return (this.parent != null)
+ ? this.parent.getVariable(id)
+ : this.owner.getVariable(id);
+ }
+
+ /**
+ * Returns the value of the parent element.
+ *
+ * @return the value of the parent element
+ */
+ protected Object getContextBean() {
+ if (this.parent != null) {
+ ValueObject value = this.parent.getValueObject();
+ if (!value.isVoid()) {
+ return value.getValue();
+ }
+ throw new IllegalStateException("The outer element does not return value");
+ } else {
+ Object value = this.owner.getOwner();
+ if (value != null) {
+ return value;
+ }
+ throw new IllegalStateException("The topmost element does not have context");
+ }
+ }
+
+ /**
+ * Parses attributes of the element.
+ * By default, the following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ public void addAttribute(String name, String value) {
+ if (name.equals("id")) { // NON-NLS: the attribute name
+ this.id = value;
+ } else {
+ throw new IllegalArgumentException("Unsupported attribute: " + name);
+ }
+ }
+
+ /**
+ * This method is called before parsing of the element's body.
+ * All attributes are parsed at this point.
+ * By default, do nothing.
+ */
+ public void startElement() {
+ }
+
+ /**
+ * This method is called after parsing of the element's body.
+ * By default, it calculates the value of this element.
+ * The following tasks are executing for any non-void value:
+ * <ol>
+ * <li>If the {@code id} attribute is set
+ * the value of the variable with the specified identifier
+ * is set to the value of this element.</li>
+ * <li>This element is used as an argument of parent element if it is possible.</li>
+ * </ol>
+ *
+ * @see #isArgument
+ */
+ public void endElement() {
+ // do nothing if no value returned
+ ValueObject value = getValueObject();
+ if (!value.isVoid()) {
+ if (this.id != null) {
+ this.owner.setVariable(this.id, value.getValue());
+ }
+ if (isArgument()) {
+ if (this.parent != null) {
+ this.parent.addArgument(value.getValue());
+ } else {
+ this.owner.addObject(value.getValue());
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the character that contained in this element.
+ * By default, only whitespaces are acceptable.
+ *
+ * @param ch the character
+ */
+ public void addCharacter(char ch) {
+ if ((ch != ' ') && (ch != '\n') && (ch != '\t') && (ch != '\r')) {
+ throw new IllegalStateException("Illegal character with code " + (int) ch);
+ }
+ }
+
+ /**
+ * Adds the argument that is used to calculate the value of this element.
+ * By default, no arguments are acceptable.
+ *
+ * @param argument the value of the element that contained in this one
+ */
+ protected void addArgument(Object argument) {
+ throw new IllegalStateException("Could not add argument to simple element");
+ }
+
+ /**
+ * Tests whether the value of this element can be used
+ * as an argument of the element that contained in this one.
+ *
+ * @return {@code true} if the value of this element can be used
+ * as an argument of the element that contained in this one,
+ * {@code false} otherwise
+ */
+ protected boolean isArgument() {
+ return this.id == null;
+ }
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ protected abstract ValueObject getValueObject();
+}
diff --git a/src/share/classes/com/sun/beans/decoder/FalseElementHandler.java b/src/share/classes/com/sun/beans/decoder/FalseElementHandler.java
new file mode 100644
index 000000000..6e8d786aa
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/FalseElementHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;false&gt; element.
+ * This element specifies {@code false} value.
+ * It should not contain body or inner elements.
+ * For example:<pre>
+ * &lt;false/&gt;</pre>
+ * is equivalent to {@code false} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class FalseElementHandler extends NullElementHandler {
+
+ /**
+ * Returns {@code Boolean.FALSE}
+ * as a value of &lt;false&gt; element.
+ *
+ * @return {@code Boolean.FALSE} by default
+ */
+ @Override
+ public Object getValue() {
+ return Boolean.FALSE;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/FieldElementHandler.java b/src/share/classes/com/sun/beans/decoder/FieldElementHandler.java
new file mode 100644
index 000000000..ac255dde7
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/FieldElementHandler.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import com.sun.beans.finder.FieldFinder;
+
+import java.lang.reflect.Field;
+
+/**
+ * This class is intended to handle &lt;field&gt; element.
+ * This element simplifies access to the fields.
+ * If the {@code class} attribute is specified
+ * this element accesses static field of specified class.
+ * This element defines getter if it contains no argument.
+ * It returns the value of the field in this case.
+ * For example:<pre>
+ * &lt;field name="TYPE" class="java.lang.Long"/&gt;</pre>
+ * is equivalent to {@code Long.TYPE} in Java code.
+ * This element defines setter if it contains one argument.
+ * It does not return the value of the field in this case.
+ * For example:<pre>
+ * &lt;field name="id"&gt;&lt;int&gt;0&lt;/int&gt;&lt;/field&gt;</pre>
+ * is equivalent to {@code id = 0} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the field name
+ * <dt>class
+ * <dd>the type is used for static fields only
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class FieldElementHandler extends AccessorElementHandler {
+ private Class<?> type;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the field name
+ * <dt>class
+ * <dd>the type is used for static fields only
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("class")) { // NON-NLS: the attribute name
+ this.type = getOwner().findClass(value);
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Tests whether the value of this element can be used
+ * as an argument of the element that contained in this one.
+ *
+ * @return {@code true} if the value of this element should be used
+ * as an argument of the element that contained in this one,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isArgument() {
+ return super.isArgument() && (this.type != null); // only static accessor can be used an argument
+ }
+
+ /**
+ * Returns the context of the field.
+ * The context of the static field is the class object.
+ * The context of the non-static field is the value of the parent element.
+ *
+ * @return the context of the field
+ */
+ @Override
+ protected Object getContextBean() {
+ return (this.type != null)
+ ? this.type
+ : super.getContextBean();
+ }
+
+ /**
+ * Returns the value of the field with specified {@code name}.
+ *
+ * @param name the name of the field
+ * @return the value of the specified field
+ */
+ @Override
+ protected Object getValue(String name) {
+ try {
+ return getFieldValue(getContextBean(), name);
+ }
+ catch (Exception exception) {
+ getOwner().handleException(exception);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the new value for the field with specified {@code name}.
+ *
+ * @param name the name of the field
+ * @param value the new value for the specified field
+ */
+ @Override
+ protected void setValue(String name, Object value) {
+ try {
+ setFieldValue(getContextBean(), name, value);
+ }
+ catch (Exception exception) {
+ getOwner().handleException(exception);
+ }
+ }
+
+ /**
+ * Performs the search of the field with specified {@code name}
+ * in specified context and returns its value.
+ *
+ * @param bean the context bean that contains field
+ * @param name the name of the field
+ * @return the value of the field
+ * @throws IllegalAccessException if the field is not accesible
+ * @throws NoSuchFieldException if the field is not found
+ */
+ static Object getFieldValue(Object bean, String name) throws IllegalAccessException, NoSuchFieldException {
+ return findField(bean, name).get(bean);
+ }
+
+ /**
+ * Performs the search of the field with specified {@code name}
+ * in specified context and updates its value.
+ *
+ * @param bean the context bean that contains field
+ * @param name the name of the field
+ * @param value the new value for the field
+ * @throws IllegalAccessException if the field is not accesible
+ * @throws NoSuchFieldException if the field is not found
+ */
+ private static void setFieldValue(Object bean, String name, Object value) throws IllegalAccessException, NoSuchFieldException {
+ findField(bean, name).set(bean, value);
+ }
+
+ /**
+ * Performs the search of the field
+ * with specified {@code name} in specified context.
+ *
+ * @param bean the context bean that contains field
+ * @param name the name of the field
+ * @return field object that represents found field
+ * @throws NoSuchFieldException if the field is not found
+ */
+ private static Field findField(Object bean, String name) throws NoSuchFieldException {
+ return (bean instanceof Class<?>)
+ ? FieldFinder.findStaticField((Class<?>) bean, name)
+ : FieldFinder.findField(bean.getClass(), name);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/FloatElementHandler.java b/src/share/classes/com/sun/beans/decoder/FloatElementHandler.java
new file mode 100644
index 000000000..08311b49f
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/FloatElementHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;float&gt; element.
+ * This element specifies {@code float} values.
+ * The class {@link Float} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;float&gt;-1.23&lt;/float&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="valueOf" class="java.lang.Float"&gt;
+ * &lt;string&gt;-1.23&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Float.valueOf("-1.23")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class FloatElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code float} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code float} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return Float.valueOf(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/IntElementHandler.java b/src/share/classes/com/sun/beans/decoder/IntElementHandler.java
new file mode 100644
index 000000000..ec5063fcd
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/IntElementHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;int&gt; element.
+ * This element specifies {@code int} values.
+ * The class {@link Integer} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;int&gt;-1&lt;/int&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="decode" class="java.lang.Integer"&gt;
+ * &lt;string&gt;-1&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Integer.decode("-1")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class IntElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code int} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code int} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return Integer.decode(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/JavaElementHandler.java b/src/share/classes/com/sun/beans/decoder/JavaElementHandler.java
new file mode 100644
index 000000000..2d416a139
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/JavaElementHandler.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import java.beans.XMLDecoder;
+
+/**
+ * This class is intended to handle &lt;java&gt; element.
+ * Each element that appears in the body of this element
+ * is evaluated in the context of the decoder itself.
+ * Typically this outer context is used to retrieve the owner of the decoder,
+ * which can be set before reading the archive.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>version
+ * <dd>the Java version (not supported)
+ * <dt>class
+ * <dd>the type of preferable parser (not supported)
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @see DocumentHandler#getOwner
+ * @see DocumentHandler#setOwner
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class JavaElementHandler extends ElementHandler {
+ private Class<?> type;
+ private ValueObject value;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>version
+ * <dd>the Java version (not supported)
+ * <dt>class
+ * <dd>the type of preferable parser (not supported)
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("version")) { // NON-NLS: the attribute name
+ // unsupported attribute
+ } else if (name.equals("class")) { // NON-NLS: the attribute name
+ // check class for owner
+ this.type = getOwner().findClass(value);
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Adds the argument to the list of readed objects.
+ *
+ * @param argument the value of the element that contained in this one
+ */
+ @Override
+ protected void addArgument(Object argument) {
+ getOwner().addObject(argument);
+ }
+
+ /**
+ * Tests whether the value of this element can be used
+ * as an argument of the element that contained in this one.
+ *
+ * @return {@code true} if the value of this element should be used
+ * as an argument of the element that contained in this one,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isArgument() {
+ return false; // do not use owner as object
+ }
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ @Override
+ protected ValueObject getValueObject() {
+ if (this.value == null) {
+ this.value = ValueObjectImpl.create(getValue());
+ }
+ return this.value;
+ }
+
+ /**
+ * Returns the owner of the owner document handler
+ * as a value of &lt;java&gt; element.
+ *
+ * @return the owner of the owner document handler
+ */
+ private Object getValue() {
+ Object owner = getOwner().getOwner();
+ if ((this.type == null) || isValid(owner)) {
+ return owner;
+ }
+ if (owner instanceof XMLDecoder) {
+ XMLDecoder decoder = (XMLDecoder) owner;
+ owner = decoder.getOwner();
+ if (isValid(owner)) {
+ return owner;
+ }
+ }
+ throw new IllegalStateException("Unexpected owner class: " + owner.getClass().getName());
+ }
+
+ /**
+ * Validates the owner of the &lt;java&gt; element.
+ * The owner is valid if it is {@code null} or an instance
+ * of the class specified by the {@code class} attribute.
+ *
+ * @param owner the owner of the &lt;java&gt; element
+ * @return {@code true} if the {@code owner} is valid;
+ * {@code false} otherwise
+ */
+ private boolean isValid(Object owner) {
+ return (owner == null) || this.type.isInstance(owner);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/LongElementHandler.java b/src/share/classes/com/sun/beans/decoder/LongElementHandler.java
new file mode 100644
index 000000000..a26f2f57f
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/LongElementHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;long&gt; element.
+ * This element specifies {@code long} values.
+ * The class {@link Long} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;long&gt;0xFFFF&lt;/long&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="decode" class="java.lang.Long"&gt;
+ * &lt;string&gt;0xFFFF&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Long.decode("0xFFFF")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class LongElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code long} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code long} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return Long.decode(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/MethodElementHandler.java b/src/share/classes/com/sun/beans/decoder/MethodElementHandler.java
new file mode 100644
index 000000000..34d030c90
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/MethodElementHandler.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import com.sun.beans.finder.MethodFinder;
+
+import java.lang.reflect.Method;
+
+/**
+ * This class is intended to handle &lt;method&gt; element.
+ * It describes invocation of the method.
+ * The {@code name} attribute denotes
+ * the name of the method to invoke.
+ * If the {@code class} attribute is specified
+ * this element invokes static method of specified class.
+ * The inner elements specifies the arguments of the method.
+ * For example:<pre>
+ * &lt;method name="valueOf" class="java.lang.Long"&gt;
+ * &lt;string&gt;10&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * is equivalent to {@code Long.valueOf("10")} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the method name
+ * <dt>class
+ * <dd>the type of object for instantiation
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class MethodElementHandler extends NewElementHandler {
+ private String name;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the method name
+ * <dt>class
+ * <dd>the type of object for instantiation
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("name")) { // NON-NLS: the attribute name
+ this.name = value;
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Returns the result of method execution.
+ *
+ * @param type the base class
+ * @param args the array of arguments
+ * @return the value of this element
+ * @throws Exception if calculation is failed
+ */
+ @Override
+ protected ValueObject getValueObject(Class<?> type, Object[] args) throws Exception {
+ Object bean = getContextBean();
+ Class<?>[] types = getArgumentTypes(args);
+ Method method = (type != null)
+ ? MethodFinder.findStaticMethod(type, this.name, types)
+ : MethodFinder.findMethod(bean.getClass(), this.name, types);
+
+ if (method.isVarArgs()) {
+ args = getArguments(args, method.getParameterTypes());
+ }
+ Object value = method.invoke(bean, args);
+ return method.getReturnType().equals(void.class)
+ ? ValueObjectImpl.VOID
+ : ValueObjectImpl.create(value);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/NewElementHandler.java b/src/share/classes/com/sun/beans/decoder/NewElementHandler.java
new file mode 100644
index 000000000..fc7debfc5
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/NewElementHandler.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import com.sun.beans.finder.ConstructorFinder;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is intended to handle &lt;new&gt; element.
+ * It describes instantiation of the object.
+ * The {@code class} attribute denotes
+ * the name of the class to instantiate.
+ * The inner elements specifies the arguments of the constructor.
+ * For example:<pre>
+ * &lt;new class="java.lang.Long"&gt;
+ * &lt;string&gt;10&lt;/string&gt;
+ * &lt;/new&gt;</pre>
+ * is equivalent to {@code new Long("10")} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>class
+ * <dd>the type of object for instantiation
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+class NewElementHandler extends ElementHandler {
+ private List<Object> arguments = new ArrayList<Object>();
+ private ValueObject value = ValueObjectImpl.VOID;
+
+ private Class<?> type;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>class
+ * <dd>the type of object for instantiation
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("class")) { // NON-NLS: the attribute name
+ this.type = getOwner().findClass(value);
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Adds the argument to the list of arguments
+ * that is used to calculate the value of this element.
+ *
+ * @param argument the value of the element that contained in this one
+ */
+ @Override
+ protected final void addArgument(Object argument) {
+ if (this.arguments == null) {
+ throw new IllegalStateException("Could not add argument to evaluated element");
+ }
+ this.arguments.add(argument);
+ }
+
+ /**
+ * Returns the context of the method.
+ * The context of the static method is the class object.
+ * The context of the non-static method is the value of the parent element.
+ *
+ * @return the context of the method
+ */
+ @Override
+ protected final Object getContextBean() {
+ return (this.type != null)
+ ? this.type
+ : super.getContextBean();
+ }
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ @Override
+ protected final ValueObject getValueObject() {
+ if (this.arguments != null) {
+ try {
+ this.value = getValueObject(this.type, this.arguments.toArray());
+ }
+ catch (Exception exception) {
+ getOwner().handleException(exception);
+ }
+ finally {
+ this.arguments = null;
+ }
+ }
+ return this.value;
+ }
+
+ /**
+ * Calculates the value of this element
+ * using the base class and the array of arguments.
+ * By default, it creates an instance of the base class.
+ * This method should be overridden in those handlers
+ * that extend behavior of this element.
+ *
+ * @param type the base class
+ * @param args the array of arguments
+ * @return the value of this element
+ * @throws Exception if calculation is failed
+ */
+ ValueObject getValueObject(Class<?> type, Object[] args) throws Exception {
+ if (type == null) {
+ throw new IllegalArgumentException("Class name is not set");
+ }
+ Class<?>[] types = getArgumentTypes(args);
+ Constructor<?> constructor = ConstructorFinder.findConstructor(type, types);
+ if (constructor.isVarArgs()) {
+ args = getArguments(args, constructor.getParameterTypes());
+ }
+ return ValueObjectImpl.create(constructor.newInstance(args));
+ }
+
+ /**
+ * Converts the array of arguments to the array of corresponding classes.
+ * If argument is {@code null} the class is {@code null} too.
+ *
+ * @param arguments the array of arguments
+ * @return the array of corresponding classes
+ */
+ static Class<?>[] getArgumentTypes(Object[] arguments) {
+ Class<?>[] types = new Class<?>[arguments.length];
+ for (int i = 0; i < arguments.length; i++) {
+ if (arguments[i] != null) {
+ types[i] = arguments[i].getClass();
+ }
+ }
+ return types;
+ }
+
+ /**
+ * Resolves variable arguments.
+ *
+ * @param arguments the array of arguments
+ * @param types the array of parameter types
+ * @return the resolved array of arguments
+ */
+ static Object[] getArguments(Object[] arguments, Class<?>[] types) {
+ int index = types.length - 1;
+ if (types.length == arguments.length) {
+ Object argument = arguments[index];
+ if (argument == null) {
+ return arguments;
+ }
+ Class<?> type = types[index];
+ if (type.isAssignableFrom(argument.getClass())) {
+ return arguments;
+ }
+ }
+ int length = arguments.length - index;
+ Class<?> type = types[index].getComponentType();
+ Object array = Array.newInstance(type, length);
+ System.arraycopy(arguments, index, array, 0, length);
+
+ Object[] args = new Object[types.length];
+ System.arraycopy(arguments, 0, args, 0, index);
+ args[index] = array;
+ return args;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/NullElementHandler.java b/src/share/classes/com/sun/beans/decoder/NullElementHandler.java
new file mode 100644
index 000000000..a3e2d699c
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/NullElementHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;null&gt; element.
+ * This element specifies {@code null} value.
+ * It should not contain body or inner elements.
+ * For example:<pre>
+ * &lt;null/&gt;</pre>
+ * is equivalent to {@code null} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+class NullElementHandler extends ElementHandler implements ValueObject {
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ @Override
+ protected final ValueObject getValueObject() {
+ return this;
+ }
+
+ /**
+ * Returns {@code null}
+ * as a value of &lt;null&gt; element.
+ * This method should be overridden in those handlers
+ * that extend behavior of this element.
+ *
+ * @return {@code null} by default
+ */
+ public Object getValue() {
+ return null;
+ }
+
+ /**
+ * Returns {@code void} state of this value object.
+ *
+ * @return {@code false} always
+ */
+ public final boolean isVoid() {
+ return false;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ObjectElementHandler.java b/src/share/classes/com/sun/beans/decoder/ObjectElementHandler.java
new file mode 100644
index 000000000..3ab5ddca9
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ObjectElementHandler.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import java.beans.Expression;
+
+import static java.util.Locale.ENGLISH;
+
+/**
+ * This class is intended to handle &lt;object&gt; element.
+ * This element looks like &lt;void&gt; element,
+ * but its value is always used as an argument for element
+ * that contains this one.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>class
+ * <dd>the type is used for static methods and fields
+ * <dt>method
+ * <dd>the method name
+ * <dt>property
+ * <dd>the property name
+ * <dt>index
+ * <dd>the property index
+ * <dt>field
+ * <dd>the field name
+ * <dt>idref
+ * <dd>the identifier to refer to the variable
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+class ObjectElementHandler extends NewElementHandler {
+ private String idref;
+ private String field;
+ private Integer index;
+ private String property;
+ private String method;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>class
+ * <dd>the type is used for static methods and fields
+ * <dt>method
+ * <dd>the method name
+ * <dt>property
+ * <dd>the property name
+ * <dt>index
+ * <dd>the property index
+ * <dt>field
+ * <dd>the field name
+ * <dt>idref
+ * <dd>the identifier to refer to the variable
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public final void addAttribute(String name, String value) {
+ if (name.equals("idref")) { // NON-NLS: the attribute name
+ this.idref = value;
+ } else if (name.equals("field")) { // NON-NLS: the attribute name
+ this.field = value;
+ } else if (name.equals("index")) { // NON-NLS: the attribute name
+ this.index = Integer.valueOf(value);
+ addArgument(this.index); // hack for compatibility
+ } else if (name.equals("property")) { // NON-NLS: the attribute name
+ this.property = value;
+ } else if (name.equals("method")) { // NON-NLS: the attribute name
+ this.method = value;
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Calculates the value of this element
+ * if the field attribute or the idref attribute is set.
+ */
+ @Override
+ public final void startElement() {
+ if ((this.field != null) || (this.idref != null)) {
+ getValueObject();
+ }
+ }
+
+ /**
+ * Tests whether the value of this element can be used
+ * as an argument of the element that contained in this one.
+ *
+ * @return {@code true} if the value of this element can be used
+ * as an argument of the element that contained in this one,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isArgument() {
+ return true; // hack for compatibility
+ }
+
+ /**
+ * Creates the value of this element.
+ *
+ * @param type the base class
+ * @param args the array of arguments
+ * @return the value of this element
+ * @throws Exception if calculation is failed
+ */
+ @Override
+ protected final ValueObject getValueObject(Class<?> type, Object[] args) throws Exception {
+ if (this.field != null) {
+ return ValueObjectImpl.create(FieldElementHandler.getFieldValue(getContextBean(), this.field));
+ }
+ if (this.idref != null) {
+ return ValueObjectImpl.create(getVariable(this.idref));
+ }
+ Object bean = getContextBean();
+ String name;
+ if (this.index != null) {
+ name = (args.length == 2)
+ ? PropertyElementHandler.SETTER
+ : PropertyElementHandler.GETTER;
+ } else if (this.property != null) {
+ name = (args.length == 1)
+ ? PropertyElementHandler.SETTER
+ : PropertyElementHandler.GETTER;
+
+ if (0 < this.property.length()) {
+ name += this.property.substring(0, 1).toUpperCase(ENGLISH) + this.property.substring(1);
+ }
+ } else {
+ name = (this.method != null) && (0 < this.method.length())
+ ? this.method
+ : "new"; // NON-NLS: the constructor marker
+ }
+ Expression expression = new Expression(bean, name, args);
+ return ValueObjectImpl.create(expression.getValue());
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/PropertyElementHandler.java b/src/share/classes/com/sun/beans/decoder/PropertyElementHandler.java
new file mode 100644
index 000000000..dcc550629
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/PropertyElementHandler.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+import com.sun.beans.finder.MethodFinder;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This class is intended to handle &lt;property&gt; element.
+ * This element simplifies access to the properties.
+ * If the {@code index} attribute is specified
+ * this element uses additional {@code int} parameter.
+ * If the {@code name} attribute is not specified
+ * this element uses method "get" as getter
+ * and method "set" as setter.
+ * This element defines getter if it contains no argument.
+ * It returns the value of the property in this case.
+ * For example:<pre>
+ * &lt;property name="object" index="10"/&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="getObject"&gt;
+ * &lt;int&gt;10&lt;/int&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code getObject(10)} in Java code.
+ * This element defines setter if it contains one argument.
+ * It does not return the value of the property in this case.
+ * For example:<pre>
+ * &lt;property&gt;&lt;int&gt;0&lt;/int&gt;&lt;/property&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="set"&gt;
+ * &lt;int&gt;0&lt;/int&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code set(0)} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the property name
+ * <dt>index
+ * <dd>the property index
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class PropertyElementHandler extends AccessorElementHandler {
+ static final String GETTER = "get"; // NON-NLS: the getter prefix
+ static final String SETTER = "set"; // NON-NLS: the setter prefix
+
+ private Integer index;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>name
+ * <dd>the property name
+ * <dt>index
+ * <dd>the property index
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("index")) { // NON-NLS: the attribute name
+ this.index = Integer.valueOf(value);
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Tests whether the value of this element can be used
+ * as an argument of the element that contained in this one.
+ *
+ * @return {@code true} if the value of this element should be used
+ * as an argument of the element that contained in this one,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isArgument() {
+ return false; // non-static accessor cannot be used an argument
+ }
+
+ /**
+ * Returns the value of the property with specified {@code name}.
+ *
+ * @param name the name of the property
+ * @return the value of the specified property
+ */
+ @Override
+ protected Object getValue(String name) {
+ try {
+ return getPropertyValue(getContextBean(), name, this.index);
+ }
+ catch (Exception exception) {
+ getOwner().handleException(exception);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the new value for the property with specified {@code name}.
+ *
+ * @param name the name of the property
+ * @param value the new value for the specified property
+ */
+ @Override
+ protected void setValue(String name, Object value) {
+ try {
+ setPropertyValue(getContextBean(), name, this.index, value);
+ }
+ catch (Exception exception) {
+ getOwner().handleException(exception);
+ }
+ }
+
+ /**
+ * Performs the search of the getter for the property
+ * with specified {@code name} in specified class
+ * and returns value of the property.
+ *
+ * @param bean the context bean that contains property
+ * @param name the name of the property
+ * @param index the index of the indexed property
+ * @return the value of the property
+ * @throws IllegalAccessException if the property is not accesible
+ * @throws IntrospectionException if the bean introspection is failed
+ * @throws InvocationTargetException if the getter cannot be invoked
+ * @throws NoSuchMethodException if the getter is not found
+ */
+ private static Object getPropertyValue(Object bean, String name, Integer index) throws IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException {
+ Class<?> type = bean.getClass();
+ if (index == null) {
+ return findGetter(type, name).invoke(bean);
+ } else if (type.isArray() && (name == null)) {
+ return Array.get(bean, index);
+ } else {
+ return findGetter(type, name, int.class).invoke(bean, index);
+ }
+ }
+
+ /**
+ * Performs the search of the setter for the property
+ * with specified {@code name} in specified class
+ * and updates value of the property.
+ *
+ * @param bean the context bean that contains property
+ * @param name the name of the property
+ * @param index the index of the indexed property
+ * @param value the new value for the property
+ * @throws IllegalAccessException if the property is not accesible
+ * @throws IntrospectionException if the bean introspection is failed
+ * @throws InvocationTargetException if the setter cannot be invoked
+ * @throws NoSuchMethodException if the setter is not found
+ */
+ private static void setPropertyValue(Object bean, String name, Integer index, Object value) throws IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException {
+ Class<?> type = bean.getClass();
+ Class<?> param = (value != null)
+ ? value.getClass()
+ : null;
+
+ if (index == null) {
+ findSetter(type, name, param).invoke(bean, value);
+ } else if (type.isArray() && (name == null)) {
+ Array.set(bean, index, value);
+ } else {
+ findSetter(type, name, int.class, param).invoke(bean, index, value);
+ }
+ }
+
+ /**
+ * Performs the search of the getter for the property
+ * with specified {@code name} in specified class.
+ *
+ * @param type the class that contains method
+ * @param name the name of the property
+ * @param args the method arguments
+ * @return method object that represents found getter
+ * @throws IntrospectionException if the bean introspection is failed
+ * @throws NoSuchMethodException if method is not found
+ */
+ private static Method findGetter(Class<?> type, String name, Class<?>...args) throws IntrospectionException, NoSuchMethodException {
+ if (name == null) {
+ return MethodFinder.findInstanceMethod(type, GETTER, args);
+ }
+ PropertyDescriptor pd = getProperty(type, name);
+ if (args.length == 0) {
+ Method method = pd.getReadMethod();
+ if (method != null) {
+ return method;
+ }
+ } else if (pd instanceof IndexedPropertyDescriptor) {
+ IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
+ Method method = ipd.getIndexedReadMethod();
+ if (method != null) {
+ return method;
+ }
+ }
+ throw new IntrospectionException("Could not find getter for the " + name + " property");
+ }
+
+ /**
+ * Performs the search of the setter for the property
+ * with specified {@code name} in specified class.
+ *
+ * @param type the class that contains method
+ * @param name the name of the property
+ * @param args the method arguments
+ * @return method object that represents found setter
+ * @throws IntrospectionException if the bean introspection is failed
+ * @throws NoSuchMethodException if method is not found
+ */
+ private static Method findSetter(Class<?> type, String name, Class<?>...args) throws IntrospectionException, NoSuchMethodException {
+ if (name == null) {
+ return MethodFinder.findInstanceMethod(type, SETTER, args);
+ }
+ PropertyDescriptor pd = getProperty(type, name);
+ if (args.length == 1) {
+ Method method = pd.getWriteMethod();
+ if (method != null) {
+ return method;
+ }
+ } else if (pd instanceof IndexedPropertyDescriptor) {
+ IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
+ Method method = ipd.getIndexedWriteMethod();
+ if (method != null) {
+ return method;
+ }
+ }
+ throw new IntrospectionException("Could not find setter for the " + name + " property");
+ }
+
+ /**
+ * Performs the search of the descriptor for the property
+ * with specified {@code name} in specified class.
+ *
+ * @param type the class to introspect
+ * @param name the property name
+ * @return descriptor for the named property
+ * @throws IntrospectionException if property descriptor is not found
+ */
+ private static PropertyDescriptor getProperty(Class<?> type, String name) throws IntrospectionException {
+ for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
+ if (name.equals(pd.getName())) {
+ return pd;
+ }
+ }
+ throw new IntrospectionException("Could not find the " + name + " property descriptor");
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ShortElementHandler.java b/src/share/classes/com/sun/beans/decoder/ShortElementHandler.java
new file mode 100644
index 000000000..5f91e2544
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ShortElementHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;short&gt; element.
+ * This element specifies {@code short} values.
+ * The class {@link Short} is used as wrapper for these values.
+ * The result value is created from text of the body of this element.
+ * The body parsing is described in the class {@link StringElementHandler}.
+ * For example:<pre>
+ * &lt;short&gt;200&lt;/short&gt;</pre>
+ * is shortcut to<pre>
+ * &lt;method name="decode" class="java.lang.Short"&gt;
+ * &lt;string&gt;200&lt;/string&gt;
+ * &lt;/method&gt;</pre>
+ * which is equivalent to {@code Short.decode("200")} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class ShortElementHandler extends StringElementHandler {
+
+ /**
+ * Creates {@code short} value from
+ * the text of the body of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated {@code short} value
+ */
+ @Override
+ public Object getValue(String argument) {
+ return Short.decode(argument);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/StringElementHandler.java b/src/share/classes/com/sun/beans/decoder/StringElementHandler.java
new file mode 100644
index 000000000..6075f436a
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/StringElementHandler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;string&gt; element.
+ * This element specifies {@link String} values.
+ * The result value is created from text of the body of this element.
+ * For example:<pre>
+ * &lt;string&gt;description&lt;/string&gt;</pre>
+ * is equivalent to {@code "description"} in Java code.
+ * The value of inner element is calculated
+ * before adding to the string using {@link String#valueOf(Object)}.
+ * Note that all characters are used including whitespaces (' ', '\t', '\n', '\r').
+ * So the value of the element<pre>
+ * &lt;string&gt&lt;true&gt&lt;/string&gt;</pre>
+ * is not equal to the value of the element<pre>
+ * &lt;string&gt;
+ * &lt;true&gt;
+ * &lt;/string&gt;</pre>
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+public class StringElementHandler extends ElementHandler {
+ private StringBuilder sb = new StringBuilder();
+ private ValueObject value = ValueObjectImpl.NULL;
+
+ /**
+ * Adds the character that contained in this element.
+ *
+ * @param ch the character
+ */
+ @Override
+ public final void addCharacter(char ch) {
+ if (this.sb == null) {
+ throw new IllegalStateException("Could not add chararcter to evaluated string element");
+ }
+ this.sb.append(ch);
+ }
+
+ /**
+ * Adds the string value of the argument to the string value of this element.
+ *
+ * @param argument the value of the element that contained in this one
+ */
+ @Override
+ protected final void addArgument(Object argument) {
+ if (this.sb == null) {
+ throw new IllegalStateException("Could not add argument to evaluated string element");
+ }
+ this.sb.append(argument);
+ }
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ @Override
+ protected final ValueObject getValueObject() {
+ if (this.sb != null) {
+ try {
+ this.value = ValueObjectImpl.create(getValue(this.sb.toString()));
+ }
+ catch (RuntimeException exception) {
+ getOwner().handleException(exception);
+ }
+ finally {
+ this.sb = null;
+ }
+ }
+ return this.value;
+ }
+
+ /**
+ * Returns the text of the body of this element.
+ * This method evaluates value from text of the body,
+ * and should be overridden in those handlers
+ * that extend behavior of this element.
+ *
+ * @param argument the text of the body
+ * @return evaluated value
+ */
+ protected Object getValue(String argument) {
+ return argument;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/TrueElementHandler.java b/src/share/classes/com/sun/beans/decoder/TrueElementHandler.java
new file mode 100644
index 000000000..faf8904d4
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/TrueElementHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;true&gt; element.
+ * This element specifies {@code true} value.
+ * It should not contain body or inner elements.
+ * For example:<pre>
+ * &lt;true/&gt;</pre>
+ * is equivalent to {@code true} in Java code.
+ * <p>The following atribute is supported:
+ * <dl>
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class TrueElementHandler extends NullElementHandler {
+
+ /**
+ * Returns {@code Boolean.TRUE}
+ * as a value of &lt;true&gt; element.
+ *
+ * @return {@code Boolean.TRUE} by default
+ */
+ @Override
+ public Object getValue() {
+ return Boolean.TRUE;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ValueObject.java b/src/share/classes/com/sun/beans/decoder/ValueObject.java
new file mode 100644
index 000000000..04f28278e
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ValueObject.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This interface represents the result of method execution.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+public interface ValueObject {
+
+ /**
+ * Returns the result of method execution.
+ *
+ * @return the result of method execution
+ */
+ Object getValue();
+
+ /**
+ * Returns {@code void} state of this value object.
+ *
+ * @return {@code true} if value can be ignored,
+ * {@code false} otherwise
+ */
+ boolean isVoid();
+}
diff --git a/src/share/classes/com/sun/beans/decoder/ValueObjectImpl.java b/src/share/classes/com/sun/beans/decoder/ValueObjectImpl.java
new file mode 100644
index 000000000..cb891fe6c
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/ValueObjectImpl.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This utility class provides {@code static} method
+ * to create the object that contains the result of method execution.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class ValueObjectImpl implements ValueObject {
+ static final ValueObject NULL = new ValueObjectImpl(null);
+ static final ValueObject VOID = new ValueObjectImpl();
+
+ /**
+ * Returns the object that describes returning value.
+ *
+ * @param value the result of method execution
+ * @return the object that describes value
+ */
+ static ValueObject create(Object value) {
+ return (value != null)
+ ? new ValueObjectImpl(value)
+ : NULL;
+ }
+
+ private Object value;
+ private boolean isVoid;
+
+ /**
+ * Creates the object that describes returning void value.
+ */
+ private ValueObjectImpl() {
+ this.isVoid = true;
+ }
+
+ /**
+ * Creates the object that describes returning non-void value.
+ *
+ * @param value the result of method execution
+ */
+ private ValueObjectImpl(Object value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the result of method execution.
+ *
+ * @return the result of method execution
+ */
+ public Object getValue() {
+ return this.value;
+ }
+
+ /**
+ * Returns {@code void} state of this value object.
+ *
+ * @return {@code true} if value should be ignored,
+ * {@code false} otherwise
+ */
+ public boolean isVoid() {
+ return this.isVoid;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/VarElementHandler.java b/src/share/classes/com/sun/beans/decoder/VarElementHandler.java
new file mode 100644
index 000000000..d948c18e5
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/VarElementHandler.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;var&gt; element.
+ * This element retrieves the value of specified variable.
+ * For example:<pre>
+ * &lt;var id="id1" idref="id2"/&gt;</pre>
+ * is equivalent to {@code id1 = id2} in Java code.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>idref
+ * <dd>the identifier to refer to the variable
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class VarElementHandler extends ElementHandler {
+ private ValueObject value;
+
+ /**
+ * Parses attributes of the element.
+ * The following atributes are supported:
+ * <dl>
+ * <dt>idref
+ * <dd>the identifier to refer to the variable
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ */
+ @Override
+ public void addAttribute(String name, String value) {
+ if (name.equals("idref")) { // NON-NLS: the attribute name
+ this.value = ValueObjectImpl.create(getVariable(value));
+ } else {
+ super.addAttribute(name, value);
+ }
+ }
+
+ /**
+ * Returns the value of this element.
+ *
+ * @return the value of this element
+ */
+ @Override
+ protected ValueObject getValueObject() {
+ if (this.value == null) {
+ throw new IllegalArgumentException("Variable name is not set");
+ }
+ return this.value;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/decoder/VoidElementHandler.java b/src/share/classes/com/sun/beans/decoder/VoidElementHandler.java
new file mode 100644
index 000000000..44726a0f7
--- /dev/null
+++ b/src/share/classes/com/sun/beans/decoder/VoidElementHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.decoder;
+
+/**
+ * This class is intended to handle &lt;void&gt; element.
+ * This element looks like &lt;object&gt; element,
+ * but its value is not used as an argument for element
+ * that contains this one.
+ * <p>The following atributes are supported:
+ * <dl>
+ * <dt>class
+ * <dd>the type is used for static methods and fields
+ * <dt>method
+ * <dd>the method name
+ * <dt>property
+ * <dd>the property name
+ * <dt>index
+ * <dd>the property index
+ * <dt>field
+ * <dd>the field name
+ * <dt>idref
+ * <dd>the identifier to refer to the variable
+ * <dt>id
+ * <dd>the identifier of the variable that is intended to store the result
+ * </dl>
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class VoidElementHandler extends ObjectElementHandler {
+
+ /**
+ * Tests whether the value of this element can be used
+ * as an argument of the element that contained in this one.
+ *
+ * @return {@code true} if the value of this element should be used
+ * as an argument of the element that contained in this one,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isArgument() {
+ return false; // hack for compatibility
+ }
+}
diff --git a/src/share/classes/com/sun/beans/finder/AbstractFinder.java b/src/share/classes/com/sun/beans/finder/AbstractFinder.java
new file mode 100644
index 000000000..396d6cbf8
--- /dev/null
+++ b/src/share/classes/com/sun/beans/finder/AbstractFinder.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.finder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This abstract class provides functionality
+ * to find a public method or constructor
+ * with specified parameter types.
+ * It supports a variable number of parameters.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+abstract class AbstractFinder<T> {
+ private final Class<?>[] args;
+
+ /**
+ * Creates finder for array of classes of arguments.
+ * If a particular element of array equals {@code null},
+ * than the appropriate pair of classes
+ * does not take into consideration.
+ *
+ * @param args array of classes of arguments
+ */
+ protected AbstractFinder(Class<?>[] args) {
+ this.args = args;
+ }
+
+ /**
+ * Returns an array of {@code Class} objects
+ * that represent the formal parameter types of the method
+ * Returns an empty array if the method takes no parameters.
+ *
+ * @param method the object that represents method
+ * @return the parameter types of the method
+ */
+ protected abstract Class<?>[] getParameters(T method);
+
+ /**
+ * Returns {@code true} if and only if the method
+ * was declared to take a variable number of arguments.
+ *
+ * @param method the object that represents method
+ * @return {@code true} if the method was declared
+ * to take a variable number of arguments;
+ * {@code false} otherwise
+ */
+ protected abstract boolean isVarArgs(T method);
+
+ /**
+ * Checks validness of the method.
+ * At least the valid method should be public.
+ *
+ * @param method the object that represents method
+ * @return {@code true} if the method is valid,
+ * {@code false} otherwise
+ */
+ protected abstract boolean isValid(T method);
+
+ /**
+ * Performs a search in the {@code methods} array.
+ * The one method is selected from the array of the valid methods.
+ * The list of parameters of the selected method shows
+ * the best correlation with the list of arguments
+ * specified at class initialization.
+ * If more than one method is both accessible and applicable
+ * to a method invocation, it is necessary to choose one
+ * to provide the descriptor for the run-time method dispatch.
+ * The most specific method should be chosen.
+ *
+ * @param methods the array of methods to search within
+ * @return the object that represents found method
+ * @throws NoSuchMethodException if no method was found or several
+ * methods meet the search criteria
+ * @see #isAssignable
+ */
+ final T find(T[] methods) throws NoSuchMethodException {
+ Map<T, Class<?>[]> map = new HashMap<T, Class<?>[]>();
+
+ T oldMethod = null;
+ Class<?>[] oldParams = null;
+ boolean ambiguous = false;
+
+ for (T newMethod : methods) {
+ if (isValid(newMethod)) {
+ Class<?>[] newParams = getParameters(newMethod);
+ if (newParams.length == this.args.length) {
+ PrimitiveWrapperMap.replacePrimitivesWithWrappers(newParams);
+ if (isAssignable(newParams, this.args)) {
+ if (oldMethod == null) {
+ oldMethod = newMethod;
+ oldParams = newParams;
+ } else {
+ boolean useNew = isAssignable(oldParams, newParams);
+ boolean useOld = isAssignable(newParams, oldParams);
+
+ if (useOld == useNew) {
+ ambiguous = true;
+ } else if (useNew) {
+ oldMethod = newMethod;
+ oldParams = newParams;
+ ambiguous = false;
+ }
+ }
+ }
+ }
+ if (isVarArgs(newMethod)) {
+ int length = newParams.length - 1;
+ if (length <= this.args.length) {
+ Class<?>[] array = new Class<?>[this.args.length];
+ System.arraycopy(newParams, 0, array, 0, length);
+ if (length < this.args.length) {
+ Class<?> type = newParams[length].getComponentType();
+ if (type.isPrimitive()) {
+ type = PrimitiveWrapperMap.getType(type.getName());
+ }
+ for (int i = length; i < this.args.length; i++) {
+ array[i] = type;
+ }
+ }
+ map.put(newMethod, array);
+ }
+ }
+ }
+ }
+ for (T newMethod : methods) {
+ Class<?>[] newParams = map.get(newMethod);
+ if (newParams != null) {
+ if (isAssignable(newParams, this.args)) {
+ if (oldMethod == null) {
+ oldMethod = newMethod;
+ oldParams = newParams;
+ } else {
+ boolean useNew = isAssignable(oldParams, newParams);
+ boolean useOld = isAssignable(newParams, oldParams);
+
+ if (useOld == useNew) {
+ if (oldParams == map.get(oldMethod)) {
+ ambiguous = true;
+ }
+ } else if (useNew) {
+ oldMethod = newMethod;
+ oldParams = newParams;
+ ambiguous = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (ambiguous) {
+ throw new NoSuchMethodException("Ambiguous methods are found");
+ }
+ if (oldMethod == null) {
+ throw new NoSuchMethodException("Method is not found");
+ }
+ return oldMethod;
+ }
+
+ /**
+ * Determines if every class in {@code min} array is either the same as,
+ * or is a superclass of, the corresponding class in {@code max} array.
+ * The length of every array must equal the number of arguments.
+ * This comparison is performed in the {@link #find} method
+ * before the first call of the isAssignable method.
+ * If an argument equals {@code null}
+ * the appropriate pair of classes does not take into consideration.
+ *
+ * @param min the array of classes to be checked
+ * @param max the array of classes that is used to check
+ * @return {@code true} if all classes in {@code min} array
+ * are assignable from corresponding classes in {@code max} array,
+ * {@code false} otherwise
+ *
+ * @see Class#isAssignableFrom
+ */
+ private boolean isAssignable(Class<?>[] min, Class<?>[] max) {
+ for (int i = 0; i < this.args.length; i++) {
+ if (null != this.args[i]) {
+ if (!min[i].isAssignableFrom(max[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/share/classes/com/sun/beans/finder/ClassFinder.java b/src/share/classes/com/sun/beans/finder/ClassFinder.java
index 79b774156..01a72fa58 100644
--- a/src/share/classes/com/sun/beans/finder/ClassFinder.java
+++ b/src/share/classes/com/sun/beans/finder/ClassFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2006-2008 Sun Microsystems, Inc. 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,7 +25,7 @@
package com.sun.beans.finder;
/**
- * This is utility class that provides <code>static</code> methods
+ * This is utility class that provides {@code static} methods
* to find a class with the specified name using the specified class loader.
*
* @since 1.7
@@ -33,137 +33,138 @@ package com.sun.beans.finder;
* @author Sergey A. Malenkov
*/
public final class ClassFinder {
+
/**
- * Returns the <code>Class</code> object associated
+ * Returns the {@code Class} object associated
* with the class or interface with the given string name,
* using the default class loader.
* <p>
- * The <code>name</code> can denote an array class
+ * The {@code name} can denote an array class
* (see {@link Class#getName} for details).
*
* @param name fully qualified name of the desired class
* @return class object representing the desired class
*
- * @exception ClassNotFoundException if the class cannot be located
- * by the specified class loader
+ * @throws ClassNotFoundException if the class cannot be located
+ * by the specified class loader
*
* @see Class#forName(String)
* @see Class#forName(String,boolean,ClassLoader)
* @see ClassLoader#getSystemClassLoader()
* @see Thread#getContextClassLoader()
*/
- public static Class findClass( String name ) throws ClassNotFoundException {
+ public static Class<?> findClass(String name) throws ClassNotFoundException {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
- if ( loader == null ) {
+ if (loader == null) {
// can be null in IE (see 6204697)
loader = ClassLoader.getSystemClassLoader();
}
- if ( loader != null ) {
- return Class.forName( name, false, loader );
+ if (loader != null) {
+ return Class.forName(name, false, loader);
}
- } catch ( ClassNotFoundException exception ) {
+ } catch (ClassNotFoundException exception) {
// use current class loader instead
- } catch ( SecurityException exception ) {
+ } catch (SecurityException exception) {
// use current class loader instead
}
- return Class.forName( name );
+ return Class.forName(name);
}
/**
- * Returns the <code>Class</code> object associated with
+ * Returns the {@code Class} object associated with
* the class or interface with the given string name,
* using the given class loader.
* <p>
- * The <code>name</code> can denote an array class
+ * The {@code name} can denote an array class
* (see {@link Class#getName} for details).
* <p>
- * If the parameter <code>loader</code> is null,
+ * If the parameter {@code loader} is null,
* the class is loaded through the default class loader.
*
* @param name fully qualified name of the desired class
* @param loader class loader from which the class must be loaded
* @return class object representing the desired class
*
- * @exception ClassNotFoundException if the class cannot be located
- * by the specified class loader
+ * @throws ClassNotFoundException if the class cannot be located
+ * by the specified class loader
*
* @see #findClass(String,ClassLoader)
* @see Class#forName(String,boolean,ClassLoader)
*/
- public static Class findClass( String name, ClassLoader loader ) throws ClassNotFoundException {
- if ( loader != null ) {
+ public static Class<?> findClass(String name, ClassLoader loader) throws ClassNotFoundException {
+ if (loader != null) {
try {
- return Class.forName( name, false, loader );
- } catch ( ClassNotFoundException exception ) {
+ return Class.forName(name, false, loader);
+ } catch (ClassNotFoundException exception) {
// use default class loader instead
- } catch ( SecurityException exception ) {
+ } catch (SecurityException exception) {
// use default class loader instead
}
}
- return findClass( name );
+ return findClass(name);
}
/**
- * Returns the <code>Class</code> object associated
+ * Returns the {@code Class} object associated
* with the class or interface with the given string name,
* using the default class loader.
* <p>
- * The <code>name</code> can denote an array class
+ * The {@code name} can denote an array class
* (see {@link Class#getName} for details).
* <p>
* This method can be used to obtain
- * any of the <code>Class</code> objects
- * representing <code>void</code> or primitive Java types:
- * <code>char</code>, <code>byte</code>, <code>short</code>,
- * <code>int</code>, <code>long</code>, <code>float</code>,
- * <code>double</code> and <code>boolean</code>.
+ * any of the {@code Class} objects
+ * representing {@code void} or primitive Java types:
+ * {@code char}, {@code byte}, {@code short},
+ * {@code int}, {@code long}, {@code float},
+ * {@code double} and {@code boolean}.
*
* @param name fully qualified name of the desired class
* @return class object representing the desired class
*
- * @exception ClassNotFoundException if the class cannot be located
- * by the specified class loader
+ * @throws ClassNotFoundException if the class cannot be located
+ * by the specified class loader
*
* @see #resolveClass(String,ClassLoader)
*/
- public static Class resolveClass( String name ) throws ClassNotFoundException {
- return resolveClass( name, null );
+ public static Class<?> resolveClass(String name) throws ClassNotFoundException {
+ return resolveClass(name, null);
}
/**
- * Returns the <code>Class</code> object associated with
+ * Returns the {@code Class} object associated with
* the class or interface with the given string name,
* using the given class loader.
* <p>
- * The <code>name</code> can denote an array class
+ * The {@code name} can denote an array class
* (see {@link Class#getName} for details).
* <p>
- * If the parameter <code>loader</code> is null,
+ * If the parameter {@code loader} is null,
* the class is loaded through the default class loader.
* <p>
* This method can be used to obtain
- * any of the <code>Class</code> objects
- * representing <code>void</code> or primitive Java types:
- * <code>char</code>, <code>byte</code>, <code>short</code>,
- * <code>int</code>, <code>long</code>, <code>float</code>,
- * <code>double</code> and <code>boolean</code>.
+ * any of the {@code Class} objects
+ * representing {@code void} or primitive Java types:
+ * {@code char}, {@code byte}, {@code short},
+ * {@code int}, {@code long}, {@code float},
+ * {@code double} and {@code boolean}.
*
* @param name fully qualified name of the desired class
* @param loader class loader from which the class must be loaded
* @return class object representing the desired class
*
- * @exception ClassNotFoundException if the class cannot be located
- * by the specified class loader
+ * @throws ClassNotFoundException if the class cannot be located
+ * by the specified class loader
*
* @see #findClass(String,ClassLoader)
* @see PrimitiveTypeMap#getType(String)
*/
- public static Class resolveClass( String name, ClassLoader loader ) throws ClassNotFoundException {
- Class type = PrimitiveTypeMap.getType( name );
- return ( type == null )
- ? findClass( name, loader )
+ public static Class<?> resolveClass(String name, ClassLoader loader) throws ClassNotFoundException {
+ Class<?> type = PrimitiveTypeMap.getType(name);
+ return (type == null)
+ ? findClass(name, loader)
: type;
}
diff --git a/src/share/classes/com/sun/beans/finder/ConstructorFinder.java b/src/share/classes/com/sun/beans/finder/ConstructorFinder.java
new file mode 100644
index 000000000..e7bb33421
--- /dev/null
+++ b/src/share/classes/com/sun/beans/finder/ConstructorFinder.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.finder;
+
+import com.sun.beans.WeakCache;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+
+/**
+ * This utility class provides {@code static} methods
+ * to find a public constructor with specified parameter types
+ * in specified class.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+public final class ConstructorFinder extends AbstractFinder<Constructor<?>> {
+ private static final WeakCache<Signature, Constructor<?>> CACHE = new WeakCache<Signature, Constructor<?>>();
+
+ /**
+ * Finds public constructor
+ * that is declared in public class.
+ *
+ * @param type the class that can have constructor
+ * @param args parameter types that is used to find constructor
+ * @return object that represents found constructor
+ * @throws NoSuchMethodException if constructor could not be found
+ * or some constructors are found
+ */
+ public static Constructor<?> findConstructor(Class<?> type, Class<?>...args) throws NoSuchMethodException {
+ if (type.isPrimitive()) {
+ throw new NoSuchMethodException("Primitive wrapper does not contain constructors");
+ }
+ if (type.isInterface()) {
+ throw new NoSuchMethodException("Interface does not contain constructors");
+ }
+ if (Modifier.isAbstract(type.getModifiers())) {
+ throw new NoSuchMethodException("Abstract class cannot be instantiated");
+ }
+ if (!Modifier.isPublic(type.getModifiers())) {
+ throw new NoSuchMethodException("Class is not accessible");
+ }
+ PrimitiveWrapperMap.replacePrimitivesWithWrappers(args);
+ Signature signature = new Signature(type, args);
+
+ Constructor<?> constructor = CACHE.get(signature);
+ if (constructor != null) {
+ return constructor;
+ }
+ constructor = new ConstructorFinder(args).find(type.getConstructors());
+ CACHE.put(signature, constructor);
+ return constructor;
+ }
+
+ /**
+ * Creates constructor finder with specified array of parameter types.
+ *
+ * @param args the array of parameter types
+ */
+ private ConstructorFinder(Class<?>[] args) {
+ super(args);
+ }
+
+ /**
+ * Returns an array of {@code Class} objects
+ * that represent the formal parameter types of the constructor
+ * Returns an empty array if the constructor takes no parameters.
+ *
+ * @param constructor the object that represents constructor
+ * @return the parameter types of the constructor
+ */
+ @Override
+ protected Class<?>[] getParameters(Constructor<?> constructor) {
+ return constructor.getParameterTypes();
+ }
+
+ /**
+ * Returns {@code true} if and only if the constructor
+ * was declared to take a variable number of arguments.
+ *
+ * @param constructor the object that represents constructor
+ * @return {@code true} if the constructor was declared
+ * to take a variable number of arguments;
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isVarArgs(Constructor<?> constructor) {
+ return constructor.isVarArgs();
+ }
+
+ /**
+ * Checks validness of the constructor.
+ * The valid constructor should be public.
+ *
+ * @param constructor the object that represents constructor
+ * @return {@code true} if the constructor is valid,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isValid(Constructor<?> constructor) {
+ return Modifier.isPublic(constructor.getModifiers());
+ }
+}
diff --git a/src/share/classes/com/sun/beans/finder/FieldFinder.java b/src/share/classes/com/sun/beans/finder/FieldFinder.java
new file mode 100644
index 000000000..cb17d0241
--- /dev/null
+++ b/src/share/classes/com/sun/beans/finder/FieldFinder.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.finder;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * This utility class provides {@code static} methods
+ * to find a public field with specified name
+ * in specified class.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+public final class FieldFinder {
+
+ /**
+ * Finds public field (static or non-static)
+ * that is declared in public class.
+ *
+ * @param type the class that can have field
+ * @param name the name of field to find
+ * @return object that represents found field
+ * @throws NoSuchFieldException if field is not found
+ * @see Class#getField
+ */
+ public static Field findField(Class<?> type, String name) throws NoSuchFieldException {
+ if (name == null) {
+ throw new IllegalArgumentException("Field name is not set");
+ }
+ Field field = type.getField(name);
+ if (!Modifier.isPublic(field.getModifiers())) {
+ throw new NoSuchFieldException("Field '" + name + "' is not public");
+ }
+ if (!Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
+ throw new NoSuchFieldException("Field '" + name + "' is not accessible");
+ }
+ return field;
+ }
+
+ /**
+ * Finds public non-static field
+ * that is declared in public class.
+ *
+ * @param type the class that can have field
+ * @param name the name of field to find
+ * @return object that represents found field
+ * @throws NoSuchFieldException if field is not found
+ * @see Class#getField
+ */
+ public static Field findInstanceField(Class<?> type, String name) throws NoSuchFieldException {
+ Field field = findField(type, name);
+ if (Modifier.isStatic(field.getModifiers())) {
+ throw new NoSuchFieldException("Field '" + name + "' is static");
+ }
+ return field;
+ }
+
+ /**
+ * Finds public static field
+ * that is declared in public class.
+ *
+ * @param type the class that can have field
+ * @param name the name of field to find
+ * @return object that represents found field
+ * @throws NoSuchFieldException if field is not found
+ * @see Class#getField
+ */
+ public static Field findStaticField(Class<?> type, String name) throws NoSuchFieldException {
+ Field field = findField(type, name);
+ if (!Modifier.isStatic(field.getModifiers())) {
+ throw new NoSuchFieldException("Field '" + name + "' is not static");
+ }
+ return field;
+ }
+
+ /**
+ * Disable instantiation.
+ */
+ private FieldFinder() {
+ }
+}
diff --git a/src/share/classes/com/sun/beans/finder/MethodFinder.java b/src/share/classes/com/sun/beans/finder/MethodFinder.java
new file mode 100644
index 000000000..9ccb4c439
--- /dev/null
+++ b/src/share/classes/com/sun/beans/finder/MethodFinder.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.finder;
+
+import com.sun.beans.TypeResolver;
+import com.sun.beans.WeakCache;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+/**
+ * This utility class provides {@code static} methods
+ * to find a public method with specified name and parameter types
+ * in specified class.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+public final class MethodFinder extends AbstractFinder<Method> {
+ private static final WeakCache<Signature, Method> CACHE = new WeakCache<Signature, Method>();
+
+ /**
+ * Finds public method (static or non-static)
+ * that is accessible from public class.
+ *
+ * @param type the class that can have method
+ * @param name the name of method to find
+ * @param args parameter types that is used to find method
+ * @return object that represents found method
+ * @throws NoSuchMethodException if method could not be found
+ * or some methods are found
+ */
+ public static Method findMethod(Class<?> type, String name, Class<?>...args) throws NoSuchMethodException {
+ if (name == null) {
+ throw new IllegalArgumentException("Method name is not set");
+ }
+ PrimitiveWrapperMap.replacePrimitivesWithWrappers(args);
+ Signature signature = new Signature(type, name, args);
+
+ Method method = CACHE.get(signature);
+ if (method != null) {
+ return method;
+ }
+ method = findAccessibleMethod(new MethodFinder(name, args).find(type.getMethods()));
+ CACHE.put(signature, method);
+ return method;
+ }
+
+ /**
+ * Finds public non-static method
+ * that is accessible from public class.
+ *
+ * @param type the class that can have method
+ * @param name the name of method to find
+ * @param args parameter types that is used to find method
+ * @return object that represents found method
+ * @throws NoSuchMethodException if method could not be found
+ * or some methods are found
+ */
+ public static Method findInstanceMethod(Class<?> type, String name, Class<?>... args) throws NoSuchMethodException {
+ Method method = findMethod(type, name, args);
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new NoSuchMethodException("Method '" + name + "' is static");
+ }
+ return method;
+ }
+
+ /**
+ * Finds public static method
+ * that is accessible from public class.
+ *
+ * @param type the class that can have method
+ * @param name the name of method to find
+ * @param args parameter types that is used to find method
+ * @return object that represents found method
+ * @throws NoSuchMethodException if method could not be found
+ * or some methods are found
+ */
+ public static Method findStaticMethod(Class<?> type, String name, Class<?>...args) throws NoSuchMethodException {
+ Method method = findMethod(type, name, args);
+ if (!Modifier.isStatic(method.getModifiers())) {
+ throw new NoSuchMethodException("Method '" + name + "' is not static");
+ }
+ return method;
+ }
+
+ /**
+ * Finds method that is accessible from public class or interface through class hierarchy.
+ *
+ * @param method object that represents found method
+ * @return object that represents accessible method
+ * @throws NoSuchMethodException if method is not accessible or is not found
+ * in specified superclass or interface
+ */
+ private static Method findAccessibleMethod(Method method) throws NoSuchMethodException {
+ Class<?> type = method.getDeclaringClass();
+ if (Modifier.isPublic(type.getModifiers())) {
+ return method;
+ }
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new NoSuchMethodException("Method '" + method.getName() + "' is not accessible");
+ }
+ for (Type generic : type.getGenericInterfaces()) {
+ try {
+ return findAccessibleMethod(method, generic);
+ }
+ catch (NoSuchMethodException exception) {
+ // try to find in superclass or another interface
+ }
+ }
+ return findAccessibleMethod(method, type.getGenericSuperclass());
+ }
+
+ /**
+ * Finds method that accessible from specified class.
+ *
+ * @param method object that represents found method
+ * @param generic generic type that is used to find accessible method
+ * @return object that represents accessible method
+ * @throws NoSuchMethodException if method is not accessible or is not found
+ * in specified superclass or interface
+ */
+ private static Method findAccessibleMethod(Method method, Type generic) throws NoSuchMethodException {
+ String name = method.getName();
+ Class<?>[] params = method.getParameterTypes();
+ if (generic instanceof Class) {
+ Class<?> type = (Class<?>) generic;
+ return findAccessibleMethod(type.getMethod(name, params));
+ }
+ if (generic instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) generic;
+ Class<?> type = (Class<?>) pt.getRawType();
+ for (Method m : type.getMethods()) {
+ if (m.getName().equals(name)) {
+ Class<?>[] pts = m.getParameterTypes();
+ if (pts.length == params.length) {
+ if (Arrays.equals(params, pts)) {
+ return findAccessibleMethod(m);
+ }
+ Type[] gpts = m.getGenericParameterTypes();
+ if (Arrays.equals(params, TypeResolver.erase(TypeResolver.resolve(pt, gpts)))) {
+ return findAccessibleMethod(m);
+ }
+ }
+ }
+ }
+ }
+ throw new NoSuchMethodException("Method '" + name + "' is not accessible");
+ }
+
+
+ private final String name;
+
+ /**
+ * Creates method finder with specified array of parameter types.
+ *
+ * @param name the name of method to find
+ * @param args the array of parameter types
+ */
+ private MethodFinder(String name, Class<?>[] args) {
+ super(args);
+ this.name = name;
+ }
+
+ /**
+ * Returns an array of {@code Class} objects
+ * that represent the formal parameter types of the method
+ * Returns an empty array if the method takes no parameters.
+ *
+ * @param method the object that represents method
+ * @return the parameter types of the method
+ */
+ @Override
+ protected Class<?>[] getParameters(Method method) {
+ return method.getParameterTypes();
+ }
+
+ /**
+ * Returns {@code true} if and only if the method
+ * was declared to take a variable number of arguments.
+ *
+ * @param method the object that represents method
+ * @return {@code true} if the method was declared
+ * to take a variable number of arguments;
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isVarArgs(Method method) {
+ return method.isVarArgs();
+ }
+
+ /**
+ * Checks validness of the method.
+ * The valid method should be public and
+ * should have the specified name.
+ *
+ * @param method the object that represents method
+ * @return {@code true} if the method is valid,
+ * {@code false} otherwise
+ */
+ @Override
+ protected boolean isValid(Method method) {
+ return Modifier.isPublic(method.getModifiers()) && method.getName().equals(this.name);
+ }
+}
diff --git a/src/share/classes/com/sun/beans/finder/PrimitiveTypeMap.java b/src/share/classes/com/sun/beans/finder/PrimitiveTypeMap.java
index 0bdb09e5b..36a552b90 100644
--- a/src/share/classes/com/sun/beans/finder/PrimitiveTypeMap.java
+++ b/src/share/classes/com/sun/beans/finder/PrimitiveTypeMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2006-2008 Sun Microsystems, Inc. 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
@@ -36,29 +36,30 @@ import java.util.Map;
* @author Sergey A. Malenkov
*/
final class PrimitiveTypeMap {
+
/**
* Returns primitive type class by its name.
*
* @param name the name of primitive type
* @return found primitive type class,
- * or <code>null</code> if not found
+ * or {@code null} if not found
*/
- static Class getType( String name ) {
- return map.get( name );
+ static Class<?> getType(String name) {
+ return map.get(name);
}
- private static final Map<String, Class> map = new HashMap<String, Class>( 9 );
+ private static final Map<String, Class<?>> map = new HashMap<String, Class<?>>(9);
static {
- map.put( boolean.class.getName(), boolean.class );
- map.put( char.class.getName(), char.class );
- map.put( byte.class.getName(), byte.class );
- map.put( short.class.getName(), short.class );
- map.put( int.class.getName(), int.class );
- map.put( long.class.getName(), long.class );
- map.put( float.class.getName(), float.class );
- map.put( double.class.getName(), double.class );
- map.put( void.class.getName(), void.class );
+ map.put(boolean.class.getName(), boolean.class);
+ map.put(char.class.getName(), char.class);
+ map.put(byte.class.getName(), byte.class);
+ map.put(short.class.getName(), short.class);
+ map.put(int.class.getName(), int.class);
+ map.put(long.class.getName(), long.class);
+ map.put(float.class.getName(), float.class);
+ map.put(double.class.getName(), double.class);
+ map.put(void.class.getName(), void.class);
}
/**
diff --git a/src/share/classes/com/sun/beans/finder/PrimitiveWrapperMap.java b/src/share/classes/com/sun/beans/finder/PrimitiveWrapperMap.java
new file mode 100644
index 000000000..f39b3ea3d
--- /dev/null
+++ b/src/share/classes/com/sun/beans/finder/PrimitiveWrapperMap.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.finder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This utility class associates
+ * name of primitive type with appropriate wrapper.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+public final class PrimitiveWrapperMap {
+
+ /**
+ * Replaces all primitive types in specified array with wrappers.
+ *
+ * @param types array of classes where all primitive types
+ * will be replaced by appropriate wrappers
+ */
+ static void replacePrimitivesWithWrappers(Class<?>[] types) {
+ for (int i = 0; i < types.length; i++) {
+ if (types[i] != null) {
+ if (types[i].isPrimitive()) {
+ types[i] = getType(types[i].getName());
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns wrapper for primitive type by its name.
+ *
+ * @param name the name of primitive type
+ * @return found wrapper for primitive type,
+ * or {@code null} if not found
+ */
+ public static Class<?> getType(String name) {
+ return map.get(name);
+ }
+
+ private static final Map<String, Class<?>> map = new HashMap<String, Class<?>>(9);
+
+ static {
+ map.put(Boolean.TYPE.getName(), Boolean.class);
+ map.put(Character.TYPE.getName(), Character.class);
+ map.put(Byte.TYPE.getName(), Byte.class);
+ map.put(Short.TYPE.getName(), Short.class);
+ map.put(Integer.TYPE.getName(), Integer.class);
+ map.put(Long.TYPE.getName(), Long.class);
+ map.put(Float.TYPE.getName(), Float.class);
+ map.put(Double.TYPE.getName(), Double.class);
+ map.put(Void.TYPE.getName(), Void.class);
+ }
+
+ /**
+ * Disable instantiation.
+ */
+ private PrimitiveWrapperMap() {
+ }
+}
diff --git a/src/share/classes/com/sun/beans/finder/Signature.java b/src/share/classes/com/sun/beans/finder/Signature.java
new file mode 100644
index 000000000..8c09e11f4
--- /dev/null
+++ b/src/share/classes/com/sun/beans/finder/Signature.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.beans.finder;
+
+/**
+ * This class is designed to be a key of a cache
+ * of constructors or methods.
+ *
+ * @since 1.7
+ *
+ * @author Sergey A. Malenkov
+ */
+final class Signature {
+ private final Class<?> type;
+ private final String name;
+ private final Class<?>[] args;
+
+ private volatile int code;
+
+ /**
+ * Constructs signature for constructor.
+ *
+ * @param type the class that contains constructor
+ * @param args the types of constructor's parameters
+ */
+ Signature(Class<?> type, Class<?>[] args) {
+ this(type, null, args);
+ }
+
+ /**
+ * Constructs signature for method.
+ *
+ * @param type the class that contains method
+ * @param name the name of the method
+ * @param args the types of method's parameters
+ */
+ Signature(Class<?> type, String name, Class<?>[] args) {
+ this.type = type;
+ this.name = name;
+ this.args = args;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param object the reference object with which to compare
+ * @return {@code true} if this object is the same as the
+ * {@code object} argument, {@code false} otherwise
+ * @see #hashCode()
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof Signature) {
+ Signature signature = (Signature) object;
+ return isEqual(signature.type, this.type)
+ && isEqual(signature.name, this.name)
+ && isEqual(signature.args, this.args);
+ }
+ return false;
+ }
+
+ /**
+ * Indicates whether some object is "equal to" another one.
+ * This method supports {@code null} values.
+ *
+ * @param obj1 the first reference object that will compared
+ * @param obj2 the second reference object that will compared
+ * @return {@code true} if first object is the same as the second object,
+ * {@code false} otherwise
+ */
+ private static boolean isEqual(Object obj1, Object obj2) {
+ return (obj1 == null)
+ ? obj2 == null
+ : obj1.equals(obj2);
+ }
+
+ /**
+ * Indicates whether some array is "equal to" another one.
+ * This method supports {@code null} values.
+ *
+ * @param args1 the first reference array that will compared
+ * @param args2 the second reference array that will compared
+ * @return {@code true} if first array is the same as the second array,
+ * {@code false} otherwise
+ */
+ private static boolean isEqual(Class<?>[] args1, Class<?>[] args2) {
+ if ((args1 == null) || (args2 == null)) {
+ return args1 == args2;
+ }
+ if (args1.length != args2.length) {
+ return false;
+ }
+ for (int i = 0; i < args1.length; i++) {
+ if (!isEqual(args1[i], args2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ * This method is supported for the benefit of hashtables
+ * such as {@link java.util.HashMap} or {@link java.util.HashSet}.
+ * Hash code computed using algorithm
+ * suggested in Effective Java, Item 8.
+ *
+ * @return a hash code value for this object
+ * @see #equals(Object)
+ */
+ @Override
+ public int hashCode() {
+ if (this.code == 0) {
+ int code = 17;
+ code = addHashCode(code, this.type);
+ code = addHashCode(code, this.name);
+
+ if (this.args != null) {
+ for (Class<?> arg : this.args) {
+ code = addHashCode(code, arg);
+ }
+ }
+ this.code = code;
+ }
+ return this.code;
+ }
+
+ /**
+ * Adds hash code value if specified object.
+ * This is a part of the algorithm
+ * suggested in Effective Java, Item 8.
+ *
+ * @param code current hash code value
+ * @param object object that updates hash code value
+ * @return updated hash code value
+ * @see #hashCode()
+ */
+ private static int addHashCode(int code, Object object) {
+ code *= 37;
+ return (object != null)
+ ? code + object.hashCode()
+ : code;
+ }
+}
diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKColorChooserPanel.java b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKColorChooserPanel.java
index c70d889f4..d54839f7e 100644
--- a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKColorChooserPanel.java
+++ b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKColorChooserPanel.java
@@ -799,9 +799,9 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
Graphics g = triangleImage.getGraphics();
g.setColor(new Color(0, 0, 0, 0));
g.fillRect(0, 0, a, a);
- g.translate((int)(a / 2), 0);
+ g.translate(a / 2, 0);
paintTriangle(g, triangleSize, getColor());
- g.translate((int)(-a / 2), 0);
+ g.translate(-a / 2, 0);
g.dispose();
g = wheelImage.getGraphics();
@@ -897,7 +897,7 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
return false;
}
// Rotate to origin and and verify x is valid.
- int triangleSize = (int)innerR * 3 / 2;
+ int triangleSize = innerR * 3 / 2;
double x1 = Math.cos(angle) * x - Math.sin(angle) * y;
double y1 = Math.sin(angle) * x + Math.cos(angle) * y;
if (x1 < -(innerR / 2)) {
@@ -960,7 +960,7 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
*/
private void setSaturationAndBrightness(float s, float b) {
int innerR = getTriangleCircumscribedRadius();
- int triangleSize = (int)innerR * 3 / 2;
+ int triangleSize = innerR * 3 / 2;
double x = b * triangleSize;
double maxY = x * Math.tan(Math.toRadians(30.0));
double y = 2 * maxY * s - maxY;
@@ -1156,7 +1156,7 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
* @param x X location to get color for
* @param y Y location to get color for
* @param rad Radius from center of color wheel
- * @param integer with red, green and blue components
+ * @return integer with red, green and blue components
*/
private int colorWheelLocationToRGB(int x, int y, double rad) {
double angle = Math.acos((double)x / rad);
@@ -1165,12 +1165,12 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
if (angle < PI_3) {
if (y < 0) {
// FFFF00 - FF0000
- rgb = 0xFF0000 | (int)Math.min(255,
+ rgb = 0xFF0000 | Math.min(255,
(int)(255 * angle / PI_3)) << 8;
}
else {
// FF0000 - FF00FF
- rgb = 0xFF0000 | (int)Math.min(255,
+ rgb = 0xFF0000 | Math.min(255,
(int)(255 * angle / PI_3));
}
}
@@ -1178,12 +1178,12 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
angle -= PI_3;
if (y < 0) {
// 00FF00 - FFFF00
- rgb = 0x00FF00 | (int)Math.max(0, 255 -
+ rgb = 0x00FF00 | Math.max(0, 255 -
(int)(255 * angle / PI_3)) << 16;
}
else {
// FF00FF - 0000FF
- rgb = 0x0000FF | (int)Math.max(0, 255 -
+ rgb = 0x0000FF | Math.max(0, 255 -
(int)(255 * angle / PI_3)) << 16;
}
}
@@ -1191,12 +1191,12 @@ class GTKColorChooserPanel extends AbstractColorChooserPanel implements
angle -= 2 * PI_3;
if (y < 0) {
// 00FFFF - 00FF00
- rgb = 0x00FF00 | (int)Math.min(255,
+ rgb = 0x00FF00 | Math.min(255,
(int)(255 * angle / PI_3));
}
else {
// 0000FF - 00FFFF
- rgb = 0x0000FF | (int)Math.min(255,
+ rgb = 0x0000FF | Math.min(255,
(int)(255 * angle / PI_3)) << 8;
}
}
diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java
index 95bf090b6..737e20c22 100644
--- a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java
+++ b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java
@@ -112,7 +112,7 @@ class GTKEngine {
}
- private static HashMap regionToWidgetTypeMap;
+ private static HashMap<Region, Object> regionToWidgetTypeMap;
private ImageCache cache = new ImageCache(CACHE_SIZE);
private int x0, y0, w0, h0;
private Graphics graphics;
@@ -178,7 +178,7 @@ class GTKEngine {
Toolkit.getDefaultToolkit();
// Initialize regionToWidgetTypeMap
- regionToWidgetTypeMap = new HashMap(50);
+ regionToWidgetTypeMap = new HashMap<Region, Object>(50);
regionToWidgetTypeMap.put(Region.ARROW_BUTTON, new WidgetType[] {
WidgetType.SPINNER_ARROW_BUTTON,
WidgetType.COMBO_BOX_ARROW_BUTTON,
diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java
index 8b786da73..acb944dfa 100644
--- a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java
+++ b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java
@@ -148,7 +148,7 @@ class GTKFileChooserUI extends SynthFileChooserUI {
directoryList : fileList;
Object[] files = list.getSelectedValues();
int len = files.length;
- Vector result = new Vector(len + 1);
+ Vector<String> result = new Vector<String>(len + 1);
// we return all selected file names
for (int i = 0; i < len; i++) {
@@ -263,13 +263,13 @@ class GTKFileChooserUI extends SynthFileChooserUI {
ListSelectionModel sm = directoryList.getSelectionModel();
if (sm instanceof DefaultListSelectionModel) {
((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
- ((DefaultListSelectionModel)sm).setAnchorSelectionIndex(0);
+ sm.setAnchorSelectionIndex(0);
}
fileList.clearSelection();
sm = fileList.getSelectionModel();
if (sm instanceof DefaultListSelectionModel) {
((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
- ((DefaultListSelectionModel)sm).setAnchorSelectionIndex(0);
+ sm.setAnchorSelectionIndex(0);
}
File currentDirectory = getFileChooser().getCurrentDirectory();
@@ -425,16 +425,16 @@ class GTKFileChooserUI extends SynthFileChooserUI {
setDirectorySelected(true);
setDirectory(((File)objects[0]));
} else {
- ArrayList fList = new ArrayList(objects.length);
- for (int i = 0; i < objects.length; i++) {
- File f = (File)objects[i];
+ ArrayList<File> fList = new ArrayList<File>(objects.length);
+ for (Object object : objects) {
+ File f = (File) object;
if ((chooser.isFileSelectionEnabled() && f.isFile())
|| (chooser.isDirectorySelectionEnabled() && f.isDirectory())) {
fList.add(f);
}
}
if (fList.size() > 0) {
- files = (File[])fList.toArray(new File[fList.size()]);
+ files = fList.toArray(new File[fList.size()]);
}
setDirectorySelected(false);
}
@@ -671,9 +671,9 @@ class GTKFileChooserUI extends SynthFileChooserUI {
pathFieldLabel.setLabelFor(fileNameTextField);
- Set forwardTraversalKeys = fileNameTextField.getFocusTraversalKeys(
+ Set<AWTKeyStroke> forwardTraversalKeys = fileNameTextField.getFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
- forwardTraversalKeys = new HashSet(forwardTraversalKeys);
+ forwardTraversalKeys = new HashSet<AWTKeyStroke>(forwardTraversalKeys);
forwardTraversalKeys.remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
fileNameTextField.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardTraversalKeys);
@@ -895,10 +895,9 @@ class GTKFileChooserUI extends SynthFileChooserUI {
private class GTKDirectoryModel extends BasicDirectoryModel {
FileSystemView fsv;
- private Comparator fileComparator = new Comparator() {
- public int compare(Object o, Object o1) {
- return fsv.getSystemDisplayName((File) o).compareTo
- (fsv.getSystemDisplayName((File) o1));
+ private Comparator<File> fileComparator = new Comparator<File>() {
+ public int compare(File o, File o1) {
+ return fsv.getSystemDisplayName(o).compareTo(fsv.getSystemDisplayName(o1));
}
};
@@ -1074,7 +1073,7 @@ class GTKFileChooserUI extends SynthFileChooserUI {
* Data model for a type-face selection combo-box.
*/
protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
- Vector directories = new Vector();
+ Vector<File> directories = new Vector<File>();
File selectedDirectory = null;
JFileChooser chooser = getFileChooser();
FileSystemView fsv = chooser.getFileSystemView();
@@ -1216,7 +1215,7 @@ class GTKFileChooserUI extends SynthFileChooserUI {
ListSelectionModel sm = fileList.getSelectionModel();
if (sm instanceof DefaultListSelectionModel) {
((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
- ((DefaultListSelectionModel)sm).setAnchorSelectionIndex(0);
+ sm.setAnchorSelectionIndex(0);
}
rescanCurrentDirectory(getFileChooser());
return;
@@ -1352,8 +1351,8 @@ class GTKFileChooserUI extends SynthFileChooserUI {
FileFilter currentFilter = getFileChooser().getFileFilter();
boolean found = false;
if (currentFilter != null) {
- for (int i = 0; i < filters.length; i++) {
- if (filters[i] == currentFilter) {
+ for (FileFilter filter : filters) {
+ if (filter == currentFilter) {
found = true;
}
}
diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java
index 6934aa213..23e4ee2ee 100644
--- a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java
+++ b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java
@@ -1470,7 +1470,7 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(gtkAAFontSettingsCond);
}
- static ReferenceQueue queue = new ReferenceQueue();
+ static ReferenceQueue<GTKLookAndFeel> queue = new ReferenceQueue<GTKLookAndFeel>();
private static void flushUnreferenced() {
WeakPCL pcl;
@@ -1480,12 +1480,12 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
}
}
- static class WeakPCL extends WeakReference implements
+ static class WeakPCL extends WeakReference<GTKLookAndFeel> implements
PropertyChangeListener {
private Toolkit kit;
private String key;
- WeakPCL(Object target, Toolkit kit, String key) {
+ WeakPCL(GTKLookAndFeel target, Toolkit kit, String key) {
super(target, queue);
this.kit = kit;
this.key = key;
@@ -1494,7 +1494,7 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
public String getKey() { return key; }
public void propertyChange(final PropertyChangeEvent pce) {
- final GTKLookAndFeel lnf = (GTKLookAndFeel)get();
+ final GTKLookAndFeel lnf = get();
if (lnf == null || UIManager.getLookAndFeel() != lnf) {
// The property was GC'ed, we're no longer interested in
diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java
index 780b01cf6..58d61dc90 100644
--- a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java
+++ b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java
@@ -299,7 +299,7 @@ class GTKPainter extends SynthPainter {
// Paint the default indicator
GTKStyle style = (GTKStyle)context.getStyle();
if (defaultCapable && !toolButton) {
- Insets defaultInsets = (Insets)style.getClassSpecificInsetsValue(
+ Insets defaultInsets = style.getClassSpecificInsetsValue(
context, "default-border",
GTKStyle.BUTTON_DEFAULT_BORDER_INSETS);
diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java b/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java
index a3eada90d..d432bb088 100644
--- a/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java
+++ b/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java
@@ -124,7 +124,7 @@ class Metacity implements SynthConstants {
}
// Initialize constants
- variables = new HashMap();
+ variables = new HashMap<String, Integer>();
NodeList nodes = xmlDoc.getElementsByTagName("constant");
int n = nodes.getLength();
for (int i = 0; i < n; i++) {
@@ -144,14 +144,14 @@ class Metacity implements SynthConstants {
}
// Cache frame geometries
- frameGeometries = new HashMap();
+ frameGeometries = new HashMap<String, Map<String, Object>>();
nodes = xmlDoc.getElementsByTagName("frame_geometry");
n = nodes.getLength();
for (int i = 0; i < n; i++) {
Node node = nodes.item(i);
String name = getStringAttr(node, "name");
if (name != null) {
- HashMap<String, Object> gm = new HashMap();
+ HashMap<String, Object> gm = new HashMap<String, Object>();
frameGeometries.put(name, gm);
String parentGM = getStringAttr(node, "parent");
@@ -458,7 +458,7 @@ class Metacity implements SynthConstants {
- private static class Privileged implements PrivilegedAction {
+ private static class Privileged implements PrivilegedAction<Object> {
private static int GET_THEME_DIR = 0;
private static int GET_USER_THEME = 1;
private static int GET_IMAGE = 2;
@@ -598,7 +598,7 @@ class Metacity implements SynthConstants {
g2.setComposite(oldComp);
}
- private HashMap<String, Image> images = new HashMap();
+ private HashMap<String, Image> images = new HashMap<String, Image>();
protected Image getImage(String key, Color c) {
Image image = images.get(key+"-"+c.getRGB());
@@ -1530,8 +1530,8 @@ class Metacity implements SynthConstants {
DocumentBuilderFactory.newInstance().newDocumentBuilder();
}
InputStream inputStream =
- (InputStream)AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
+ AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+ public InputStream run() {
try {
return new BufferedInputStream(xmlFile.openStream());
} catch (IOException ex) {
@@ -1551,7 +1551,7 @@ class Metacity implements SynthConstants {
protected Node[] getNodesByName(Node parent, String name) {
NodeList nodes = parent.getChildNodes(); // ElementNode
int n = nodes.getLength();
- ArrayList<Node> list = new ArrayList();
+ ArrayList<Node> list = new ArrayList<Node>();
for (int i=0; i < n; i++) {
Node node = nodes.item(i);
if (name.equals(node.getNodeName())) {
@@ -1603,7 +1603,7 @@ class Metacity implements SynthConstants {
String aValue = attrs[a * 2 + 1];
Node attr = nodeAttrs.getNamedItem(aName);
if (attr == null ||
- aValue != null && !aValue.equals((String)attr.getNodeValue())) {
+ aValue != null && !aValue.equals(attr.getNodeValue())) {
matches = false;
break;
}
@@ -1642,7 +1642,7 @@ class Metacity implements SynthConstants {
protected String getStringAttr(NamedNodeMap attrs, String name) {
Node item = attrs.getNamedItem(name);
- return (item != null) ? (String)item.getNodeValue() : null;
+ return (item != null) ? item.getNodeValue() : null;
}
protected boolean getBooleanAttr(Node node, String name, boolean fallback) {
diff --git a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
index f9f32f2ff..57af97958 100644
--- a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
+++ b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
@@ -70,7 +70,6 @@ public class WindowsFileChooserUI extends BasicFileChooserUI {
private JTextField filenameTextField;
private FilePane filePane;
private WindowsPlacesBar placesBar;
- private boolean useShellFolder;
private JButton approveButton;
private JButton cancelButton;
@@ -210,10 +209,6 @@ public class WindowsFileChooserUI extends BasicFileChooserUI {
public ListSelectionListener createListSelectionListener() {
return WindowsFileChooserUI.this.createListSelectionListener(getFileChooser());
}
-
- public boolean usesShellFolder() {
- return useShellFolder;
- }
}
public void installComponents(JFileChooser fc) {
@@ -625,15 +620,8 @@ public class WindowsFileChooserUI extends BasicFileChooserUI {
// Decide whether to use the ShellFolder class to populate shortcut
// panel and combobox.
JFileChooser fc = getFileChooser();
- Boolean prop =
- (Boolean)fc.getClientProperty("FileChooser.useShellFolder");
- if (prop != null) {
- useShellFolder = prop.booleanValue();
- } else {
- useShellFolder = fc.getFileSystemView().equals(FileSystemView.getFileSystemView());
- }
if (OS_VERSION.compareTo(OSInfo.WINDOWS_ME) >= 0) {
- if (useShellFolder) {
+ if (FilePane.usesShellFolder(fc)) {
if (placesBar == null && !UIManager.getBoolean("FileChooser.noPlacesBar")) {
placesBar = new WindowsPlacesBar(fc, XPStyle.getXP() != null);
fc.add(placesBar, BorderLayout.BEFORE_LINE_BEGINS);
@@ -1149,6 +1137,8 @@ public class WindowsFileChooserUI extends BasicFileChooserUI {
return;
}
+ boolean useShellFolder = FilePane.usesShellFolder(chooser);
+
directories.clear();
File[] baseFolders;
diff --git a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java
index bb7842a00..788fe9fec 100644
--- a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java
+++ b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java
@@ -1554,10 +1554,10 @@ public class WindowsLookAndFeel extends BasicLookAndFeel
"Tree.selectionBackground", SelectionBackgroundColor,
"Tree.expandedIcon", treeExpandedIcon,
"Tree.collapsedIcon", treeCollapsedIcon,
- "Tree.openIcon", new ActiveWindowsIcon("win.icon.shellIconBPP", "shell32Icon 5",
- (Icon)table.get("Tree.openIcon")),
- "Tree.closedIcon", new ActiveWindowsIcon("win.icon.shellIconBPP", "shell32Icon 4",
- (Icon)table.get("Tree.closedIcon")),
+ "Tree.openIcon", new ActiveWindowsIcon("win.icon.shellIconBPP",
+ "shell32Icon 5", "icons/TreeOpen.gif"),
+ "Tree.closedIcon", new ActiveWindowsIcon("win.icon.shellIconBPP",
+ "shell32Icon 4", "icons/TreeClosed.gif"),
"Tree.focusInputMap",
new UIDefaults.LazyInputMap(new Object[] {
"ADD", "expand",
@@ -2205,21 +2205,21 @@ public class WindowsLookAndFeel extends BasicLookAndFeel
*/
private class ActiveWindowsIcon implements UIDefaults.ActiveValue {
private Icon icon;
- private Icon fallback;
private String nativeImageName;
+ private String fallbackName;
private DesktopProperty desktopProperty;
ActiveWindowsIcon(String desktopPropertyName,
- String nativeImageName, Icon fallback) {
+ String nativeImageName, String fallbackName) {
this.nativeImageName = nativeImageName;
- this.fallback = fallback;
+ this.fallbackName = fallbackName;
if (OSInfo.getOSType() == OSInfo.OSType.WINDOWS &&
OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_XP) < 0) {
// This desktop property is needed to trigger reloading the icon.
// It is kept in member variable to avoid GC.
this.desktopProperty = new TriggerDesktopProperty(desktopPropertyName) {
- protected void updateUI() {
+ @Override protected void updateUI() {
icon = null;
super.updateUI();
}
@@ -2227,6 +2227,7 @@ public class WindowsLookAndFeel extends BasicLookAndFeel
}
}
+ @Override
public Object createValue(UIDefaults table) {
if (icon == null) {
Image image = (Image)ShellFolder.get(nativeImageName);
@@ -2234,8 +2235,11 @@ public class WindowsLookAndFeel extends BasicLookAndFeel
icon = new ImageIconUIResource(image);
}
}
- if (icon == null && fallback != null) {
- icon = fallback;
+ if (icon == null && fallbackName != null) {
+ UIDefaults.LazyValue fallback = (UIDefaults.LazyValue)
+ SwingUtilities2.makeIcon(WindowsLookAndFeel.class,
+ BasicLookAndFeel.class, fallbackName);
+ icon = (Icon) fallback.createValue(table);
}
return icon;
}
diff --git a/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java b/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java
index 5409ab056..70cb15e0a 100644
--- a/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java
+++ b/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java
@@ -75,13 +75,6 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice
/**
* This is the device handle returned from native code
*/
- /*
- * $$rratta Solaris 64 bit holds pointer must be long
- *
- * $$mp 2003-08-07:
- * 'id' is a really bad name. The variable should
- * be called nativePointer or something similar.
- */
protected long id = 0;
@@ -586,7 +579,6 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice
private ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>();
private MidiOutDevice.MidiOutReceiver midiOutReceiver;
- private MixerSynth.SynthReceiver mixerSynthReceiver;
// how many transmitters must be present for optimized
// handling
@@ -621,22 +613,14 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice
if (midiOutReceiver == oldR) {
midiOutReceiver = null;
}
- if (mixerSynthReceiver == oldR) {
- mixerSynthReceiver = null;
- }
if (newR != null) {
if ((newR instanceof MidiOutDevice.MidiOutReceiver)
&& (midiOutReceiver == null)) {
midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);
}
- if ((newR instanceof MixerSynth.SynthReceiver)
- && (mixerSynthReceiver == null)) {
- mixerSynthReceiver = ((MixerSynth.SynthReceiver) newR);
- }
}
optimizedReceiverCount =
- ((midiOutReceiver!=null)?1:0)
- + ((mixerSynthReceiver!=null)?1:0);
+ ((midiOutReceiver!=null)?1:0);
}
// more potential for optimization here
}
@@ -670,10 +654,6 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver");
midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
}
- if (mixerSynthReceiver != null) {
- if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MixerSynthReceiver");
- mixerSynthReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
- }
} else {
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers");
for (int i = 0; i < size; i++) {
@@ -682,9 +662,6 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice
if (optimizedReceiverCount > 0) {
if (receiver instanceof MidiOutDevice.MidiOutReceiver) {
((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
- }
- else if (receiver instanceof MixerSynth.SynthReceiver) {
- ((MixerSynth.SynthReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
} else {
receiver.send(new FastShortMessage(packedMessage), timeStamp);
}
@@ -739,10 +716,6 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
midiOutReceiver.send(message, timeStamp);
}
- if (mixerSynthReceiver != null) {
- if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MixerSynthReceiver");
- mixerSynthReceiver.send(message, timeStamp);
- }
} else {
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
for (int i = 0; i < size; i++) {
diff --git a/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java b/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java
new file mode 100644
index 000000000..7f4090d8d
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.spi.SoundbankReader;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+/**
+ * Soundbank reader that uses audio files as soundbanks.
+ *
+ * @author Karl Helgason
+ */
+public class AudioFileSoundbankReader extends SoundbankReader {
+
+ public Soundbank getSoundbank(URL url)
+ throws InvalidMidiDataException, IOException {
+ try {
+ AudioInputStream ais = AudioSystem.getAudioInputStream(url);
+ Soundbank sbk = getSoundbank(ais);
+ ais.close();
+ return sbk;
+ } catch (UnsupportedAudioFileException e) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public Soundbank getSoundbank(InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ stream.mark(512);
+ try {
+ AudioInputStream ais = AudioSystem.getAudioInputStream(stream);
+ Soundbank sbk = getSoundbank(ais);
+ if (sbk != null)
+ return sbk;
+ } catch (UnsupportedAudioFileException e) {
+ } catch (IOException e) {
+ }
+ stream.reset();
+ return null;
+ }
+
+ public Soundbank getSoundbank(AudioInputStream ais)
+ throws InvalidMidiDataException, IOException {
+ try {
+ byte[] buffer;
+ if (ais.getFrameLength() == -1) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buff = new byte[1024
+ - (1024 % ais.getFormat().getFrameSize())];
+ int ret;
+ while ((ret = ais.read(buff)) != -1) {
+ baos.write(buff, 0, ret);
+ }
+ ais.close();
+ buffer = baos.toByteArray();
+ } else {
+ buffer = new byte[(int) (ais.getFrameLength()
+ * ais.getFormat().getFrameSize())];
+ new DataInputStream(ais).readFully(buffer);
+ }
+ ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
+ new ModelByteBuffer(buffer), ais.getFormat(), -4800);
+ ModelPerformer performer = new ModelPerformer();
+ performer.getOscillators().add(osc);
+
+ SimpleSoundbank sbk = new SimpleSoundbank();
+ SimpleInstrument ins = new SimpleInstrument();
+ ins.add(performer);
+ sbk.addInstrument(ins);
+ return sbk;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public Soundbank getSoundbank(File file)
+ throws InvalidMidiDataException, IOException {
+ try {
+ AudioInputStream ais = AudioSystem.getAudioInputStream(file);
+ ais.close();
+ ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
+ new ModelByteBuffer(file, 0, file.length()), -4800);
+ ModelPerformer performer = new ModelPerformer();
+ performer.getOscillators().add(osc);
+ SimpleSoundbank sbk = new SimpleSoundbank();
+ SimpleInstrument ins = new SimpleInstrument();
+ ins.add(performer);
+ sbk.addInstrument(ins);
+ return sbk;
+ } catch (UnsupportedAudioFileException e1) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/AudioFloatConverter.java b/src/share/classes/com/sun/media/sound/AudioFloatConverter.java
new file mode 100644
index 000000000..e8d9dbeb1
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/AudioFloatConverter.java
@@ -0,0 +1,1058 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioFormat.Encoding;
+
+/**
+ * This class is used to convert between 8,16,24,32,32+ bit signed/unsigned
+ * big/litle endian fixed/floating point byte buffers and float buffers.
+ *
+ * @author Karl Helgason
+ */
+public abstract class AudioFloatConverter {
+
+ public static final Encoding PCM_FLOAT = new Encoding("PCM_FLOAT");
+
+ /***************************************************************************
+ *
+ * LSB Filter, used filter least significant byte in samples arrays.
+ *
+ * Is used filter out data in lsb byte when SampleSizeInBits is not
+ * dividable by 8.
+ *
+ **************************************************************************/
+
+ private static class AudioFloatLSBFilter extends AudioFloatConverter {
+
+ private AudioFloatConverter converter;
+
+ final private int offset;
+
+ final private int stepsize;
+
+ final private byte mask;
+
+ private byte[] mask_buffer;
+
+ public AudioFloatLSBFilter(AudioFloatConverter converter,
+ AudioFormat format) {
+ int bits = format.getSampleSizeInBits();
+ boolean bigEndian = format.isBigEndian();
+ this.converter = converter;
+ stepsize = (bits + 7) / 8;
+ offset = bigEndian ? (stepsize - 1) : 0;
+ int lsb_bits = bits % 8;
+ if (lsb_bits == 0)
+ mask = (byte) 0x00;
+ else if (lsb_bits == 1)
+ mask = (byte) 0x80;
+ else if (lsb_bits == 2)
+ mask = (byte) 0xC0;
+ else if (lsb_bits == 3)
+ mask = (byte) 0xE0;
+ else if (lsb_bits == 4)
+ mask = (byte) 0xF0;
+ else if (lsb_bits == 5)
+ mask = (byte) 0xF8;
+ else if (lsb_bits == 6)
+ mask = (byte) 0xFC;
+ else if (lsb_bits == 7)
+ mask = (byte) 0xFE;
+ else
+ mask = (byte) 0xFF;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ byte[] ret = converter.toByteArray(in_buff, in_offset, in_len,
+ out_buff, out_offset);
+
+ int out_offset_end = in_len * stepsize;
+ for (int i = out_offset + offset; i < out_offset_end; i += stepsize) {
+ out_buff[i] = (byte) (out_buff[i] & mask);
+ }
+
+ return ret;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ if (mask_buffer == null || mask_buffer.length < in_buff.length)
+ mask_buffer = new byte[in_buff.length];
+ System.arraycopy(in_buff, 0, mask_buffer, 0, in_buff.length);
+ int in_offset_end = out_len * stepsize;
+ for (int i = in_offset + offset; i < in_offset_end; i += stepsize) {
+ mask_buffer[i] = (byte) (mask_buffer[i] & mask);
+ }
+ float[] ret = converter.toFloatArray(mask_buffer, in_offset,
+ out_buff, out_offset, out_len);
+ return ret;
+ }
+
+ }
+
+ /***************************************************************************
+ *
+ * 64 bit float, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 64 bit float, little-endian
+ private static class AudioFloatConversion64L extends AudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ DoubleBuffer floatbuffer = null;
+
+ double[] double_buff = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ if (double_buff == null
+ || double_buff.length < out_len + out_offset)
+ double_buff = new double[out_len + out_offset];
+ floatbuffer.get(double_buff, out_offset, out_len);
+ int out_offset_end = out_offset + out_len;
+ for (int i = out_offset; i < out_offset_end; i++) {
+ out_buff[i] = (float) double_buff[i];
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ if (double_buff == null || double_buff.length < in_offset + in_len)
+ double_buff = new double[in_offset + in_len];
+ int in_offset_end = in_offset + in_len;
+ for (int i = in_offset; i < in_offset_end; i++) {
+ double_buff[i] = in_buff[i];
+ }
+ floatbuffer.put(double_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ // PCM 64 bit float, big-endian
+ private static class AudioFloatConversion64B extends AudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ DoubleBuffer floatbuffer = null;
+
+ double[] double_buff = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ if (double_buff == null
+ || double_buff.length < out_len + out_offset)
+ double_buff = new double[out_len + out_offset];
+ floatbuffer.get(double_buff, out_offset, out_len);
+ int out_offset_end = out_offset + out_len;
+ for (int i = out_offset; i < out_offset_end; i++) {
+ out_buff[i] = (float) double_buff[i];
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ if (double_buff == null || double_buff.length < in_offset + in_len)
+ double_buff = new double[in_offset + in_len];
+ int in_offset_end = in_offset + in_len;
+ for (int i = in_offset; i < in_offset_end; i++) {
+ double_buff[i] = in_buff[i];
+ }
+ floatbuffer.put(double_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 32 bit float, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 32 bit float, little-endian
+ private static class AudioFloatConversion32L extends AudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ FloatBuffer floatbuffer = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ floatbuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ floatbuffer.put(in_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit float, big-endian
+ private static class AudioFloatConversion32B extends AudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ FloatBuffer floatbuffer = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ floatbuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ floatbuffer.put(in_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 8 bit signed/unsigned
+ *
+ **************************************************************************/
+
+ // PCM 8 bit, signed
+ private static class AudioFloatConversion8S extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++)
+ out_buff[ox++] = in_buff[ix++] * (1.0f / 127.0f);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++)
+ out_buff[ox++] = (byte) (in_buff[ix++] * 127.0f);
+ return out_buff;
+ }
+ }
+
+ // PCM 8 bit, unsigned
+ private static class AudioFloatConversion8U extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++)
+ out_buff[ox++] = ((in_buff[ix++] & 0xFF) - 127)
+ * (1.0f / 127.0f);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++)
+ out_buff[ox++] = (byte) (127 + in_buff[ix++] * 127.0f);
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 16 bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 16 bit, signed, little-endian
+ private static class AudioFloatConversion16SL extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int len = out_offset + out_len;
+ for (int ox = out_offset; ox < len; ox++) {
+ out_buff[ox] = ((short) ((in_buff[ix++] & 0xFF) |
+ (in_buff[ix++] << 8))) * (1.0f / 32767.0f);
+ }
+
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ox = out_offset;
+ int len = in_offset + in_len;
+ for (int ix = in_offset; ix < len; ix++) {
+ int x = (int) (in_buff[ix] * 32767.0);
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 16 bit, signed, big-endian
+ private static class AudioFloatConversion16SB extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ out_buff[ox++] = ((short) ((in_buff[ix++] << 8) |
+ (in_buff[ix++] & 0xFF))) * (1.0f / 32767.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * 32767.0);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 16 bit, unsigned, little-endian
+ private static class AudioFloatConversion16UL extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8);
+ out_buff[ox++] = (x - 32767) * (1.0f / 32767.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = 32767 + (int) (in_buff[ix++] * 32767.0);
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 16 bit, unsigned, big-endian
+ private static class AudioFloatConversion16UB extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ out_buff[ox++] = (x - 32767) * (1.0f / 32767.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = 32767 + (int) (in_buff[ix++] * 32767.0);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 24 bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 24 bit, signed, little-endian
+ private static class AudioFloatConversion24SL extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16);
+ if (x > 0x7FFFFF)
+ x -= 0x1000000;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ if (x < 0)
+ x += 0x1000000;
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 24 bit, signed, big-endian
+ private static class AudioFloatConversion24SB extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ if (x > 0x7FFFFF)
+ x -= 0x1000000;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ if (x < 0)
+ x += 0x1000000;
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 24 bit, unsigned, little-endian
+ private static class AudioFloatConversion24UL extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16);
+ x -= 0x7FFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ x += 0x7FFFFF;
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 24 bit, unsigned, big-endian
+ private static class AudioFloatConversion24UB extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ x -= 0x7FFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ x += 0x7FFFFF;
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 32 bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 32 bit, signed, little-endian
+ private static class AudioFloatConversion32SL extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 24);
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit, signed, big-endian
+ private static class AudioFloatConversion32SB extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit, unsigned, little-endian
+ private static class AudioFloatConversion32UL extends AudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 24);
+ x -= 0x7FFFFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ x += 0x7FFFFFFF;
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit, unsigned, big-endian
+ private static class AudioFloatConversion32UB extends AudioFloatConverter {
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ x -= 0x7FFFFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ x += 0x7FFFFFFF;
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 32+ bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 32+ bit, signed, little-endian
+ private static class AudioFloatConversion32xSL extends AudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xSL(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ ix += xbytes;
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 24);
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32+ bit, signed, big-endian
+ private static class AudioFloatConversion32xSB extends AudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xSB(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24)
+ | ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 8)
+ | (in_buff[ix++] & 0xFF);
+ ix += xbytes;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32+ bit, unsigned, little-endian
+ private static class AudioFloatConversion32xUL extends AudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xUL(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ ix += xbytes;
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 24);
+ x -= 0x7FFFFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ x += 0x7FFFFFFF;
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32+ bit, unsigned, big-endian
+ private static class AudioFloatConversion32xUB extends AudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xUB(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ ix += xbytes;
+ x -= 2147483647;
+ out_buff[ox++] = x * (1.0f / 2147483647.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * 2147483647.0);
+ x += 2147483647;
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ }
+ return out_buff;
+ }
+ }
+
+ public static AudioFloatConverter getConverter(AudioFormat format) {
+ AudioFloatConverter conv = null;
+ if (format.getFrameSize() == 0)
+ return null;
+ if (format.getFrameSize() !=
+ ((format.getSampleSizeInBits() + 7) / 8) * format.getChannels()) {
+ return null;
+ }
+ if (format.getEncoding().equals(Encoding.PCM_SIGNED)) {
+ if (format.isBigEndian()) {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8S();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16SB();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24SB();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32SB();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xSB(((format
+ .getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ } else {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8S();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16SL();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24SL();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32SL();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xSL(((format
+ .getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ }
+ } else if (format.getEncoding().equals(Encoding.PCM_UNSIGNED)) {
+ if (format.isBigEndian()) {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8U();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16UB();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24UB();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32UB();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xUB(((
+ format.getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ } else {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8U();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16UL();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24UL();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32UL();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xUL(((
+ format.getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ }
+ } else if (format.getEncoding().equals(PCM_FLOAT)) {
+ if (format.getSampleSizeInBits() == 32) {
+ if (format.isBigEndian())
+ conv = new AudioFloatConversion32B();
+ else
+ conv = new AudioFloatConversion32L();
+ } else if (format.getSampleSizeInBits() == 64) {
+ if (format.isBigEndian())
+ conv = new AudioFloatConversion64B();
+ else
+ conv = new AudioFloatConversion64L();
+ }
+
+ }
+
+ if ((format.getEncoding().equals(Encoding.PCM_SIGNED) ||
+ format.getEncoding().equals(Encoding.PCM_UNSIGNED)) &&
+ (format.getSampleSizeInBits() % 8 != 0)) {
+ conv = new AudioFloatLSBFilter(conv, format);
+ }
+
+ if (conv != null)
+ conv.format = format;
+ return conv;
+ }
+
+ private AudioFormat format;
+
+ public AudioFormat getFormat() {
+ return format;
+ }
+
+ public abstract float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len);
+
+ public float[] toFloatArray(byte[] in_buff, float[] out_buff,
+ int out_offset, int out_len) {
+ return toFloatArray(in_buff, 0, out_buff, out_offset, out_len);
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_len) {
+ return toFloatArray(in_buff, in_offset, out_buff, 0, out_len);
+ }
+
+ public float[] toFloatArray(byte[] in_buff, float[] out_buff, int out_len) {
+ return toFloatArray(in_buff, 0, out_buff, 0, out_len);
+ }
+
+ public float[] toFloatArray(byte[] in_buff, float[] out_buff) {
+ return toFloatArray(in_buff, 0, out_buff, 0, out_buff.length);
+ }
+
+ public abstract byte[] toByteArray(float[] in_buff, int in_offset,
+ int in_len, byte[] out_buff, int out_offset);
+
+ public byte[] toByteArray(float[] in_buff, int in_len, byte[] out_buff,
+ int out_offset) {
+ return toByteArray(in_buff, 0, in_len, out_buff, out_offset);
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff) {
+ return toByteArray(in_buff, in_offset, in_len, out_buff, 0);
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_len, byte[] out_buff) {
+ return toByteArray(in_buff, 0, in_len, out_buff, 0);
+ }
+
+ public byte[] toByteArray(float[] in_buff, byte[] out_buff) {
+ return toByteArray(in_buff, 0, in_buff.length, out_buff, 0);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java b/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java
new file mode 100644
index 000000000..4e835f4c1
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java
@@ -0,0 +1,617 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.AudioFormat.Encoding;
+import javax.sound.sampled.spi.FormatConversionProvider;
+
+/**
+ * This class is used to convert between 8,16,24,32 bit signed/unsigned
+ * big/litle endian fixed/floating stereo/mono/multi-channel audio streams and
+ * perform sample-rate conversion if needed.
+ *
+ * @author Karl Helgason
+ */
+public class AudioFloatFormatConverter extends FormatConversionProvider {
+
+ private static class AudioFloatFormatConverterInputStream extends
+ InputStream {
+ private AudioFloatConverter converter;
+
+ private AudioFloatInputStream stream;
+
+ private float[] readfloatbuffer;
+
+ private int fsize = 0;
+
+ public AudioFloatFormatConverterInputStream(AudioFormat targetFormat,
+ AudioFloatInputStream stream) {
+ this.stream = stream;
+ converter = AudioFloatConverter.getConverter(targetFormat);
+ fsize = ((targetFormat.getSampleSizeInBits() + 7) / 8);
+ }
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ int ret = read(b);
+ if (ret < 0)
+ return ret;
+ return b[0] & 0xFF;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+
+ int flen = len / fsize;
+ if (readfloatbuffer == null || readfloatbuffer.length < flen)
+ readfloatbuffer = new float[flen];
+ int ret = stream.read(readfloatbuffer, 0, flen);
+ if (ret < 0)
+ return ret;
+ converter.toByteArray(readfloatbuffer, 0, ret, b, off);
+ return ret * fsize;
+ }
+
+ public int available() throws IOException {
+ int ret = stream.available();
+ if (ret < 0)
+ return ret;
+ return ret * fsize;
+ }
+
+ public void close() throws IOException {
+ stream.close();
+ }
+
+ public synchronized void mark(int readlimit) {
+ stream.mark(readlimit * fsize);
+ }
+
+ public boolean markSupported() {
+ return stream.markSupported();
+ }
+
+ public synchronized void reset() throws IOException {
+ stream.reset();
+ }
+
+ public long skip(long n) throws IOException {
+ long ret = stream.skip(n / fsize);
+ if (ret < 0)
+ return ret;
+ return ret * fsize;
+ }
+
+ }
+
+ private static class AudioFloatInputStreamChannelMixer extends
+ AudioFloatInputStream {
+
+ private int targetChannels;
+
+ private int sourceChannels;
+
+ private AudioFloatInputStream ais;
+
+ private AudioFormat targetFormat;
+
+ private float[] conversion_buffer;
+
+ public AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais,
+ int targetChannels) {
+ this.sourceChannels = ais.getFormat().getChannels();
+ this.targetChannels = targetChannels;
+ this.ais = ais;
+ AudioFormat format = ais.getFormat();
+ targetFormat = new AudioFormat(format.getEncoding(), format
+ .getSampleRate(), format.getSampleSizeInBits(),
+ targetChannels, (format.getFrameSize() / sourceChannels)
+ * targetChannels, format.getFrameRate(), format
+ .isBigEndian());
+ }
+
+ public int available() throws IOException {
+ return (ais.available() / sourceChannels) * targetChannels;
+ }
+
+ public void close() throws IOException {
+ ais.close();
+ }
+
+ public AudioFormat getFormat() {
+ return targetFormat;
+ }
+
+ public long getFrameLength() {
+ return ais.getFrameLength();
+ }
+
+ public void mark(int readlimit) {
+ ais.mark((readlimit / targetChannels) * sourceChannels);
+ }
+
+ public boolean markSupported() {
+ return ais.markSupported();
+ }
+
+ public int read(float[] b, int off, int len) throws IOException {
+ int len2 = (len / targetChannels) * sourceChannels;
+ if (conversion_buffer == null || conversion_buffer.length < len2)
+ conversion_buffer = new float[len2];
+ int ret = ais.read(conversion_buffer, 0, len2);
+ if (ret < 0)
+ return ret;
+ if (sourceChannels == 1) {
+ int cs = targetChannels;
+ for (int c = 0; c < targetChannels; c++) {
+ for (int i = 0, ix = off + c; i < len2; i++, ix += cs) {
+ b[ix] = conversion_buffer[i];
+ ;
+ }
+ }
+ } else if (targetChannels == 1) {
+ int cs = sourceChannels;
+ for (int i = 0, ix = off; i < len2; i += cs, ix++) {
+ b[ix] = conversion_buffer[i];
+ }
+ for (int c = 1; c < sourceChannels; c++) {
+ for (int i = c, ix = off; i < len2; i += cs, ix++) {
+ b[ix] += conversion_buffer[i];
+ ;
+ }
+ }
+ float vol = 1f / ((float) sourceChannels);
+ for (int i = 0, ix = off; i < len2; i += cs, ix++) {
+ b[ix] *= vol;
+ }
+ } else {
+ int minChannels = Math.min(sourceChannels, targetChannels);
+ int off_len = off + len;
+ int ct = targetChannels;
+ int cs = sourceChannels;
+ for (int c = 0; c < minChannels; c++) {
+ for (int i = off + c, ix = c; i < off_len; i += ct, ix += cs) {
+ b[i] = conversion_buffer[ix];
+ }
+ }
+ for (int c = minChannels; c < targetChannels; c++) {
+ for (int i = off + c; i < off_len; i += ct) {
+ b[i] = 0;
+ }
+ }
+ }
+ return (ret / sourceChannels) * targetChannels;
+ }
+
+ public void reset() throws IOException {
+ ais.reset();
+ }
+
+ public long skip(long len) throws IOException {
+ long ret = ais.skip((len / targetChannels) * sourceChannels);
+ if (ret < 0)
+ return ret;
+ return (ret / sourceChannels) * targetChannels;
+ }
+
+ }
+
+ private static class AudioFloatInputStreamResampler extends
+ AudioFloatInputStream {
+
+ private AudioFloatInputStream ais;
+
+ private AudioFormat targetFormat;
+
+ private float[] skipbuffer;
+
+ private SoftAbstractResampler resampler;
+
+ private float[] pitch = new float[1];
+
+ private float[] ibuffer2;
+
+ private float[][] ibuffer;
+
+ private float ibuffer_index = 0;
+
+ private int ibuffer_len = 0;
+
+ private int nrofchannels = 0;
+
+ private float[][] cbuffer;
+
+ private int buffer_len = 512;
+
+ private int pad;
+
+ private int pad2;
+
+ private float[] ix = new float[1];
+
+ private int[] ox = new int[1];
+
+ private float[][] mark_ibuffer = null;
+
+ private float mark_ibuffer_index = 0;
+
+ private int mark_ibuffer_len = 0;
+
+ public AudioFloatInputStreamResampler(AudioFloatInputStream ais,
+ AudioFormat format) {
+ this.ais = ais;
+ AudioFormat sourceFormat = ais.getFormat();
+ targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
+ .getSampleRate(), sourceFormat.getSampleSizeInBits(),
+ sourceFormat.getChannels(), sourceFormat.getFrameSize(),
+ format.getSampleRate(), sourceFormat.isBigEndian());
+ nrofchannels = targetFormat.getChannels();
+ Object interpolation = format.getProperty("interpolation");
+ if (interpolation != null && (interpolation instanceof String)) {
+ String resamplerType = (String) interpolation;
+ if (resamplerType.equalsIgnoreCase("point"))
+ this.resampler = new SoftPointResampler();
+ if (resamplerType.equalsIgnoreCase("linear"))
+ this.resampler = new SoftLinearResampler2();
+ if (resamplerType.equalsIgnoreCase("linear1"))
+ this.resampler = new SoftLinearResampler();
+ if (resamplerType.equalsIgnoreCase("linear2"))
+ this.resampler = new SoftLinearResampler2();
+ if (resamplerType.equalsIgnoreCase("cubic"))
+ this.resampler = new SoftCubicResampler();
+ if (resamplerType.equalsIgnoreCase("lanczos"))
+ this.resampler = new SoftLanczosResampler();
+ if (resamplerType.equalsIgnoreCase("sinc"))
+ this.resampler = new SoftSincResampler();
+ }
+ if (resampler == null)
+ resampler = new SoftLinearResampler2(); // new
+ // SoftLinearResampler2();
+ pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
+ pad = resampler.getPadding();
+ pad2 = pad * 2;
+ ibuffer = new float[nrofchannels][buffer_len + pad2];
+ ibuffer2 = new float[nrofchannels * buffer_len];
+ ibuffer_index = buffer_len + pad;
+ ibuffer_len = buffer_len;
+ }
+
+ public int available() throws IOException {
+ return 0;
+ }
+
+ public void close() throws IOException {
+ ais.close();
+ }
+
+ public AudioFormat getFormat() {
+ return targetFormat;
+ }
+
+ public long getFrameLength() {
+ return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
+ }
+
+ public void mark(int readlimit) {
+ ais.mark((int) (readlimit * pitch[0]));
+ mark_ibuffer_index = ibuffer_index;
+ mark_ibuffer_len = ibuffer_len;
+ if (mark_ibuffer == null) {
+ mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
+ }
+ for (int c = 0; c < ibuffer.length; c++) {
+ float[] from = ibuffer[c];
+ float[] to = mark_ibuffer[c];
+ for (int i = 0; i < to.length; i++) {
+ to[i] = from[i];
+ }
+ }
+ }
+
+ public boolean markSupported() {
+ return ais.markSupported();
+ }
+
+ private void readNextBuffer() throws IOException {
+
+ if (ibuffer_len == -1)
+ return;
+
+ for (int c = 0; c < nrofchannels; c++) {
+ float[] buff = ibuffer[c];
+ int buffer_len_pad = ibuffer_len + pad2;
+ for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
+ buff[ix] = buff[i];
+ }
+ }
+
+ ibuffer_index -= (ibuffer_len);
+
+ ibuffer_len = ais.read(ibuffer2);
+ if (ibuffer_len >= 0) {
+ while (ibuffer_len < ibuffer2.length) {
+ int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
+ - ibuffer_len);
+ if (ret == -1)
+ break;
+ ibuffer_len += ret;
+ }
+ Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
+ ibuffer_len /= nrofchannels;
+ } else {
+ Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
+ }
+
+ int ibuffer2_len = ibuffer2.length;
+ for (int c = 0; c < nrofchannels; c++) {
+ float[] buff = ibuffer[c];
+ for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
+ buff[ix] = ibuffer2[i];
+ }
+ }
+
+ }
+
+ public int read(float[] b, int off, int len) throws IOException {
+
+ if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
+ cbuffer = new float[nrofchannels][len / nrofchannels];
+ }
+ if (ibuffer_len == -1)
+ return -1;
+ if (len < 0)
+ return 0;
+ int remain = len / nrofchannels;
+ int destPos = 0;
+ int in_end = ibuffer_len;
+ while (remain > 0) {
+ if (ibuffer_len >= 0) {
+ if (ibuffer_index >= (ibuffer_len + pad))
+ readNextBuffer();
+ in_end = ibuffer_len + pad;
+ }
+
+ if (ibuffer_len < 0) {
+ in_end = pad2;
+ if (ibuffer_index >= in_end)
+ break;
+ }
+
+ if (ibuffer_index < 0)
+ break;
+ int preDestPos = destPos;
+ for (int c = 0; c < nrofchannels; c++) {
+ ix[0] = ibuffer_index;
+ ox[0] = destPos;
+ float[] buff = ibuffer[c];
+ resampler.interpolate(buff, ix, in_end, pitch, 0,
+ cbuffer[c], ox, len / nrofchannels);
+ }
+ ibuffer_index = ix[0];
+ destPos = ox[0];
+ remain -= destPos - preDestPos;
+ }
+ for (int c = 0; c < nrofchannels; c++) {
+ int ix = 0;
+ float[] buff = cbuffer[c];
+ for (int i = c; i < b.length; i += nrofchannels) {
+ b[i] = buff[ix++];
+ }
+ }
+ return len - remain * nrofchannels;
+ }
+
+ public void reset() throws IOException {
+ ais.reset();
+ if (mark_ibuffer == null)
+ return;
+ ibuffer_index = mark_ibuffer_index;
+ ibuffer_len = mark_ibuffer_len;
+ for (int c = 0; c < ibuffer.length; c++) {
+ float[] from = mark_ibuffer[c];
+ float[] to = ibuffer[c];
+ for (int i = 0; i < to.length; i++) {
+ to[i] = from[i];
+ }
+ }
+
+ }
+
+ public long skip(long len) throws IOException {
+ if (len > 0)
+ return 0;
+ if (skipbuffer == null)
+ skipbuffer = new float[1024 * targetFormat.getFrameSize()];
+ float[] l_skipbuffer = skipbuffer;
+ long remain = len;
+ while (remain > 0) {
+ int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
+ skipbuffer.length));
+ if (ret < 0) {
+ if (remain == len)
+ return ret;
+ break;
+ }
+ remain -= ret;
+ }
+ return len - remain;
+
+ }
+
+ }
+
+ private Encoding[] formats = { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
+ AudioFloatConverter.PCM_FLOAT };
+
+ public AudioInputStream getAudioInputStream(Encoding targetEncoding,
+ AudioInputStream sourceStream) {
+ if (sourceStream.getFormat().getEncoding().equals(targetEncoding))
+ return sourceStream;
+ AudioFormat format = sourceStream.getFormat();
+ int channels = format.getChannels();
+ Encoding encoding = targetEncoding;
+ float samplerate = format.getSampleRate();
+ int bits = format.getSampleSizeInBits();
+ boolean bigendian = format.isBigEndian();
+ if (targetEncoding.equals(AudioFloatConverter.PCM_FLOAT))
+ bits = 32;
+ AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits,
+ channels, channels * bits / 8, samplerate, bigendian);
+ return getAudioInputStream(targetFormat, sourceStream);
+ }
+
+ public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
+ AudioInputStream sourceStream) {
+ if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
+ throw new IllegalArgumentException("Unsupported conversion: "
+ + sourceStream.getFormat().toString() + " to "
+ + targetFormat.toString());
+ return getAudioInputStream(targetFormat, AudioFloatInputStream
+ .getInputStream(sourceStream));
+ }
+
+ public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
+ AudioFloatInputStream sourceStream) {
+
+ if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
+ throw new IllegalArgumentException("Unsupported conversion: "
+ + sourceStream.getFormat().toString() + " to "
+ + targetFormat.toString());
+ if (targetFormat.getChannels() != sourceStream.getFormat()
+ .getChannels())
+ sourceStream = new AudioFloatInputStreamChannelMixer(sourceStream,
+ targetFormat.getChannels());
+ if (Math.abs(targetFormat.getSampleRate()
+ - sourceStream.getFormat().getSampleRate()) > 0.000001)
+ sourceStream = new AudioFloatInputStreamResampler(sourceStream,
+ targetFormat);
+ return new AudioInputStream(new AudioFloatFormatConverterInputStream(
+ targetFormat, sourceStream), targetFormat, sourceStream
+ .getFrameLength());
+ }
+
+ public Encoding[] getSourceEncodings() {
+ return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
+ AudioFloatConverter.PCM_FLOAT };
+ }
+
+ public Encoding[] getTargetEncodings() {
+ return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
+ AudioFloatConverter.PCM_FLOAT };
+ }
+
+ public Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
+ if (AudioFloatConverter.getConverter(sourceFormat) == null)
+ return new Encoding[0];
+ return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
+ AudioFloatConverter.PCM_FLOAT };
+ }
+
+ public AudioFormat[] getTargetFormats(Encoding targetEncoding,
+ AudioFormat sourceFormat) {
+ if (AudioFloatConverter.getConverter(sourceFormat) == null)
+ return new AudioFormat[0];
+ int channels = sourceFormat.getChannels();
+
+ ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>();
+
+ if (targetEncoding.equals(Encoding.PCM_SIGNED))
+ formats.add(new AudioFormat(Encoding.PCM_SIGNED,
+ AudioSystem.NOT_SPECIFIED, 8, channels, channels,
+ AudioSystem.NOT_SPECIFIED, false));
+ if (targetEncoding.equals(Encoding.PCM_UNSIGNED))
+ formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
+ AudioSystem.NOT_SPECIFIED, 8, channels, channels,
+ AudioSystem.NOT_SPECIFIED, false));
+
+ for (int bits = 16; bits < 32; bits += 8) {
+ if (targetEncoding.equals(Encoding.PCM_SIGNED)) {
+ formats.add(new AudioFormat(Encoding.PCM_SIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(Encoding.PCM_SIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, true));
+ }
+ if (targetEncoding.equals(Encoding.PCM_UNSIGNED)) {
+ formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, true));
+ formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, false));
+ }
+ }
+
+ if (targetEncoding.equals(AudioFloatConverter.PCM_FLOAT)) {
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
+ AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
+ AudioSystem.NOT_SPECIFIED, true));
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
+ AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
+ AudioSystem.NOT_SPECIFIED, true));
+ }
+
+ return formats.toArray(new AudioFormat[formats.size()]);
+ }
+
+ public boolean isConversionSupported(AudioFormat targetFormat,
+ AudioFormat sourceFormat) {
+ if (AudioFloatConverter.getConverter(sourceFormat) == null)
+ return false;
+ if (AudioFloatConverter.getConverter(targetFormat) == null)
+ return false;
+ if (sourceFormat.getChannels() <= 0)
+ return false;
+ if (targetFormat.getChannels() <= 0)
+ return false;
+ return true;
+ }
+
+ public boolean isConversionSupported(Encoding targetEncoding,
+ AudioFormat sourceFormat) {
+ if (AudioFloatConverter.getConverter(sourceFormat) == null)
+ return false;
+ for (int i = 0; i < formats.length; i++) {
+ if (targetEncoding.equals(formats[i]))
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/AudioFloatInputStream.java b/src/share/classes/com/sun/media/sound/AudioFloatInputStream.java
new file mode 100644
index 000000000..9c3673721
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/AudioFloatInputStream.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+/**
+ * This class is used to create AudioFloatInputStream from AudioInputStream and
+ * byte buffers.
+ *
+ * @author Karl Helgason
+ */
+public abstract class AudioFloatInputStream {
+
+ private static class BytaArrayAudioFloatInputStream
+ extends AudioFloatInputStream {
+
+ private int pos = 0;
+ private int markpos = 0;
+ private AudioFloatConverter converter;
+ private AudioFormat format;
+ private byte[] buffer;
+ private int buffer_offset;
+ private int buffer_len;
+ private int framesize_pc;
+
+ public BytaArrayAudioFloatInputStream(AudioFloatConverter converter,
+ byte[] buffer, int offset, int len) {
+ this.converter = converter;
+ this.format = converter.getFormat();
+ this.buffer = buffer;
+ this.buffer_offset = offset;
+ framesize_pc = format.getFrameSize() / format.getChannels();
+ this.buffer_len = len / framesize_pc;
+
+ }
+
+ public AudioFormat getFormat() {
+ return format;
+ }
+
+ public long getFrameLength() {
+ return buffer_len;// / format.getFrameSize();
+ }
+
+ public int read(float[] b, int off, int len) throws IOException {
+ if (b == null)
+ throw new NullPointerException();
+ if (off < 0 || len < 0 || len > b.length - off)
+ throw new IndexOutOfBoundsException();
+ if (pos >= buffer_len)
+ return -1;
+ if (len == 0)
+ return 0;
+ if (pos + len > buffer_len)
+ len = buffer_len - pos;
+ converter.toFloatArray(buffer, buffer_offset + pos * framesize_pc,
+ b, off, len);
+ pos += len;
+ return len;
+ }
+
+ public long skip(long len) throws IOException {
+ if (pos >= buffer_len)
+ return -1;
+ if (len <= 0)
+ return 0;
+ if (pos + len > buffer_len)
+ len = buffer_len - pos;
+ pos += len;
+ return len;
+ }
+
+ public int available() throws IOException {
+ return buffer_len - pos;
+ }
+
+ public void close() throws IOException {
+ }
+
+ public void mark(int readlimit) {
+ markpos = pos;
+ }
+
+ public boolean markSupported() {
+ return true;
+ }
+
+ public void reset() throws IOException {
+ pos = markpos;
+ }
+ }
+
+ private static class DirectAudioFloatInputStream
+ extends AudioFloatInputStream {
+
+ private AudioInputStream stream;
+ private AudioFloatConverter converter;
+ private int framesize_pc; // framesize / channels
+ private byte[] buffer;
+
+ public DirectAudioFloatInputStream(AudioInputStream stream) {
+ converter = AudioFloatConverter.getConverter(stream.getFormat());
+ if (converter == null) {
+ AudioFormat format = stream.getFormat();
+ AudioFormat newformat;
+
+ AudioFormat[] formats = AudioSystem.getTargetFormats(
+ AudioFormat.Encoding.PCM_SIGNED, format);
+ if (formats.length != 0) {
+ newformat = formats[0];
+ } else {
+ float samplerate = format.getSampleRate();
+ int samplesizeinbits = format.getSampleSizeInBits();
+ int framesize = format.getFrameSize();
+ float framerate = format.getFrameRate();
+ samplesizeinbits = 16;
+ framesize = format.getChannels() * (samplesizeinbits / 8);
+ framerate = samplerate;
+
+ newformat = new AudioFormat(
+ AudioFormat.Encoding.PCM_SIGNED, samplerate,
+ samplesizeinbits, format.getChannels(), framesize,
+ framerate, false);
+ }
+
+ stream = AudioSystem.getAudioInputStream(newformat, stream);
+ converter = AudioFloatConverter.getConverter(stream.getFormat());
+ }
+ framesize_pc = stream.getFormat().getFrameSize()
+ / stream.getFormat().getChannels();
+ this.stream = stream;
+ }
+
+ public AudioFormat getFormat() {
+ return stream.getFormat();
+ }
+
+ public long getFrameLength() {
+ return stream.getFrameLength();
+ }
+
+ public int read(float[] b, int off, int len) throws IOException {
+ int b_len = len * framesize_pc;
+ if (buffer == null || buffer.length < b_len)
+ buffer = new byte[b_len];
+ int ret = stream.read(buffer, 0, b_len);
+ if (ret == -1)
+ return -1;
+ converter.toFloatArray(buffer, b, off, ret / framesize_pc);
+ return ret / framesize_pc;
+ }
+
+ public long skip(long len) throws IOException {
+ long b_len = len * framesize_pc;
+ long ret = stream.skip(b_len);
+ if (ret == -1)
+ return -1;
+ return ret / framesize_pc;
+ }
+
+ public int available() throws IOException {
+ return stream.available() / framesize_pc;
+ }
+
+ public void close() throws IOException {
+ stream.close();
+ }
+
+ public void mark(int readlimit) {
+ stream.mark(readlimit * framesize_pc);
+ }
+
+ public boolean markSupported() {
+ return stream.markSupported();
+ }
+
+ public void reset() throws IOException {
+ stream.reset();
+ }
+ }
+
+ public static AudioFloatInputStream getInputStream(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ return new DirectAudioFloatInputStream(AudioSystem
+ .getAudioInputStream(url));
+ }
+
+ public static AudioFloatInputStream getInputStream(File file)
+ throws UnsupportedAudioFileException, IOException {
+ return new DirectAudioFloatInputStream(AudioSystem
+ .getAudioInputStream(file));
+ }
+
+ public static AudioFloatInputStream getInputStream(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+ return new DirectAudioFloatInputStream(AudioSystem
+ .getAudioInputStream(stream));
+ }
+
+ public static AudioFloatInputStream getInputStream(
+ AudioInputStream stream) {
+ return new DirectAudioFloatInputStream(stream);
+ }
+
+ public static AudioFloatInputStream getInputStream(AudioFormat format,
+ byte[] buffer, int offset, int len) {
+ AudioFloatConverter converter = AudioFloatConverter
+ .getConverter(format);
+ if (converter != null)
+ return new BytaArrayAudioFloatInputStream(converter, buffer,
+ offset, len);
+
+ InputStream stream = new ByteArrayInputStream(buffer, offset, len);
+ long aLen = format.getFrameSize() == AudioSystem.NOT_SPECIFIED
+ ? AudioSystem.NOT_SPECIFIED : len / format.getFrameSize();
+ AudioInputStream astream = new AudioInputStream(stream, format, aLen);
+ return getInputStream(astream);
+ }
+
+ public abstract AudioFormat getFormat();
+
+ public abstract long getFrameLength();
+
+ public abstract int read(float[] b, int off, int len) throws IOException;
+
+ public int read(float[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public float read() throws IOException {
+ float[] b = new float[1];
+ int ret = read(b, 0, 1);
+ if (ret == -1 || ret == 0)
+ return 0;
+ return b[0];
+ }
+
+ public abstract long skip(long len) throws IOException;
+
+ public abstract int available() throws IOException;
+
+ public abstract void close() throws IOException;
+
+ public abstract void mark(int readlimit);
+
+ public abstract boolean markSupported();
+
+ public abstract void reset() throws IOException;
+}
diff --git a/src/share/classes/com/sun/media/sound/AudioSynthesizer.java b/src/share/classes/com/sun/media/sound/AudioSynthesizer.java
new file mode 100644
index 000000000..247e03f04
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/AudioSynthesizer.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.Map;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Synthesizer;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.SourceDataLine;
+
+/**
+ * <code>AudioSynthesizer</code> is a <code>Synthesizer</code>
+ * which renders it's output audio into <code>SourceDataLine</code>
+ * or <code>AudioInputStream</code>.
+ *
+ * @see MidiSystem#getSynthesizer
+ * @see Synthesizer
+ *
+ * @author Karl Helgason
+ */
+public interface AudioSynthesizer extends Synthesizer {
+
+ /**
+ * Obtains the current format (encoding, sample rate, number of channels,
+ * etc.) of the synthesizer audio data.
+ *
+ * <p>If the synthesizer is not open and has never been opened, it returns
+ * the default format.
+ *
+ * @return current audio data format
+ * @see AudioFormat
+ */
+ public AudioFormat getFormat();
+
+ /**
+ * Gets information about the possible properties for the synthesizer.
+ *
+ * @param info a proposed list of tag/value pairs that will be sent on open.
+ * @return an array of <code>AudioSynthesizerPropertyInfo</code> objects
+ * describing possible properties. This array may be an empty array if
+ * no properties are required.
+ */
+ public AudioSynthesizerPropertyInfo[] getPropertyInfo(
+ Map<String, Object> info);
+
+ /**
+ * Opens the synthesizer and starts rendering audio into
+ * <code>SourceDataLine</code>.
+ *
+ * <p>An application opening a synthesizer explicitly with this call
+ * has to close the synthesizer by calling {@link #close}. This is
+ * necessary to release system resources and allow applications to
+ * exit cleanly.
+ *
+ * <p>Note that some synthesizers, once closed, cannot be reopened.
+ * Attempts to reopen such a synthesizer will always result in
+ * a <code>MidiUnavailableException</code>.
+ *
+ * @param line which <code>AudioSynthesizer</code> writes output audio into.
+ * If <code>line</code> is null, then line from system default mixer is used.
+ * @param info a <code>Map<String,Object></code> object containing
+ * properties for additional configuration supported by synthesizer.
+ * If <code>info</code> is null then default settings are used.
+ *
+ * @throws MidiUnavailableException thrown if the synthesizer cannot be
+ * opened due to resource restrictions.
+ * @throws SecurityException thrown if the synthesizer cannot be
+ * opened due to security restrictions.
+ *
+ * @see #close
+ * @see #isOpen
+ */
+ public void open(SourceDataLine line, Map<String, Object> info)
+ throws MidiUnavailableException;
+
+ /**
+ * Opens the synthesizer and renders audio into returned
+ * <code>AudioInputStream</code>.
+ *
+ * <p>An application opening a synthesizer explicitly with this call
+ * has to close the synthesizer by calling {@link #close}. This is
+ * necessary to release system resources and allow applications to
+ * exit cleanly.
+ *
+ * <p>Note that some synthesizers, once closed, cannot be reopened.
+ * Attempts to reopen such a synthesizer will always result in
+ * a <code>MidiUnavailableException<code>.
+ *
+ * @param targetFormat specifies the <code>AudioFormat</code>
+ * used in returned <code>AudioInputStream</code>.
+ * @param info a <code>Map<String,Object></code> object containing
+ * properties for additional configuration supported by synthesizer.
+ * If <code>info</code> is null then default settings are used.
+ *
+ * @throws MidiUnavailableException thrown if the synthesizer cannot be
+ * opened due to resource restrictions.
+ * @throws SecurityException thrown if the synthesizer cannot be
+ * opened due to security restrictions.
+ *
+ * @see #close
+ * @see #isOpen
+ */
+ public AudioInputStream openStream(AudioFormat targetFormat,
+ Map<String, Object> info) throws MidiUnavailableException;
+}
diff --git a/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java b/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java
new file mode 100644
index 000000000..cf40dda25
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Information about property used in opening <code>AudioSynthesizer</code>.
+ *
+ * @author Karl Helgason
+ */
+public class AudioSynthesizerPropertyInfo {
+
+ /**
+ * Constructs a <code>AudioSynthesizerPropertyInfo</code> object with a given
+ * name and value. The <code>description</code> and <code>choices</code>
+ * are intialized by <code>null</code> values.
+ *
+ * @param name the name of the property
+ * @param value the current value or class used for values.
+ *
+ */
+ public AudioSynthesizerPropertyInfo(String name, Object value) {
+ this.name = name;
+ this.value = value;
+ if (value instanceof Class)
+ valueClass = (Class)value;
+ else if (value != null)
+ valueClass = value.getClass();
+ }
+ /**
+ * The name of the property.
+ */
+ public String name;
+ /**
+ * A brief description of the property, which may be null.
+ */
+ public String description = null;
+ /**
+ * The <code>value</code> field specifies the current value of
+ * the property.
+ */
+ public Object value = null;
+ /**
+ * The <code>valueClass</code> field specifies class
+ * used in <code>value</code> field.
+ */
+ public Class valueClass = null;
+ /**
+ * An array of possible values if the value for the field
+ * <code>AudioSynthesizerPropertyInfo.value</code> may be selected
+ * from a particular set of values; otherwise null.
+ */
+ public Object[] choices = null;
+
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSInfo.java b/src/share/classes/com/sun/media/sound/DLSInfo.java
new file mode 100644
index 000000000..4e9042161
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSInfo.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This class is used to store information to describe soundbanks, instruments
+ * and samples. It is stored inside a "INFO" List Chunk inside DLS files.
+ *
+ * @author Karl Helgason
+ */
+public class DLSInfo {
+
+ /**
+ * (INAM) Title or subject.
+ */
+ public String name = "untitled";
+ /**
+ * (ICRD) Date of creation, the format is: YYYY-MM-DD.
+ * For example 2007-01-01 for 1. january of year 2007.
+ */
+ public String creationDate = null;
+ /**
+ * (IENG) Name of engineer who created the object.
+ */
+ public String engineers = null;
+ /**
+ * (IPRD) Name of the product which the object is intended for.
+ */
+ public String product = null;
+ /**
+ * (ICOP) Copyright information.
+ */
+ public String copyright = null;
+ /**
+ * (ICMT) General comments. Doesn't contain newline characters.
+ */
+ public String comments = null;
+ /**
+ * (ISFT) Name of software package used to create the file.
+ */
+ public String tools = null;
+ /**
+ * (IARL) Where content is archived.
+ */
+ public String archival_location = null;
+ /**
+ * (IART) Artists of original content.
+ */
+ public String artist = null;
+ /**
+ * (ICMS) Names of persons or orginizations who commissioned the file.
+ */
+ public String commissioned = null;
+ /**
+ * (IGNR) Genre of the work.
+ * Example: jazz, classical, rock, etc.
+ */
+ public String genre = null;
+ /**
+ * (IKEY) List of keyword that describe the content.
+ * Examples: FX, bird, piano, etc.
+ */
+ public String keywords = null;
+ /**
+ * (IMED) Describes original medium of the data.
+ * For example: record, CD, etc.
+ */
+ public String medium = null;
+ /**
+ * (ISBJ) Description of the content.
+ */
+ public String subject = null;
+ /**
+ * (ISRC) Name of person or orginization who supplied
+ * orginal material for the file.
+ */
+ public String source = null;
+ /**
+ * (ISRF) Source media for sample data is from.
+ * For example: CD, TV, etc.
+ */
+ public String source_form = null;
+ /**
+ * (ITCH) Technician who sample the file/object.
+ */
+ public String technician = null;
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSInstrument.java b/src/share/classes/com/sun/media/sound/DLSInstrument.java
new file mode 100644
index 000000000..c4912314a
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSInstrument.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sound.midi.Patch;
+
+/**
+ * This class is used to store information to describe instrument.
+ * It contains list of regions and modulators.
+ * It is stored inside a "ins " List Chunk inside DLS files.
+ * In the DLS documentation a modulator is called articulator.
+ *
+ * @author Karl Helgason
+ */
+public class DLSInstrument extends ModelInstrument {
+
+ protected int preset = 0;
+ protected int bank = 0;
+ protected boolean druminstrument = false;
+ protected byte[] guid = null;
+ protected DLSInfo info = new DLSInfo();
+ protected List<DLSRegion> regions = new ArrayList<DLSRegion>();
+ protected List<DLSModulator> modulators = new ArrayList<DLSModulator>();
+
+ public DLSInstrument() {
+ super(null, null, null, null);
+ }
+
+ public DLSInstrument(DLSSoundbank soundbank) {
+ super(soundbank, null, null, null);
+ }
+
+ public DLSInfo getInfo() {
+ return info;
+ }
+
+ public String getName() {
+ return info.name;
+ }
+
+ public void setName(String name) {
+ info.name = name;
+ }
+
+ public ModelPatch getPatch() {
+ return new ModelPatch(bank, preset, druminstrument);
+ }
+
+ public void setPatch(Patch patch) {
+ if (patch instanceof ModelPatch && ((ModelPatch)patch).isPercussion()) {
+ druminstrument = true;
+ bank = patch.getBank();
+ preset = patch.getProgram();
+ } else {
+ druminstrument = false;
+ bank = patch.getBank();
+ preset = patch.getProgram();
+ }
+ }
+
+ public Object getData() {
+ return null;
+ }
+
+ public List<DLSRegion> getRegions() {
+ return regions;
+ }
+
+ public List<DLSModulator> getModulators() {
+ return modulators;
+ }
+
+ public String toString() {
+ if (druminstrument)
+ return "Drumkit: " + info.name
+ + " bank #" + bank + " preset #" + preset;
+ else
+ return "Instrument: " + info.name
+ + " bank #" + bank + " preset #" + preset;
+ }
+
+ private ModelIdentifier convertToModelDest(int dest) {
+ if (dest == DLSModulator.CONN_DST_NONE)
+ return null;
+ if (dest == DLSModulator.CONN_DST_GAIN)
+ return ModelDestination.DESTINATION_GAIN;
+ if (dest == DLSModulator.CONN_DST_PITCH)
+ return ModelDestination.DESTINATION_PITCH;
+ if (dest == DLSModulator.CONN_DST_PAN)
+ return ModelDestination.DESTINATION_PAN;
+
+ if (dest == DLSModulator.CONN_DST_LFO_FREQUENCY)
+ return ModelDestination.DESTINATION_LFO1_FREQ;
+ if (dest == DLSModulator.CONN_DST_LFO_STARTDELAY)
+ return ModelDestination.DESTINATION_LFO1_DELAY;
+
+ if (dest == DLSModulator.CONN_DST_EG1_ATTACKTIME)
+ return ModelDestination.DESTINATION_EG1_ATTACK;
+ if (dest == DLSModulator.CONN_DST_EG1_DECAYTIME)
+ return ModelDestination.DESTINATION_EG1_DECAY;
+ if (dest == DLSModulator.CONN_DST_EG1_RELEASETIME)
+ return ModelDestination.DESTINATION_EG1_RELEASE;
+ if (dest == DLSModulator.CONN_DST_EG1_SUSTAINLEVEL)
+ return ModelDestination.DESTINATION_EG1_SUSTAIN;
+
+ if (dest == DLSModulator.CONN_DST_EG2_ATTACKTIME)
+ return ModelDestination.DESTINATION_EG2_ATTACK;
+ if (dest == DLSModulator.CONN_DST_EG2_DECAYTIME)
+ return ModelDestination.DESTINATION_EG2_DECAY;
+ if (dest == DLSModulator.CONN_DST_EG2_RELEASETIME)
+ return ModelDestination.DESTINATION_EG2_RELEASE;
+ if (dest == DLSModulator.CONN_DST_EG2_SUSTAINLEVEL)
+ return ModelDestination.DESTINATION_EG2_SUSTAIN;
+
+ // DLS2 Destinations
+ if (dest == DLSModulator.CONN_DST_KEYNUMBER)
+ return ModelDestination.DESTINATION_KEYNUMBER;
+
+ if (dest == DLSModulator.CONN_DST_CHORUS)
+ return ModelDestination.DESTINATION_CHORUS;
+ if (dest == DLSModulator.CONN_DST_REVERB)
+ return ModelDestination.DESTINATION_REVERB;
+
+ if (dest == DLSModulator.CONN_DST_VIB_FREQUENCY)
+ return ModelDestination.DESTINATION_LFO2_FREQ;
+ if (dest == DLSModulator.CONN_DST_VIB_STARTDELAY)
+ return ModelDestination.DESTINATION_LFO2_DELAY;
+
+ if (dest == DLSModulator.CONN_DST_EG1_DELAYTIME)
+ return ModelDestination.DESTINATION_EG1_DELAY;
+ if (dest == DLSModulator.CONN_DST_EG1_HOLDTIME)
+ return ModelDestination.DESTINATION_EG1_HOLD;
+ if (dest == DLSModulator.CONN_DST_EG1_SHUTDOWNTIME)
+ return ModelDestination.DESTINATION_EG1_SHUTDOWN;
+
+ if (dest == DLSModulator.CONN_DST_EG2_DELAYTIME)
+ return ModelDestination.DESTINATION_EG2_DELAY;
+ if (dest == DLSModulator.CONN_DST_EG2_HOLDTIME)
+ return ModelDestination.DESTINATION_EG2_HOLD;
+
+ if (dest == DLSModulator.CONN_DST_FILTER_CUTOFF)
+ return ModelDestination.DESTINATION_FILTER_FREQ;
+ if (dest == DLSModulator.CONN_DST_FILTER_Q)
+ return ModelDestination.DESTINATION_FILTER_Q;
+
+ return null;
+ }
+
+ private ModelIdentifier convertToModelSrc(int src) {
+ if (src == DLSModulator.CONN_SRC_NONE)
+ return null;
+
+ if (src == DLSModulator.CONN_SRC_LFO)
+ return ModelSource.SOURCE_LFO1;
+ if (src == DLSModulator.CONN_SRC_KEYONVELOCITY)
+ return ModelSource.SOURCE_NOTEON_VELOCITY;
+ if (src == DLSModulator.CONN_SRC_KEYNUMBER)
+ return ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ if (src == DLSModulator.CONN_SRC_EG1)
+ return ModelSource.SOURCE_EG1;
+ if (src == DLSModulator.CONN_SRC_EG2)
+ return ModelSource.SOURCE_EG2;
+ if (src == DLSModulator.CONN_SRC_PITCHWHEEL)
+ return ModelSource.SOURCE_MIDI_PITCH;
+ if (src == DLSModulator.CONN_SRC_CC1)
+ return new ModelIdentifier("midi_cc", "1", 0);
+ if (src == DLSModulator.CONN_SRC_CC7)
+ return new ModelIdentifier("midi_cc", "7", 0);
+ if (src == DLSModulator.CONN_SRC_CC10)
+ return new ModelIdentifier("midi_cc", "10", 0);
+ if (src == DLSModulator.CONN_SRC_CC11)
+ return new ModelIdentifier("midi_cc", "11", 0);
+ if (src == DLSModulator.CONN_SRC_RPN0)
+ return new ModelIdentifier("midi_rpn", "0", 0);
+ if (src == DLSModulator.CONN_SRC_RPN1)
+ return new ModelIdentifier("midi_rpn", "1", 0);
+
+ if (src == DLSModulator.CONN_SRC_POLYPRESSURE)
+ return ModelSource.SOURCE_MIDI_POLY_PRESSURE;
+ if (src == DLSModulator.CONN_SRC_CHANNELPRESSURE)
+ return ModelSource.SOURCE_MIDI_CHANNEL_PRESSURE;
+ if (src == DLSModulator.CONN_SRC_VIBRATO)
+ return ModelSource.SOURCE_LFO2;
+ if (src == DLSModulator.CONN_SRC_MONOPRESSURE)
+ return ModelSource.SOURCE_MIDI_CHANNEL_PRESSURE;
+
+ if (src == DLSModulator.CONN_SRC_CC91)
+ return new ModelIdentifier("midi_cc", "91", 0);
+ if (src == DLSModulator.CONN_SRC_CC93)
+ return new ModelIdentifier("midi_cc", "93", 0);
+
+ return null;
+ }
+
+ private ModelConnectionBlock convertToModel(DLSModulator mod) {
+ ModelIdentifier source = convertToModelSrc(mod.getSource());
+ ModelIdentifier control = convertToModelSrc(mod.getControl());
+ ModelIdentifier destination_id =
+ convertToModelDest(mod.getDestination());
+
+ int scale = mod.getScale();
+ double f_scale;
+ if (scale == Integer.MIN_VALUE)
+ f_scale = Double.NEGATIVE_INFINITY;
+ else
+ f_scale = scale / 65536.0;
+
+ if (destination_id != null) {
+ ModelSource src = null;
+ ModelSource ctrl = null;
+ ModelConnectionBlock block = new ModelConnectionBlock();
+ if (control != null) {
+ ModelSource s = new ModelSource();
+ if (control == ModelSource.SOURCE_MIDI_PITCH) {
+ ((ModelStandardTransform)s.getTransform()).setPolarity(
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ } else if (control == ModelSource.SOURCE_LFO1
+ || control == ModelSource.SOURCE_LFO2) {
+ ((ModelStandardTransform)s.getTransform()).setPolarity(
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ }
+ s.setIdentifier(control);
+ block.addSource(s);
+ ctrl = s;
+ }
+ if (source != null) {
+ ModelSource s = new ModelSource();
+ if (source == ModelSource.SOURCE_MIDI_PITCH) {
+ ((ModelStandardTransform)s.getTransform()).setPolarity(
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ } else if (source == ModelSource.SOURCE_LFO1
+ || source == ModelSource.SOURCE_LFO2) {
+ ((ModelStandardTransform)s.getTransform()).setPolarity(
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ }
+ s.setIdentifier(source);
+ block.addSource(s);
+ src = s;
+ }
+ ModelDestination destination = new ModelDestination();
+ destination.setIdentifier(destination_id);
+ block.setDestination(destination);
+
+ if (mod.getVersion() == 1) {
+ //if (mod.getTransform() == DLSModulator.CONN_TRN_CONCAVE) {
+ // ((ModelStandardTransform)destination.getTransform())
+ // .setTransform(
+ // ModelStandardTransform.TRANSFORM_CONCAVE);
+ //}
+ if (mod.getTransform() == DLSModulator.CONN_TRN_CONCAVE) {
+ if (src != null) {
+ ((ModelStandardTransform)src.getTransform())
+ .setTransform(
+ ModelStandardTransform.TRANSFORM_CONCAVE);
+ ((ModelStandardTransform)src.getTransform())
+ .setDirection(
+ ModelStandardTransform.DIRECTION_MAX2MIN);
+ }
+ if (ctrl != null) {
+ ((ModelStandardTransform)ctrl.getTransform())
+ .setTransform(
+ ModelStandardTransform.TRANSFORM_CONCAVE);
+ ((ModelStandardTransform)ctrl.getTransform())
+ .setDirection(
+ ModelStandardTransform.DIRECTION_MAX2MIN);
+ }
+ }
+
+ } else if (mod.getVersion() == 2) {
+ int transform = mod.getTransform();
+ int src_transform_invert = (transform >> 15) & 1;
+ int src_transform_bipolar = (transform >> 14) & 1;
+ int src_transform = (transform >> 10) & 8;
+ int ctr_transform_invert = (transform >> 9) & 1;
+ int ctr_transform_bipolar = (transform >> 8) & 1;
+ int ctr_transform = (transform >> 4) & 8;
+
+
+ if (src != null) {
+ int trans = ModelStandardTransform.TRANSFORM_LINEAR;
+ if (src_transform == DLSModulator.CONN_TRN_SWITCH)
+ trans = ModelStandardTransform.TRANSFORM_SWITCH;
+ if (src_transform == DLSModulator.CONN_TRN_CONCAVE)
+ trans = ModelStandardTransform.TRANSFORM_CONCAVE;
+ if (src_transform == DLSModulator.CONN_TRN_CONVEX)
+ trans = ModelStandardTransform.TRANSFORM_CONVEX;
+ ((ModelStandardTransform)src.getTransform())
+ .setTransform(trans);
+ ((ModelStandardTransform)src.getTransform())
+ .setPolarity(src_transform_bipolar == 1);
+ ((ModelStandardTransform)src.getTransform())
+ .setDirection(src_transform_invert == 1);
+
+ }
+
+ if (ctrl != null) {
+ int trans = ModelStandardTransform.TRANSFORM_LINEAR;
+ if (ctr_transform == DLSModulator.CONN_TRN_SWITCH)
+ trans = ModelStandardTransform.TRANSFORM_SWITCH;
+ if (ctr_transform == DLSModulator.CONN_TRN_CONCAVE)
+ trans = ModelStandardTransform.TRANSFORM_CONCAVE;
+ if (ctr_transform == DLSModulator.CONN_TRN_CONVEX)
+ trans = ModelStandardTransform.TRANSFORM_CONVEX;
+ ((ModelStandardTransform)ctrl.getTransform())
+ .setTransform(trans);
+ ((ModelStandardTransform)ctrl.getTransform())
+ .setPolarity(ctr_transform_bipolar == 1);
+ ((ModelStandardTransform)ctrl.getTransform())
+ .setDirection(ctr_transform_invert == 1);
+ }
+
+ /* No output transforms are defined the DLS Level 2
+ int out_transform = transform % 8;
+ int trans = ModelStandardTransform.TRANSFORM_LINEAR;
+ if (out_transform == DLSModulator.CONN_TRN_SWITCH)
+ trans = ModelStandardTransform.TRANSFORM_SWITCH;
+ if (out_transform == DLSModulator.CONN_TRN_CONCAVE)
+ trans = ModelStandardTransform.TRANSFORM_CONCAVE;
+ if (out_transform == DLSModulator.CONN_TRN_CONVEX)
+ trans = ModelStandardTransform.TRANSFORM_CONVEX;
+ if (ctrl != null) {
+ ((ModelStandardTransform)destination.getTransform())
+ .setTransform(trans);
+ }
+ */
+
+ }
+
+ block.setScale(f_scale);
+
+ return block;
+ }
+
+ return null;
+ }
+
+ public ModelPerformer[] getPerformers() {
+ List<ModelPerformer> performers = new ArrayList<ModelPerformer>();
+
+ Map<String, DLSModulator> modmap = new HashMap<String, DLSModulator>();
+ for (DLSModulator mod: getModulators()) {
+ modmap.put(mod.getSource() + "x" + mod.getControl() + "=" +
+ mod.getDestination(), mod);
+ }
+
+ Map<String, DLSModulator> insmodmap =
+ new HashMap<String, DLSModulator>();
+
+ for (DLSRegion zone: regions) {
+ ModelPerformer performer = new ModelPerformer();
+ performer.setName(zone.getSample().getName());
+ performer.setSelfNonExclusive((zone.getFusoptions() &
+ DLSRegion.OPTION_SELFNONEXCLUSIVE) != 0);
+ performer.setExclusiveClass(zone.getExclusiveClass());
+ performer.setKeyFrom(zone.getKeyfrom());
+ performer.setKeyTo(zone.getKeyto());
+ performer.setVelFrom(zone.getVelfrom());
+ performer.setVelTo(zone.getVelto());
+
+ insmodmap.clear();
+ insmodmap.putAll(modmap);
+ for (DLSModulator mod: zone.getModulators()) {
+ insmodmap.put(mod.getSource() + "x" + mod.getControl() + "=" +
+ mod.getDestination(), mod);
+ }
+
+ List<ModelConnectionBlock> blocks = performer.getConnectionBlocks();
+ for (DLSModulator mod: insmodmap.values()) {
+ ModelConnectionBlock p = convertToModel(mod);
+ if (p != null)
+ blocks.add(p);
+ }
+
+
+ DLSSample sample = zone.getSample();
+ DLSSampleOptions sampleopt = zone.getSampleoptions();
+ if (sampleopt == null)
+ sampleopt = sample.getSampleoptions();
+
+ ModelByteBuffer buff = sample.getDataBuffer();
+
+ float pitchcorrection = (-sampleopt.unitynote * 100) +
+ sampleopt.finetune;
+
+ ModelByteBufferWavetable osc = new ModelByteBufferWavetable(buff,
+ sample.getFormat(), pitchcorrection);
+ osc.setAttenuation(osc.getAttenuation() / 65536f);
+ if (sampleopt.getLoops().size() != 0) {
+ DLSSampleLoop loop = sampleopt.getLoops().get(0);
+ osc.setLoopStart((int)loop.getStart());
+ osc.setLoopLength((int)loop.getLength());
+ if (loop.getType() == DLSSampleLoop.LOOP_TYPE_FORWARD)
+ osc.setLoopType(ModelWavetable.LOOP_TYPE_FORWARD);
+ if (loop.getType() == DLSSampleLoop.LOOP_TYPE_RELEASE)
+ osc.setLoopType(ModelWavetable.LOOP_TYPE_RELEASE);
+ else
+ osc.setLoopType(ModelWavetable.LOOP_TYPE_FORWARD);
+ }
+
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(SoftFilter.FILTERTYPE_LP12,
+ new ModelDestination(
+ new ModelIdentifier("filter", "type", 1))));
+
+ performer.getOscillators().add(osc);
+
+ performers.add(performer);
+
+ }
+
+ return performers.toArray(new ModelPerformer[performers.size()]);
+ }
+
+ public byte[] getGuid() {
+ return guid;
+ }
+
+ public void setGuid(byte[] guid) {
+ this.guid = guid;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSModulator.java b/src/share/classes/com/sun/media/sound/DLSModulator.java
new file mode 100644
index 000000000..082454ea9
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSModulator.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This class is used to store modulator/artiuclation data.
+ * A modulator connects one synthesizer source to
+ * a destination. For example a note on velocity
+ * can be mapped to the gain of the synthesized voice.
+ * It is stored as a "art1" or "art2" chunk inside DLS files.
+ *
+ * @author Karl Helgason
+ */
+public class DLSModulator {
+
+ // DLS1 Destinations
+ public static final int CONN_DST_NONE = 0x000; // 0
+ public static final int CONN_DST_GAIN = 0x001; // cB
+ public static final int CONN_DST_PITCH = 0x003; // cent
+ public static final int CONN_DST_PAN = 0x004; // 0.1%
+ public static final int CONN_DST_LFO_FREQUENCY = 0x104; // cent (default 5 Hz)
+ public static final int CONN_DST_LFO_STARTDELAY = 0x105; // timecent
+ public static final int CONN_DST_EG1_ATTACKTIME = 0x206; // timecent
+ public static final int CONN_DST_EG1_DECAYTIME = 0x207; // timecent
+ public static final int CONN_DST_EG1_RELEASETIME = 0x209; // timecent
+ public static final int CONN_DST_EG1_SUSTAINLEVEL = 0x20A; // 0.1%
+ public static final int CONN_DST_EG2_ATTACKTIME = 0x30A; // timecent
+ public static final int CONN_DST_EG2_DECAYTIME = 0x30B; // timecent
+ public static final int CONN_DST_EG2_RELEASETIME = 0x30D; // timecent
+ public static final int CONN_DST_EG2_SUSTAINLEVEL = 0x30E; // 0.1%
+ // DLS2 Destinations
+ public static final int CONN_DST_KEYNUMBER = 0x005;
+ public static final int CONN_DST_LEFT = 0x010; // 0.1%
+ public static final int CONN_DST_RIGHT = 0x011; // 0.1%
+ public static final int CONN_DST_CENTER = 0x012; // 0.1%
+ public static final int CONN_DST_LEFTREAR = 0x013; // 0.1%
+ public static final int CONN_DST_RIGHTREAR = 0x014; // 0.1%
+ public static final int CONN_DST_LFE_CHANNEL = 0x015; // 0.1%
+ public static final int CONN_DST_CHORUS = 0x080; // 0.1%
+ public static final int CONN_DST_REVERB = 0x081; // 0.1%
+ public static final int CONN_DST_VIB_FREQUENCY = 0x114; // cent
+ public static final int CONN_DST_VIB_STARTDELAY = 0x115; // dB
+ public static final int CONN_DST_EG1_DELAYTIME = 0x20B; // timecent
+ public static final int CONN_DST_EG1_HOLDTIME = 0x20C; // timecent
+ public static final int CONN_DST_EG1_SHUTDOWNTIME = 0x20D; // timecent
+ public static final int CONN_DST_EG2_DELAYTIME = 0x30F; // timecent
+ public static final int CONN_DST_EG2_HOLDTIME = 0x310; // timecent
+ public static final int CONN_DST_FILTER_CUTOFF = 0x500; // cent
+ public static final int CONN_DST_FILTER_Q = 0x501; // dB
+
+ // DLS1 Sources
+ public static final int CONN_SRC_NONE = 0x000; // 1
+ public static final int CONN_SRC_LFO = 0x001; // linear (sine wave)
+ public static final int CONN_SRC_KEYONVELOCITY = 0x002; // ??db or velocity??
+ public static final int CONN_SRC_KEYNUMBER = 0x003; // ??cent or keynumber??
+ public static final int CONN_SRC_EG1 = 0x004; // linear direct from eg
+ public static final int CONN_SRC_EG2 = 0x005; // linear direct from eg
+ public static final int CONN_SRC_PITCHWHEEL = 0x006; // linear -1..1
+ public static final int CONN_SRC_CC1 = 0x081; // linear 0..1
+ public static final int CONN_SRC_CC7 = 0x087; // linear 0..1
+ public static final int CONN_SRC_CC10 = 0x08A; // linear 0..1
+ public static final int CONN_SRC_CC11 = 0x08B; // linear 0..1
+ public static final int CONN_SRC_RPN0 = 0x100; // ?? // Pitch Bend Range
+ public static final int CONN_SRC_RPN1 = 0x101; // ?? // Fine Tune
+ public static final int CONN_SRC_RPN2 = 0x102; // ?? // Course Tune
+ // DLS2 Sources
+ public static final int CONN_SRC_POLYPRESSURE = 0x007; // linear 0..1
+ public static final int CONN_SRC_CHANNELPRESSURE = 0x008; // linear 0..1
+ public static final int CONN_SRC_VIBRATO = 0x009; // linear 0..1
+ public static final int CONN_SRC_MONOPRESSURE = 0x00A; // linear 0..1
+ public static final int CONN_SRC_CC91 = 0x0DB; // linear 0..1
+ public static final int CONN_SRC_CC93 = 0x0DD; // linear 0..1
+ // DLS1 Transforms
+ public static final int CONN_TRN_NONE = 0x000;
+ public static final int CONN_TRN_CONCAVE = 0x001;
+ // DLS2 Transforms
+ public static final int CONN_TRN_CONVEX = 0x002;
+ public static final int CONN_TRN_SWITCH = 0x003;
+ public static final int DST_FORMAT_CB = 1;
+ public static final int DST_FORMAT_CENT = 1;
+ public static final int DST_FORMAT_TIMECENT = 2;
+ public static final int DST_FORMAT_PERCENT = 3;
+ protected int source;
+ protected int control;
+ protected int destination;
+ protected int transform;
+ protected int scale;
+ protected int version = 1;
+
+ public int getControl() {
+ return control;
+ }
+
+ public void setControl(int control) {
+ this.control = control;
+ }
+
+ public static int getDestinationFormat(int destination) {
+
+ if (destination == CONN_DST_GAIN)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_PITCH)
+ return DST_FORMAT_CENT;
+ if (destination == CONN_DST_PAN)
+ return DST_FORMAT_PERCENT;
+
+ if (destination == CONN_DST_LFO_FREQUENCY)
+ return DST_FORMAT_CENT;
+ if (destination == CONN_DST_LFO_STARTDELAY)
+ return DST_FORMAT_TIMECENT;
+
+ if (destination == CONN_DST_EG1_ATTACKTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG1_DECAYTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG1_RELEASETIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG1_SUSTAINLEVEL)
+ return DST_FORMAT_PERCENT;
+
+ if (destination == CONN_DST_EG2_ATTACKTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG2_DECAYTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG2_RELEASETIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG2_SUSTAINLEVEL)
+ return DST_FORMAT_PERCENT;
+
+ if (destination == CONN_DST_KEYNUMBER)
+ return DST_FORMAT_CENT; // NOT SURE WITHOUT DLS 2 SPEC
+ if (destination == CONN_DST_LEFT)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_RIGHT)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_CENTER)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_LEFTREAR)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_RIGHTREAR)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_LFE_CHANNEL)
+ return DST_FORMAT_CB;
+ if (destination == CONN_DST_CHORUS)
+ return DST_FORMAT_PERCENT;
+ if (destination == CONN_DST_REVERB)
+ return DST_FORMAT_PERCENT;
+
+ if (destination == CONN_DST_VIB_FREQUENCY)
+ return DST_FORMAT_CENT;
+ if (destination == CONN_DST_VIB_STARTDELAY)
+ return DST_FORMAT_TIMECENT;
+
+ if (destination == CONN_DST_EG1_DELAYTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG1_HOLDTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG1_SHUTDOWNTIME)
+ return DST_FORMAT_TIMECENT;
+
+ if (destination == CONN_DST_EG2_DELAYTIME)
+ return DST_FORMAT_TIMECENT;
+ if (destination == CONN_DST_EG2_HOLDTIME)
+ return DST_FORMAT_TIMECENT;
+
+ if (destination == CONN_DST_FILTER_CUTOFF)
+ return DST_FORMAT_CENT;
+ if (destination == CONN_DST_FILTER_Q)
+ return DST_FORMAT_CB;
+
+ return -1;
+ }
+
+ public static String getDestinationName(int destination) {
+
+ if (destination == CONN_DST_GAIN)
+ return "gain";
+ if (destination == CONN_DST_PITCH)
+ return "pitch";
+ if (destination == CONN_DST_PAN)
+ return "pan";
+
+ if (destination == CONN_DST_LFO_FREQUENCY)
+ return "lfo1.freq";
+ if (destination == CONN_DST_LFO_STARTDELAY)
+ return "lfo1.delay";
+
+ if (destination == CONN_DST_EG1_ATTACKTIME)
+ return "eg1.attack";
+ if (destination == CONN_DST_EG1_DECAYTIME)
+ return "eg1.decay";
+ if (destination == CONN_DST_EG1_RELEASETIME)
+ return "eg1.release";
+ if (destination == CONN_DST_EG1_SUSTAINLEVEL)
+ return "eg1.sustain";
+
+ if (destination == CONN_DST_EG2_ATTACKTIME)
+ return "eg2.attack";
+ if (destination == CONN_DST_EG2_DECAYTIME)
+ return "eg2.decay";
+ if (destination == CONN_DST_EG2_RELEASETIME)
+ return "eg2.release";
+ if (destination == CONN_DST_EG2_SUSTAINLEVEL)
+ return "eg2.sustain";
+
+ if (destination == CONN_DST_KEYNUMBER)
+ return "keynumber";
+ if (destination == CONN_DST_LEFT)
+ return "left";
+ if (destination == CONN_DST_RIGHT)
+ return "right";
+ if (destination == CONN_DST_CENTER)
+ return "center";
+ if (destination == CONN_DST_LEFTREAR)
+ return "leftrear";
+ if (destination == CONN_DST_RIGHTREAR)
+ return "rightrear";
+ if (destination == CONN_DST_LFE_CHANNEL)
+ return "lfe_channel";
+ if (destination == CONN_DST_CHORUS)
+ return "chorus";
+ if (destination == CONN_DST_REVERB)
+ return "reverb";
+
+ if (destination == CONN_DST_VIB_FREQUENCY)
+ return "vib.freq";
+ if (destination == CONN_DST_VIB_STARTDELAY)
+ return "vib.delay";
+
+ if (destination == CONN_DST_EG1_DELAYTIME)
+ return "eg1.delay";
+ if (destination == CONN_DST_EG1_HOLDTIME)
+ return "eg1.hold";
+ if (destination == CONN_DST_EG1_SHUTDOWNTIME)
+ return "eg1.shutdown";
+
+ if (destination == CONN_DST_EG2_DELAYTIME)
+ return "eg2.delay";
+ if (destination == CONN_DST_EG2_HOLDTIME)
+ return "eg.2hold";
+
+ if (destination == CONN_DST_FILTER_CUTOFF)
+ return "filter.cutoff"; // NOT SURE WITHOUT DLS 2 SPEC
+ if (destination == CONN_DST_FILTER_Q)
+ return "filter.q"; // NOT SURE WITHOUT DLS 2 SPEC
+
+ return null;
+ }
+
+ public static String getSourceName(int source) {
+
+ if (source == CONN_SRC_NONE)
+ return "none";
+ if (source == CONN_SRC_LFO)
+ return "lfo";
+ if (source == CONN_SRC_KEYONVELOCITY)
+ return "keyonvelocity";
+ if (source == CONN_SRC_KEYNUMBER)
+ return "keynumber";
+ if (source == CONN_SRC_EG1)
+ return "eg1";
+ if (source == CONN_SRC_EG2)
+ return "eg2";
+ if (source == CONN_SRC_PITCHWHEEL)
+ return "pitchweel";
+ if (source == CONN_SRC_CC1)
+ return "cc1";
+ if (source == CONN_SRC_CC7)
+ return "cc7";
+ if (source == CONN_SRC_CC10)
+ return "c10";
+ if (source == CONN_SRC_CC11)
+ return "cc11";
+
+ if (source == CONN_SRC_POLYPRESSURE)
+ return "polypressure";
+ if (source == CONN_SRC_CHANNELPRESSURE)
+ return "channelpressure";
+ if (source == CONN_SRC_VIBRATO)
+ return "vibrato";
+ if (source == CONN_SRC_MONOPRESSURE)
+ return "monopressure";
+ if (source == CONN_SRC_CC91)
+ return "cc91";
+ if (source == CONN_SRC_CC93)
+ return "cc93";
+ return null;
+ }
+
+ public int getDestination() {
+ return destination;
+ }
+
+ public void setDestination(int destination) {
+ this.destination = destination;
+ }
+
+ public int getScale() {
+ return scale;
+ }
+
+ public void setScale(int scale) {
+ this.scale = scale;
+ }
+
+ public int getSource() {
+ return source;
+ }
+
+ public void setSource(int source) {
+ this.source = source;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public int getTransform() {
+ return transform;
+ }
+
+ public void setTransform(int transform) {
+ this.transform = transform;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSRegion.java b/src/share/classes/com/sun/media/sound/DLSRegion.java
new file mode 100644
index 000000000..17e57a3b3
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSRegion.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to store region parts for instrument.
+ * A region has a velocity and key range which it response to.
+ * And it has a list of modulators/articulators which
+ * is used how to synthesize a single voice.
+ * It is stored inside a "rgn " List Chunk inside DLS files.
+ *
+ * @author Karl Helgason
+ */
+public class DLSRegion {
+
+ public final static int OPTION_SELFNONEXCLUSIVE = 0x0001;
+ protected List<DLSModulator> modulators = new ArrayList<DLSModulator>();
+ protected int keyfrom;
+ protected int keyto;
+ protected int velfrom;
+ protected int velto;
+ protected int options;
+ protected int exclusiveClass;
+ protected int fusoptions;
+ protected int phasegroup;
+ protected long channel;
+ protected DLSSample sample = null;
+ protected DLSSampleOptions sampleoptions;
+
+ public List<DLSModulator> getModulators() {
+ return modulators;
+ }
+
+ public long getChannel() {
+ return channel;
+ }
+
+ public void setChannel(long channel) {
+ this.channel = channel;
+ }
+
+ public int getExclusiveClass() {
+ return exclusiveClass;
+ }
+
+ public void setExclusiveClass(int exclusiveClass) {
+ this.exclusiveClass = exclusiveClass;
+ }
+
+ public int getFusoptions() {
+ return fusoptions;
+ }
+
+ public void setFusoptions(int fusoptions) {
+ this.fusoptions = fusoptions;
+ }
+
+ public int getKeyfrom() {
+ return keyfrom;
+ }
+
+ public void setKeyfrom(int keyfrom) {
+ this.keyfrom = keyfrom;
+ }
+
+ public int getKeyto() {
+ return keyto;
+ }
+
+ public void setKeyto(int keyto) {
+ this.keyto = keyto;
+ }
+
+ public int getOptions() {
+ return options;
+ }
+
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ public int getPhasegroup() {
+ return phasegroup;
+ }
+
+ public void setPhasegroup(int phasegroup) {
+ this.phasegroup = phasegroup;
+ }
+
+ public DLSSample getSample() {
+ return sample;
+ }
+
+ public void setSample(DLSSample sample) {
+ this.sample = sample;
+ }
+
+ public int getVelfrom() {
+ return velfrom;
+ }
+
+ public void setVelfrom(int velfrom) {
+ this.velfrom = velfrom;
+ }
+
+ public int getVelto() {
+ return velto;
+ }
+
+ public void setVelto(int velto) {
+ this.velto = velto;
+ }
+
+ public void setModulators(List<DLSModulator> modulators) {
+ this.modulators = modulators;
+ }
+
+ public DLSSampleOptions getSampleoptions() {
+ return sampleoptions;
+ }
+
+ public void setSampleoptions(DLSSampleOptions sampleOptions) {
+ this.sampleoptions = sampleOptions;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSSample.java b/src/share/classes/com/sun/media/sound/DLSSample.java
new file mode 100644
index 000000000..b75b2a9a9
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSSample.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.InputStream;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.SoundbankResource;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+
+/**
+ * This class is used to store the sample data itself.
+ * A sample is encoded as PCM audio stream
+ * and in DLS Level 1 files it is always a mono 8/16 bit stream.
+ * They are stored just like RIFF WAVE files are stored.
+ * It is stored inside a "wave" List Chunk inside DLS files.
+ *
+ * @author Karl Helgason
+ */
+public class DLSSample extends SoundbankResource {
+
+ protected byte[] guid = null;
+ protected DLSInfo info = new DLSInfo();
+ protected DLSSampleOptions sampleoptions;
+ protected ModelByteBuffer data;
+ protected AudioFormat format;
+
+ public DLSSample(Soundbank soundBank) {
+ super(soundBank, null, AudioInputStream.class);
+ }
+
+ public DLSSample() {
+ super(null, null, AudioInputStream.class);
+ }
+
+ public DLSInfo getInfo() {
+ return info;
+ }
+
+ public Object getData() {
+ AudioFormat format = getFormat();
+
+ InputStream is = data.getInputStream();
+ if (is == null)
+ return null;
+ return new AudioInputStream(is, format, data.capacity());
+ }
+
+ public ModelByteBuffer getDataBuffer() {
+ return data;
+ }
+
+ public AudioFormat getFormat() {
+ return format;
+ }
+
+ public void setFormat(AudioFormat format) {
+ this.format = format;
+ }
+
+ public void setData(ModelByteBuffer data) {
+ this.data = data;
+ }
+
+ public void setData(byte[] data) {
+ this.data = new ModelByteBuffer(data);
+ }
+
+ public void setData(byte[] data, int offset, int length) {
+ this.data = new ModelByteBuffer(data, offset, length);
+ }
+
+ public String getName() {
+ return info.name;
+ }
+
+ public void setName(String name) {
+ info.name = name;
+ }
+
+ public DLSSampleOptions getSampleoptions() {
+ return sampleoptions;
+ }
+
+ public void setSampleoptions(DLSSampleOptions sampleOptions) {
+ this.sampleoptions = sampleOptions;
+ }
+
+ public String toString() {
+ return "Sample: " + info.name;
+ }
+
+ public byte[] getGuid() {
+ return guid;
+ }
+
+ public void setGuid(byte[] guid) {
+ this.guid = guid;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSSampleLoop.java b/src/share/classes/com/sun/media/sound/DLSSampleLoop.java
new file mode 100644
index 000000000..ee78d64fc
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSSampleLoop.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This class is used to store loop points inside DLSSampleOptions class.
+ *
+ * @author Karl Helgason
+ */
+public class DLSSampleLoop {
+
+ public final static int LOOP_TYPE_FORWARD = 0;
+ public final static int LOOP_TYPE_RELEASE = 1;
+ protected long type;
+ protected long start;
+ protected long length;
+
+ public long getLength() {
+ return length;
+ }
+
+ public void setLength(long length) {
+ this.length = length;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ public long getType() {
+ return type;
+ }
+
+ public void setType(long type) {
+ this.type = type;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSSampleOptions.java b/src/share/classes/com/sun/media/sound/DLSSampleOptions.java
new file mode 100644
index 000000000..849644b6c
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSSampleOptions.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class stores options how to playback sampled data like pitch/tuning,
+ * attenuation and loops.
+ * It is stored as a "wsmp" chunk inside DLS files.
+ *
+ * @author Karl Helgason
+ */
+public class DLSSampleOptions {
+
+ protected int unitynote;
+ protected short finetune;
+ protected int attenuation;
+ protected long options;
+ protected List<DLSSampleLoop> loops = new ArrayList<DLSSampleLoop>();
+
+ public int getAttenuation() {
+ return attenuation;
+ }
+
+ public void setAttenuation(int attenuation) {
+ this.attenuation = attenuation;
+ }
+
+ public short getFinetune() {
+ return finetune;
+ }
+
+ public void setFinetune(short finetune) {
+ this.finetune = finetune;
+ }
+
+ public List<DLSSampleLoop> getLoops() {
+ return loops;
+ }
+
+ public long getOptions() {
+ return options;
+ }
+
+ public void setOptions(long options) {
+ this.options = options;
+ }
+
+ public int getUnitynote() {
+ return unitynote;
+ }
+
+ public void setUnitynote(int unitynote) {
+ this.unitynote = unitynote;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSSoundbank.java b/src/share/classes/com/sun/media/sound/DLSSoundbank.java
new file mode 100644
index 000000000..027fa75b4
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSSoundbank.java
@@ -0,0 +1,1287 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.sound.midi.Instrument;
+import javax.sound.midi.Patch;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.SoundbankResource;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.AudioFormat.Encoding;
+
+/**
+ * A DLS Level 1 and Level 2 soundbank reader (from files/url/streams).
+ *
+ * @author Karl Helgason
+ */
+public class DLSSoundbank implements Soundbank {
+
+ static private class DLSID {
+ long i1;
+ int s1;
+ int s2;
+ int x1;
+ int x2;
+ int x3;
+ int x4;
+ int x5;
+ int x6;
+ int x7;
+ int x8;
+
+ private DLSID() {
+ }
+
+ public DLSID(long i1, int s1, int s2, int x1, int x2, int x3, int x4,
+ int x5, int x6, int x7, int x8) {
+ this.i1 = i1;
+ this.s1 = s1;
+ this.s2 = s2;
+ this.x1 = x1;
+ this.x2 = x2;
+ this.x3 = x3;
+ this.x4 = x4;
+ this.x5 = x5;
+ this.x6 = x6;
+ this.x7 = x7;
+ this.x8 = x8;
+ }
+
+ public static DLSID read(RIFFReader riff) throws IOException {
+ DLSID d = new DLSID();
+ d.i1 = riff.readUnsignedInt();
+ d.s1 = riff.readUnsignedShort();
+ d.s2 = riff.readUnsignedShort();
+ d.x1 = riff.readUnsignedByte();
+ d.x2 = riff.readUnsignedByte();
+ d.x3 = riff.readUnsignedByte();
+ d.x4 = riff.readUnsignedByte();
+ d.x5 = riff.readUnsignedByte();
+ d.x6 = riff.readUnsignedByte();
+ d.x7 = riff.readUnsignedByte();
+ d.x8 = riff.readUnsignedByte();
+ return d;
+ }
+
+ public int hashCode() {
+ return (int)i1;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DLSID)) {
+ return false;
+ }
+ DLSID t = (DLSID) obj;
+ return i1 == t.i1 && s1 == t.s1 && s2 == t.s2
+ && x1 == t.x1 && x2 == t.x2 && x3 == t.x3 && x4 == t.x4
+ && x5 == t.x5 && x6 == t.x6 && x7 == t.x7 && x8 == t.x8;
+ }
+ }
+
+ /** X = X & Y */
+ private static final int DLS_CDL_AND = 0x0001;
+ /** X = X | Y */
+ private static final int DLS_CDL_OR = 0x0002;
+ /** X = X ^ Y */
+ private static final int DLS_CDL_XOR = 0x0003;
+ /** X = X + Y */
+ private static final int DLS_CDL_ADD = 0x0004;
+ /** X = X - Y */
+ private static final int DLS_CDL_SUBTRACT = 0x0005;
+ /** X = X * Y */
+ private static final int DLS_CDL_MULTIPLY = 0x0006;
+ /** X = X / Y */
+ private static final int DLS_CDL_DIVIDE = 0x0007;
+ /** X = X && Y */
+ private static final int DLS_CDL_LOGICAL_AND = 0x0008;
+ /** X = X || Y */
+ private static final int DLS_CDL_LOGICAL_OR = 0x0009;
+ /** X = (X < Y) */
+ private static final int DLS_CDL_LT = 0x000A;
+ /** X = (X <= Y) */
+ private static final int DLS_CDL_LE = 0x000B;
+ /** X = (X > Y) */
+ private static final int DLS_CDL_GT = 0x000C;
+ /** X = (X >= Y) */
+ private static final int DLS_CDL_GE = 0x000D;
+ /** X = (X == Y) */
+ private static final int DLS_CDL_EQ = 0x000E;
+ /** X = !X */
+ private static final int DLS_CDL_NOT = 0x000F;
+ /** 32-bit constant */
+ private static final int DLS_CDL_CONST = 0x0010;
+ /** 32-bit value returned from query */
+ private static final int DLS_CDL_QUERY = 0x0011;
+ /** 32-bit value returned from query */
+ private static final int DLS_CDL_QUERYSUPPORTED = 0x0012;
+
+ private static final DLSID DLSID_GMInHardware = new DLSID(0x178f2f24,
+ 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
+ private static final DLSID DLSID_GSInHardware = new DLSID(0x178f2f25,
+ 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
+ private static final DLSID DLSID_XGInHardware = new DLSID(0x178f2f26,
+ 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
+ private static final DLSID DLSID_SupportsDLS1 = new DLSID(0x178f2f27,
+ 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
+ private static final DLSID DLSID_SupportsDLS2 = new DLSID(0xf14599e5,
+ 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);
+ private static final DLSID DLSID_SampleMemorySize = new DLSID(0x178f2f28,
+ 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
+ private static final DLSID DLSID_ManufacturersID = new DLSID(0xb03e1181,
+ 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
+ private static final DLSID DLSID_ProductID = new DLSID(0xb03e1182,
+ 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
+ private static final DLSID DLSID_SamplePlaybackRate = new DLSID(0x2a91f713,
+ 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
+
+ private long major = -1;
+ private long minor = -1;
+
+ private DLSInfo info = new DLSInfo();
+
+ private List<DLSInstrument> instruments = new ArrayList<DLSInstrument>();
+ private List<DLSSample> samples = new ArrayList<DLSSample>();
+
+ private boolean largeFormat = false;
+ private File sampleFile;
+
+ public DLSSoundbank() {
+ }
+
+ public DLSSoundbank(URL url) throws IOException {
+ InputStream is = url.openStream();
+ try {
+ readSoundbank(is);
+ } finally {
+ is.close();
+ }
+ }
+
+ public DLSSoundbank(File file) throws IOException {
+ largeFormat = true;
+ sampleFile = file;
+ InputStream is = new FileInputStream(file);
+ try {
+ readSoundbank(is);
+ } finally {
+ is.close();
+ }
+ }
+
+ public DLSSoundbank(InputStream inputstream) throws IOException {
+ readSoundbank(inputstream);
+ }
+
+ private void readSoundbank(InputStream inputstream) throws IOException {
+ RIFFReader riff = new RIFFReader(inputstream);
+ if (!riff.getFormat().equals("RIFF")) {
+ throw new RIFFInvalidFormatException(
+ "Input stream is not a valid RIFF stream!");
+ }
+ if (!riff.getType().equals("DLS ")) {
+ throw new RIFFInvalidFormatException(
+ "Input stream is not a valid DLS soundbank!");
+ }
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ if (chunk.getFormat().equals("LIST")) {
+ if (chunk.getType().equals("INFO"))
+ readInfoChunk(chunk);
+ if (chunk.getType().equals("lins"))
+ readLinsChunk(chunk);
+ if (chunk.getType().equals("wvpl"))
+ readWvplChunk(chunk);
+ } else {
+ if (chunk.getFormat().equals("cdl ")) {
+ if (!readCdlChunk(chunk)) {
+ throw new RIFFInvalidFormatException(
+ "DLS file isn't supported!");
+ }
+ }
+ if (chunk.getFormat().equals("colh")) {
+ // skipped because we will load the entire bank into memory
+ // long instrumentcount = chunk.readUnsignedInt();
+ // System.out.println("instrumentcount = "+ instrumentcount);
+ }
+ if (chunk.getFormat().equals("ptbl")) {
+ // Pool Table Chunk
+ // skipped because we will load the entire bank into memory
+ }
+ if (chunk.getFormat().equals("vers")) {
+ major = chunk.readUnsignedInt();
+ minor = chunk.readUnsignedInt();
+ }
+ }
+ }
+
+ for (Map.Entry<DLSRegion, Long> entry : temp_rgnassign.entrySet()) {
+ entry.getKey().sample = samples.get((int)entry.getValue().longValue());
+ }
+
+ temp_rgnassign = null;
+ }
+
+ private boolean cdlIsQuerySupported(DLSID uuid) {
+ return uuid.equals(DLSID_GMInHardware)
+ || uuid.equals(DLSID_GSInHardware)
+ || uuid.equals(DLSID_XGInHardware)
+ || uuid.equals(DLSID_SupportsDLS1)
+ || uuid.equals(DLSID_SupportsDLS2)
+ || uuid.equals(DLSID_SampleMemorySize)
+ || uuid.equals(DLSID_ManufacturersID)
+ || uuid.equals(DLSID_ProductID)
+ || uuid.equals(DLSID_SamplePlaybackRate);
+ }
+
+ private long cdlQuery(DLSID uuid) {
+ if (uuid.equals(DLSID_GMInHardware))
+ return 1;
+ if (uuid.equals(DLSID_GSInHardware))
+ return 0;
+ if (uuid.equals(DLSID_XGInHardware))
+ return 0;
+ if (uuid.equals(DLSID_SupportsDLS1))
+ return 1;
+ if (uuid.equals(DLSID_SupportsDLS2))
+ return 1;
+ if (uuid.equals(DLSID_SampleMemorySize))
+ return Runtime.getRuntime().totalMemory();
+ if (uuid.equals(DLSID_ManufacturersID))
+ return 0;
+ if (uuid.equals(DLSID_ProductID))
+ return 0;
+ if (uuid.equals(DLSID_SamplePlaybackRate))
+ return 44100;
+ return 0;
+ }
+
+
+ // Reading cdl-ck Chunk
+ // "cdl " chunk can only appear inside : DLS,lart,lar2,rgn,rgn2
+ private boolean readCdlChunk(RIFFReader riff) throws IOException {
+
+ DLSID uuid;
+ long x;
+ long y;
+ Stack<Long> stack = new Stack<Long>();
+
+ while (riff.available() != 0) {
+ int opcode = riff.readUnsignedShort();
+ switch (opcode) {
+ case DLS_CDL_AND:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(((x != 0) && (y != 0)) ? 1 : 0));
+ break;
+ case DLS_CDL_OR:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(((x != 0) || (y != 0)) ? 1 : 0));
+ break;
+ case DLS_CDL_XOR:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(((x != 0) ^ (y != 0)) ? 1 : 0));
+ break;
+ case DLS_CDL_ADD:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(x + y));
+ break;
+ case DLS_CDL_SUBTRACT:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(x - y));
+ break;
+ case DLS_CDL_MULTIPLY:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(x * y));
+ break;
+ case DLS_CDL_DIVIDE:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(x / y));
+ break;
+ case DLS_CDL_LOGICAL_AND:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(((x != 0) && (y != 0)) ? 1 : 0));
+ break;
+ case DLS_CDL_LOGICAL_OR:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf(((x != 0) || (y != 0)) ? 1 : 0));
+ break;
+ case DLS_CDL_LT:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf((x < y) ? 1 : 0));
+ break;
+ case DLS_CDL_LE:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf((x <= y) ? 1 : 0));
+ break;
+ case DLS_CDL_GT:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf((x > y) ? 1 : 0));
+ break;
+ case DLS_CDL_GE:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf((x >= y) ? 1 : 0));
+ break;
+ case DLS_CDL_EQ:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf((x == y) ? 1 : 0));
+ break;
+ case DLS_CDL_NOT:
+ x = stack.pop();
+ y = stack.pop();
+ stack.push(Long.valueOf((x == 0) ? 1 : 0));
+ break;
+ case DLS_CDL_CONST:
+ stack.push(Long.valueOf(riff.readUnsignedInt()));
+ break;
+ case DLS_CDL_QUERY:
+ uuid = DLSID.read(riff);
+ stack.push(cdlQuery(uuid));
+ break;
+ case DLS_CDL_QUERYSUPPORTED:
+ uuid = DLSID.read(riff);
+ stack.push(Long.valueOf(cdlIsQuerySupported(uuid) ? 1 : 0));
+ break;
+ default:
+ break;
+ }
+ }
+ if (stack.isEmpty())
+ return false;
+
+ return stack.pop() == 1;
+ }
+
+ private void readInfoChunk(RIFFReader riff) throws IOException {
+ info.name = null;
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("INAM"))
+ info.name = chunk.readString(chunk.available());
+ else if (format.equals("ICRD"))
+ info.creationDate = chunk.readString(chunk.available());
+ else if (format.equals("IENG"))
+ info.engineers = chunk.readString(chunk.available());
+ else if (format.equals("IPRD"))
+ info.product = chunk.readString(chunk.available());
+ else if (format.equals("ICOP"))
+ info.copyright = chunk.readString(chunk.available());
+ else if (format.equals("ICMT"))
+ info.comments = chunk.readString(chunk.available());
+ else if (format.equals("ISFT"))
+ info.tools = chunk.readString(chunk.available());
+ else if (format.equals("IARL"))
+ info.archival_location = chunk.readString(chunk.available());
+ else if (format.equals("IART"))
+ info.artist = chunk.readString(chunk.available());
+ else if (format.equals("ICMS"))
+ info.commissioned = chunk.readString(chunk.available());
+ else if (format.equals("IGNR"))
+ info.genre = chunk.readString(chunk.available());
+ else if (format.equals("IKEY"))
+ info.keywords = chunk.readString(chunk.available());
+ else if (format.equals("IMED"))
+ info.medium = chunk.readString(chunk.available());
+ else if (format.equals("ISBJ"))
+ info.subject = chunk.readString(chunk.available());
+ else if (format.equals("ISRC"))
+ info.source = chunk.readString(chunk.available());
+ else if (format.equals("ISRF"))
+ info.source_form = chunk.readString(chunk.available());
+ else if (format.equals("ITCH"))
+ info.technician = chunk.readString(chunk.available());
+ }
+ }
+
+ private void readLinsChunk(RIFFReader riff) throws IOException {
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ if (chunk.getFormat().equals("LIST")) {
+ if (chunk.getType().equals("ins "))
+ readInsChunk(chunk);
+ }
+ }
+ }
+
+ private void readInsChunk(RIFFReader riff) throws IOException {
+ DLSInstrument instrument = new DLSInstrument(this);
+
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("LIST")) {
+ if (chunk.getType().equals("INFO")) {
+ readInsInfoChunk(instrument, chunk);
+ }
+ if (chunk.getType().equals("lrgn")) {
+ while (chunk.hasNextChunk()) {
+ RIFFReader subchunk = chunk.nextChunk();
+ if (subchunk.getFormat().equals("LIST")) {
+ if (subchunk.getType().equals("rgn ")) {
+ DLSRegion split = new DLSRegion();
+ if (readRgnChunk(split, subchunk))
+ instrument.getRegions().add(split);
+ }
+ if (subchunk.getType().equals("rgn2")) {
+ // support for DLS level 2 regions
+ DLSRegion split = new DLSRegion();
+ if (readRgnChunk(split, subchunk))
+ instrument.getRegions().add(split);
+ }
+ }
+ }
+ }
+ if (chunk.getType().equals("lart")) {
+ List<DLSModulator> modlist = new ArrayList<DLSModulator>();
+ while (chunk.hasNextChunk()) {
+ RIFFReader subchunk = chunk.nextChunk();
+ if (chunk.getFormat().equals("cdl ")) {
+ if (!readCdlChunk(chunk)) {
+ modlist.clear();
+ break;
+ }
+ }
+ if (subchunk.getFormat().equals("art1"))
+ readArt1Chunk(modlist, subchunk);
+ }
+ instrument.getModulators().addAll(modlist);
+ }
+ if (chunk.getType().equals("lar2")) {
+ // support for DLS level 2 ART
+ List<DLSModulator> modlist = new ArrayList<DLSModulator>();
+ while (chunk.hasNextChunk()) {
+ RIFFReader subchunk = chunk.nextChunk();
+ if (chunk.getFormat().equals("cdl ")) {
+ if (!readCdlChunk(chunk)) {
+ modlist.clear();
+ break;
+ }
+ }
+ if (subchunk.getFormat().equals("art2"))
+ readArt2Chunk(modlist, subchunk);
+ }
+ instrument.getModulators().addAll(modlist);
+ }
+ } else {
+ if (format.equals("dlid")) {
+ instrument.guid = new byte[16];
+ chunk.readFully(instrument.guid);
+ }
+ if (format.equals("insh")) {
+ chunk.readUnsignedInt(); // Read Region Count - ignored
+
+ int bank = chunk.read(); // LSB
+ bank += (chunk.read() & 127) << 7; // MSB
+ chunk.read(); // Read Reserved byte
+ int drumins = chunk.read(); // Drum Instrument
+
+ int id = chunk.read() & 127; // Read only first 7 bits
+ chunk.read(); // Read Reserved byte
+ chunk.read(); // Read Reserved byte
+ chunk.read(); // Read Reserved byte
+
+ instrument.bank = bank;
+ instrument.preset = (int) id;
+ instrument.druminstrument = (drumins & 128) > 0;
+ //System.out.println("bank="+bank+" drumkit="+drumkit
+ // +" id="+id);
+ }
+
+ }
+ }
+ instruments.add(instrument);
+ }
+
+ private void readArt1Chunk(List<DLSModulator> modulators, RIFFReader riff)
+ throws IOException {
+ long size = riff.readUnsignedInt();
+ long count = riff.readUnsignedInt();
+
+ if (size - 8 != 0)
+ riff.skipBytes(size - 8);
+
+ for (int i = 0; i < count; i++) {
+ DLSModulator modulator = new DLSModulator();
+ modulator.version = 1;
+ modulator.source = riff.readUnsignedShort();
+ modulator.control = riff.readUnsignedShort();
+ modulator.destination = riff.readUnsignedShort();
+ modulator.transform = riff.readUnsignedShort();
+ modulator.scale = riff.readInt();
+ modulators.add(modulator);
+ }
+ }
+
+ private void readArt2Chunk(List<DLSModulator> modulators, RIFFReader riff)
+ throws IOException {
+ long size = riff.readUnsignedInt();
+ long count = riff.readUnsignedInt();
+
+ if (size - 8 != 0)
+ riff.skipBytes(size - 8);
+
+ for (int i = 0; i < count; i++) {
+ DLSModulator modulator = new DLSModulator();
+ modulator.version = 2;
+ modulator.source = riff.readUnsignedShort();
+ modulator.control = riff.readUnsignedShort();
+ modulator.destination = riff.readUnsignedShort();
+ modulator.transform = riff.readUnsignedShort();
+ modulator.scale = riff.readInt();
+ modulators.add(modulator);
+ }
+ }
+
+ private Map<DLSRegion, Long> temp_rgnassign = new HashMap<DLSRegion, Long>();
+
+ private boolean readRgnChunk(DLSRegion split, RIFFReader riff)
+ throws IOException {
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("LIST")) {
+ if (chunk.getType().equals("lart")) {
+ List<DLSModulator> modlist = new ArrayList<DLSModulator>();
+ while (chunk.hasNextChunk()) {
+ RIFFReader subchunk = chunk.nextChunk();
+ if (chunk.getFormat().equals("cdl ")) {
+ if (!readCdlChunk(chunk)) {
+ modlist.clear();
+ break;
+ }
+ }
+ if (subchunk.getFormat().equals("art1"))
+ readArt1Chunk(modlist, subchunk);
+ }
+ split.getModulators().addAll(modlist);
+ }
+ if (chunk.getType().equals("lar2")) {
+ // support for DLS level 2 ART
+ List<DLSModulator> modlist = new ArrayList<DLSModulator>();
+ while (chunk.hasNextChunk()) {
+ RIFFReader subchunk = chunk.nextChunk();
+ if (chunk.getFormat().equals("cdl ")) {
+ if (!readCdlChunk(chunk)) {
+ modlist.clear();
+ break;
+ }
+ }
+ if (subchunk.getFormat().equals("art2"))
+ readArt2Chunk(modlist, subchunk);
+ }
+ split.getModulators().addAll(modlist);
+ }
+ } else {
+
+ if (format.equals("cdl ")) {
+ if (!readCdlChunk(chunk))
+ return false;
+ }
+ if (format.equals("rgnh")) {
+ split.keyfrom = chunk.readUnsignedShort();
+ split.keyto = chunk.readUnsignedShort();
+ split.velfrom = chunk.readUnsignedShort();
+ split.velto = chunk.readUnsignedShort();
+ split.options = chunk.readUnsignedShort();
+ split.exclusiveClass = chunk.readUnsignedShort();
+ }
+ if (format.equals("wlnk")) {
+ split.fusoptions = chunk.readUnsignedShort();
+ split.phasegroup = chunk.readUnsignedShort();
+ split.channel = chunk.readUnsignedInt();
+ long sampleid = chunk.readUnsignedInt();
+ temp_rgnassign.put(split, sampleid);
+ }
+ if (format.equals("wsmp")) {
+ split.sampleoptions = new DLSSampleOptions();
+ readWsmpChunk(split.sampleoptions, chunk);
+ }
+ }
+ }
+ return true;
+ }
+
+ private void readWsmpChunk(DLSSampleOptions sampleOptions, RIFFReader riff)
+ throws IOException {
+ long size = riff.readUnsignedInt();
+ sampleOptions.unitynote = riff.readUnsignedShort();
+ sampleOptions.finetune = riff.readShort();
+ sampleOptions.attenuation = riff.readInt();
+ sampleOptions.options = riff.readUnsignedInt();
+ long loops = riff.readInt();
+
+ if (size > 20)
+ riff.skipBytes(size - 20);
+
+ for (int i = 0; i < loops; i++) {
+ DLSSampleLoop loop = new DLSSampleLoop();
+ long size2 = riff.readUnsignedInt();
+ loop.type = riff.readUnsignedInt();
+ loop.start = riff.readUnsignedInt();
+ loop.length = riff.readUnsignedInt();
+ sampleOptions.loops.add(loop);
+ if (size2 > 16)
+ riff.skipBytes(size2 - 16);
+ }
+ }
+
+ private void readInsInfoChunk(DLSInstrument dlsinstrument, RIFFReader riff)
+ throws IOException {
+ dlsinstrument.info.name = null;
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("INAM")) {
+ dlsinstrument.info.name = chunk.readString(chunk.available());
+ } else if (format.equals("ICRD")) {
+ dlsinstrument.info.creationDate =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IENG")) {
+ dlsinstrument.info.engineers =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IPRD")) {
+ dlsinstrument.info.product = chunk.readString(chunk.available());
+ } else if (format.equals("ICOP")) {
+ dlsinstrument.info.copyright =
+ chunk.readString(chunk.available());
+ } else if (format.equals("ICMT")) {
+ dlsinstrument.info.comments =
+ chunk.readString(chunk.available());
+ } else if (format.equals("ISFT")) {
+ dlsinstrument.info.tools = chunk.readString(chunk.available());
+ } else if (format.equals("IARL")) {
+ dlsinstrument.info.archival_location =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IART")) {
+ dlsinstrument.info.artist = chunk.readString(chunk.available());
+ } else if (format.equals("ICMS")) {
+ dlsinstrument.info.commissioned =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IGNR")) {
+ dlsinstrument.info.genre = chunk.readString(chunk.available());
+ } else if (format.equals("IKEY")) {
+ dlsinstrument.info.keywords =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IMED")) {
+ dlsinstrument.info.medium = chunk.readString(chunk.available());
+ } else if (format.equals("ISBJ")) {
+ dlsinstrument.info.subject = chunk.readString(chunk.available());
+ } else if (format.equals("ISRC")) {
+ dlsinstrument.info.source = chunk.readString(chunk.available());
+ } else if (format.equals("ISRF")) {
+ dlsinstrument.info.source_form =
+ chunk.readString(chunk.available());
+ } else if (format.equals("ITCH")) {
+ dlsinstrument.info.technician =
+ chunk.readString(chunk.available());
+ }
+ }
+ }
+
+ private void readWvplChunk(RIFFReader riff) throws IOException {
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ if (chunk.getFormat().equals("LIST")) {
+ if (chunk.getType().equals("wave"))
+ readWaveChunk(chunk);
+ }
+ }
+ }
+
+ private void readWaveChunk(RIFFReader riff) throws IOException {
+ DLSSample sample = new DLSSample(this);
+
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("LIST")) {
+ if (chunk.getType().equals("INFO")) {
+ readWaveInfoChunk(sample, chunk);
+ }
+ } else {
+ if (format.equals("dlid")) {
+ sample.guid = new byte[16];
+ chunk.readFully(sample.guid);
+ }
+
+ if (format.equals("fmt ")) {
+ int sampleformat = chunk.readUnsignedShort();
+ if (sampleformat != 1 && sampleformat != 3) {
+ throw new RIFFInvalidDataException(
+ "Only PCM samples are supported!");
+ }
+ int channels = chunk.readUnsignedShort();
+ long samplerate = chunk.readUnsignedInt();
+ // bytes per sec
+ /* long framerate = */ chunk.readUnsignedInt();
+ // block align, framesize
+ int framesize = chunk.readUnsignedShort();
+ int bits = chunk.readUnsignedShort();
+ AudioFormat audioformat = null;
+ if (sampleformat == 1) {
+ if (bits == 8) {
+ audioformat = new AudioFormat(
+ Encoding.PCM_UNSIGNED, samplerate, bits,
+ channels, framesize, samplerate, false);
+ } else {
+ audioformat = new AudioFormat(
+ Encoding.PCM_SIGNED, samplerate, bits,
+ channels, framesize, samplerate, false);
+ }
+ }
+ if (sampleformat == 3) {
+ audioformat = new AudioFormat(
+ AudioFloatConverter.PCM_FLOAT, samplerate, bits,
+ channels, framesize, samplerate, false);
+ }
+
+ sample.format = audioformat;
+ }
+
+ if (format.equals("data")) {
+ if (largeFormat) {
+ sample.setData(new ModelByteBuffer(sampleFile,
+ chunk.getFilePointer(), chunk.available()));
+ } else {
+ byte[] buffer = new byte[chunk.available()];
+ // chunk.read(buffer);
+ sample.setData(buffer);
+
+ int read = 0;
+ int avail = chunk.available();
+ while (read != avail) {
+ if (avail - read > 65536) {
+ chunk.readFully(buffer, read, 65536);
+ read += 65536;
+ } else {
+ chunk.readFully(buffer, read, avail - read);
+ read = avail;
+ }
+ }
+ }
+ }
+
+ if (format.equals("wsmp")) {
+ sample.sampleoptions = new DLSSampleOptions();
+ readWsmpChunk(sample.sampleoptions, chunk);
+ }
+ }
+ }
+
+ samples.add(sample);
+
+ }
+
+ private void readWaveInfoChunk(DLSSample dlssample, RIFFReader riff)
+ throws IOException {
+ dlssample.info.name = null;
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("INAM")) {
+ dlssample.info.name = chunk.readString(chunk.available());
+ } else if (format.equals("ICRD")) {
+ dlssample.info.creationDate =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IENG")) {
+ dlssample.info.engineers = chunk.readString(chunk.available());
+ } else if (format.equals("IPRD")) {
+ dlssample.info.product = chunk.readString(chunk.available());
+ } else if (format.equals("ICOP")) {
+ dlssample.info.copyright = chunk.readString(chunk.available());
+ } else if (format.equals("ICMT")) {
+ dlssample.info.comments = chunk.readString(chunk.available());
+ } else if (format.equals("ISFT")) {
+ dlssample.info.tools = chunk.readString(chunk.available());
+ } else if (format.equals("IARL")) {
+ dlssample.info.archival_location =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IART")) {
+ dlssample.info.artist = chunk.readString(chunk.available());
+ } else if (format.equals("ICMS")) {
+ dlssample.info.commissioned =
+ chunk.readString(chunk.available());
+ } else if (format.equals("IGNR")) {
+ dlssample.info.genre = chunk.readString(chunk.available());
+ } else if (format.equals("IKEY")) {
+ dlssample.info.keywords = chunk.readString(chunk.available());
+ } else if (format.equals("IMED")) {
+ dlssample.info.medium = chunk.readString(chunk.available());
+ } else if (format.equals("ISBJ")) {
+ dlssample.info.subject = chunk.readString(chunk.available());
+ } else if (format.equals("ISRC")) {
+ dlssample.info.source = chunk.readString(chunk.available());
+ } else if (format.equals("ISRF")) {
+ dlssample.info.source_form = chunk.readString(chunk.available());
+ } else if (format.equals("ITCH")) {
+ dlssample.info.technician = chunk.readString(chunk.available());
+ }
+ }
+ }
+
+ public void save(String name) throws IOException {
+ writeSoundbank(new RIFFWriter(name, "DLS "));
+ }
+
+ public void save(File file) throws IOException {
+ writeSoundbank(new RIFFWriter(file, "DLS "));
+ }
+
+ public void save(OutputStream out) throws IOException {
+ writeSoundbank(new RIFFWriter(out, "DLS "));
+ }
+
+ private void writeSoundbank(RIFFWriter writer) throws IOException {
+ RIFFWriter colh_chunk = writer.writeChunk("colh");
+ colh_chunk.writeUnsignedInt(instruments.size());
+
+ if (major != -1 && minor != -1) {
+ RIFFWriter vers_chunk = writer.writeChunk("vers");
+ vers_chunk.writeUnsignedInt(major);
+ vers_chunk.writeUnsignedInt(minor);
+ }
+
+ writeInstruments(writer.writeList("lins"));
+
+ RIFFWriter ptbl = writer.writeChunk("ptbl");
+ ptbl.writeUnsignedInt(8);
+ ptbl.writeUnsignedInt(samples.size());
+ long ptbl_offset = writer.getFilePointer();
+ for (int i = 0; i < samples.size(); i++)
+ ptbl.writeUnsignedInt(0);
+
+ RIFFWriter wvpl = writer.writeList("wvpl");
+ long off = wvpl.getFilePointer();
+ List<Long> offsettable = new ArrayList<Long>();
+ for (DLSSample sample : samples) {
+ offsettable.add(Long.valueOf(wvpl.getFilePointer() - off));
+ writeSample(wvpl.writeList("wave"), sample);
+ }
+
+ // small cheat, we are going to rewrite data back in wvpl
+ long bak = writer.getFilePointer();
+ writer.seek(ptbl_offset);
+ writer.setWriteOverride(true);
+ for (Long offset : offsettable)
+ writer.writeUnsignedInt(offset.longValue());
+ writer.setWriteOverride(false);
+ writer.seek(bak);
+
+ writeInfo(writer.writeList("INFO"), info);
+
+ writer.close();
+ }
+
+ private void writeSample(RIFFWriter writer, DLSSample sample)
+ throws IOException {
+
+ AudioFormat audioformat = sample.getFormat();
+
+ Encoding encoding = audioformat.getEncoding();
+ float sampleRate = audioformat.getSampleRate();
+ int sampleSizeInBits = audioformat.getSampleSizeInBits();
+ int channels = audioformat.getChannels();
+ int frameSize = audioformat.getFrameSize();
+ float frameRate = audioformat.getFrameRate();
+ boolean bigEndian = audioformat.isBigEndian();
+
+ boolean convert_needed = false;
+
+ if (audioformat.getSampleSizeInBits() == 8) {
+ if (!encoding.equals(Encoding.PCM_UNSIGNED)) {
+ encoding = Encoding.PCM_UNSIGNED;
+ convert_needed = true;
+ }
+ } else {
+ if (!encoding.equals(Encoding.PCM_SIGNED)) {
+ encoding = Encoding.PCM_SIGNED;
+ convert_needed = true;
+ }
+ if (bigEndian) {
+ bigEndian = false;
+ convert_needed = true;
+ }
+ }
+
+ if (convert_needed) {
+ audioformat = new AudioFormat(encoding, sampleRate,
+ sampleSizeInBits, channels, frameSize, frameRate, bigEndian);
+ }
+
+ // fmt
+ RIFFWriter fmt_chunk = writer.writeChunk("fmt ");
+ int sampleformat = 0;
+ if (audioformat.getEncoding().equals(Encoding.PCM_UNSIGNED))
+ sampleformat = 1;
+ else if (audioformat.getEncoding().equals(Encoding.PCM_SIGNED))
+ sampleformat = 1;
+ else if (audioformat.getEncoding().equals(AudioFloatConverter.PCM_FLOAT))
+ sampleformat = 3;
+
+ fmt_chunk.writeUnsignedShort(sampleformat);
+ fmt_chunk.writeUnsignedShort(audioformat.getChannels());
+ fmt_chunk.writeUnsignedInt((long) audioformat.getSampleRate());
+ long srate = ((long)audioformat.getFrameRate())*audioformat.getFrameSize();
+ fmt_chunk.writeUnsignedInt(srate);
+ fmt_chunk.writeUnsignedShort(audioformat.getFrameSize());
+ fmt_chunk.writeUnsignedShort(audioformat.getSampleSizeInBits());
+ fmt_chunk.write(0);
+ fmt_chunk.write(0);
+
+ writeSampleOptions(writer.writeChunk("wsmp"), sample.sampleoptions);
+
+ if (convert_needed) {
+ RIFFWriter data_chunk = writer.writeChunk("data");
+ AudioInputStream stream = AudioSystem.getAudioInputStream(
+ audioformat, (AudioInputStream)sample.getData());
+ byte[] buff = new byte[1024];
+ int ret;
+ while ((ret = stream.read(buff)) != -1) {
+ data_chunk.write(buff, 0, ret);
+ }
+ } else {
+ RIFFWriter data_chunk = writer.writeChunk("data");
+ ModelByteBuffer databuff = sample.getDataBuffer();
+ databuff.writeTo(data_chunk);
+ /*
+ data_chunk.write(databuff.array(),
+ databuff.arrayOffset(),
+ databuff.capacity());
+ */
+ }
+
+ writeInfo(writer.writeList("INFO"), sample.info);
+ }
+
+ private void writeInstruments(RIFFWriter writer) throws IOException {
+ for (DLSInstrument instrument : instruments) {
+ writeInstrument(writer.writeList("ins "), instrument);
+ }
+ }
+
+ private void writeInstrument(RIFFWriter writer, DLSInstrument instrument)
+ throws IOException {
+
+ int art1_count = 0;
+ int art2_count = 0;
+ for (DLSModulator modulator : instrument.getModulators()) {
+ if (modulator.version == 1)
+ art1_count++;
+ if (modulator.version == 2)
+ art2_count++;
+ }
+ for (DLSRegion region : instrument.regions) {
+ for (DLSModulator modulator : region.getModulators()) {
+ if (modulator.version == 1)
+ art1_count++;
+ if (modulator.version == 2)
+ art2_count++;
+ }
+ }
+
+ int version = 1;
+ if (art2_count > 0)
+ version = 2;
+
+ RIFFWriter insh_chunk = writer.writeChunk("insh");
+ insh_chunk.writeUnsignedInt(instrument.getRegions().size());
+ insh_chunk.writeUnsignedInt(instrument.bank +
+ (instrument.druminstrument ? 2147483648L : 0));
+ insh_chunk.writeUnsignedInt(instrument.preset);
+
+ RIFFWriter lrgn = writer.writeList("lrgn");
+ for (DLSRegion region: instrument.regions)
+ writeRegion(lrgn, region, version);
+
+ writeArticulators(writer, instrument.getModulators());
+
+ writeInfo(writer.writeList("INFO"), instrument.info);
+
+ }
+
+ private void writeArticulators(RIFFWriter writer,
+ List<DLSModulator> modulators) throws IOException {
+ int art1_count = 0;
+ int art2_count = 0;
+ for (DLSModulator modulator : modulators) {
+ if (modulator.version == 1)
+ art1_count++;
+ if (modulator.version == 2)
+ art2_count++;
+ }
+ if (art1_count > 0) {
+ RIFFWriter lar1 = writer.writeList("lart");
+ RIFFWriter art1 = lar1.writeChunk("art1");
+ art1.writeUnsignedInt(8);
+ art1.writeUnsignedInt(art1_count);
+ for (DLSModulator modulator : modulators) {
+ if (modulator.version == 1) {
+ art1.writeUnsignedShort(modulator.source);
+ art1.writeUnsignedShort(modulator.control);
+ art1.writeUnsignedShort(modulator.destination);
+ art1.writeUnsignedShort(modulator.transform);
+ art1.writeInt(modulator.scale);
+ }
+ }
+ }
+ if (art2_count > 0) {
+ RIFFWriter lar2 = writer.writeList("lar2");
+ RIFFWriter art2 = lar2.writeChunk("art2");
+ art2.writeUnsignedInt(8);
+ art2.writeUnsignedInt(art2_count);
+ for (DLSModulator modulator : modulators) {
+ if (modulator.version == 2) {
+ art2.writeUnsignedShort(modulator.source);
+ art2.writeUnsignedShort(modulator.control);
+ art2.writeUnsignedShort(modulator.destination);
+ art2.writeUnsignedShort(modulator.transform);
+ art2.writeInt(modulator.scale);
+ }
+ }
+ }
+ }
+
+ private void writeRegion(RIFFWriter writer, DLSRegion region, int version)
+ throws IOException {
+ RIFFWriter rgns = null;
+ if (version == 1)
+ rgns = writer.writeList("rgn ");
+ if (version == 2)
+ rgns = writer.writeList("rgn2");
+ if (rgns == null)
+ return;
+
+ RIFFWriter rgnh = rgns.writeChunk("rgnh");
+ rgnh.writeUnsignedShort(region.keyfrom);
+ rgnh.writeUnsignedShort(region.keyto);
+ rgnh.writeUnsignedShort(region.velfrom);
+ rgnh.writeUnsignedShort(region.velto);
+ rgnh.writeUnsignedShort(region.options);
+ rgnh.writeUnsignedShort(region.exclusiveClass);
+
+ if (region.sampleoptions != null)
+ writeSampleOptions(rgns.writeChunk("wsmp"), region.sampleoptions);
+
+ if (region.sample != null) {
+ if (samples.indexOf(region.sample) != -1) {
+ RIFFWriter wlnk = rgns.writeChunk("wlnk");
+ wlnk.writeUnsignedShort(region.fusoptions);
+ wlnk.writeUnsignedShort(region.phasegroup);
+ wlnk.writeUnsignedInt(region.channel);
+ wlnk.writeUnsignedInt(samples.indexOf(region.sample));
+ }
+ }
+ writeArticulators(rgns, region.getModulators());
+ rgns.close();
+ }
+
+ private void writeSampleOptions(RIFFWriter wsmp,
+ DLSSampleOptions sampleoptions) throws IOException {
+ wsmp.writeUnsignedInt(20);
+ wsmp.writeUnsignedShort(sampleoptions.unitynote);
+ wsmp.writeShort(sampleoptions.finetune);
+ wsmp.writeInt(sampleoptions.attenuation);
+ wsmp.writeUnsignedInt(sampleoptions.options);
+ wsmp.writeInt(sampleoptions.loops.size());
+
+ for (DLSSampleLoop loop : sampleoptions.loops) {
+ wsmp.writeUnsignedInt(16);
+ wsmp.writeUnsignedInt(loop.type);
+ wsmp.writeUnsignedInt(loop.start);
+ wsmp.writeUnsignedInt(loop.length);
+ }
+ }
+
+ private void writeInfoStringChunk(RIFFWriter writer,
+ String name, String value) throws IOException {
+ if (value == null)
+ return;
+ RIFFWriter chunk = writer.writeChunk(name);
+ chunk.writeString(value);
+ int len = value.getBytes("ascii").length;
+ chunk.write(0);
+ len++;
+ if (len % 2 != 0)
+ chunk.write(0);
+ }
+
+ private void writeInfo(RIFFWriter writer, DLSInfo info) throws IOException {
+ writeInfoStringChunk(writer, "INAM", info.name);
+ writeInfoStringChunk(writer, "ICRD", info.creationDate);
+ writeInfoStringChunk(writer, "IENG", info.engineers);
+ writeInfoStringChunk(writer, "IPRD", info.product);
+ writeInfoStringChunk(writer, "ICOP", info.copyright);
+ writeInfoStringChunk(writer, "ICMT", info.comments);
+ writeInfoStringChunk(writer, "ISFT", info.tools);
+ writeInfoStringChunk(writer, "IARL", info.archival_location);
+ writeInfoStringChunk(writer, "IART", info.artist);
+ writeInfoStringChunk(writer, "ICMS", info.commissioned);
+ writeInfoStringChunk(writer, "IGNR", info.genre);
+ writeInfoStringChunk(writer, "IKEY", info.keywords);
+ writeInfoStringChunk(writer, "IMED", info.medium);
+ writeInfoStringChunk(writer, "ISBJ", info.subject);
+ writeInfoStringChunk(writer, "ISRC", info.source);
+ writeInfoStringChunk(writer, "ISRF", info.source_form);
+ writeInfoStringChunk(writer, "ITCH", info.technician);
+ }
+
+ public DLSInfo getInfo() {
+ return info;
+ }
+
+ public String getName() {
+ return info.name;
+ }
+
+ public String getVersion() {
+ return major + "." + minor;
+ }
+
+ public String getVendor() {
+ return info.engineers;
+ }
+
+ public String getDescription() {
+ return info.comments;
+ }
+
+ public void setName(String s) {
+ info.name = s;
+ }
+
+ public void setVendor(String s) {
+ info.engineers = s;
+ }
+
+ public void setDescription(String s) {
+ info.comments = s;
+ }
+
+ public SoundbankResource[] getResources() {
+ SoundbankResource[] resources = new SoundbankResource[samples.size()];
+ int j = 0;
+ for (int i = 0; i < samples.size(); i++)
+ resources[j++] = samples.get(i);
+ return resources;
+ }
+
+ public DLSInstrument[] getInstruments() {
+ DLSInstrument[] inslist_array =
+ instruments.toArray(new DLSInstrument[instruments.size()]);
+ Arrays.sort(inslist_array, new ModelInstrumentComparator());
+ return inslist_array;
+ }
+
+ public DLSSample[] getSamples() {
+ return samples.toArray(new DLSSample[samples.size()]);
+ }
+
+ public Instrument getInstrument(Patch patch) {
+ int program = patch.getProgram();
+ int bank = patch.getBank();
+ boolean percussion = false;
+ if (patch instanceof ModelPatch)
+ percussion = ((ModelPatch) patch).isPercussion();
+ for (Instrument instrument : instruments) {
+ Patch patch2 = instrument.getPatch();
+ int program2 = patch2.getProgram();
+ int bank2 = patch2.getBank();
+ if (program == program2 && bank == bank2) {
+ boolean percussion2 = false;
+ if (patch2 instanceof ModelPatch)
+ percussion2 = ((ModelPatch) patch2).isPercussion();
+ if (percussion == percussion2)
+ return instrument;
+ }
+ }
+ return null;
+ }
+
+ public void addResource(SoundbankResource resource) {
+ if (resource instanceof DLSInstrument)
+ instruments.add((DLSInstrument) resource);
+ if (resource instanceof DLSSample)
+ samples.add((DLSSample) resource);
+ }
+
+ public void removeResource(SoundbankResource resource) {
+ if (resource instanceof DLSInstrument)
+ instruments.remove((DLSInstrument) resource);
+ if (resource instanceof DLSSample)
+ samples.remove((DLSSample) resource);
+ }
+
+ public void addInstrument(DLSInstrument resource) {
+ instruments.add(resource);
+ }
+
+ public void removeInstrument(DLSInstrument resource) {
+ instruments.remove(resource);
+ }
+
+ public long getMajor() {
+ return major;
+ }
+
+ public void setMajor(long major) {
+ this.major = major;
+ }
+
+ public long getMinor() {
+ return minor;
+ }
+
+ public void setMinor(long minor) {
+ this.minor = minor;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DLSSoundbankReader.java b/src/share/classes/com/sun/media/sound/DLSSoundbankReader.java
new file mode 100644
index 000000000..294384976
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/DLSSoundbankReader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.spi.SoundbankReader;
+
+/**
+ * This class is used to connect the DLSSoundBank class
+ * to the SoundbankReader SPI interface.
+ *
+ * @author Karl Helgason
+ */
+public class DLSSoundbankReader extends SoundbankReader {
+
+ public Soundbank getSoundbank(URL url)
+ throws InvalidMidiDataException, IOException {
+ try {
+ return new DLSSoundbank(url);
+ } catch (RIFFInvalidFormatException e) {
+ return null;
+ } catch(IOException ioe) {
+ return null;
+ }
+ }
+
+ public Soundbank getSoundbank(InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ try {
+ stream.mark(512);
+ return new DLSSoundbank(stream);
+ } catch (RIFFInvalidFormatException e) {
+ stream.reset();
+ return null;
+ }
+ }
+
+ public Soundbank getSoundbank(File file)
+ throws InvalidMidiDataException, IOException {
+ try {
+ return new DLSSoundbank(file);
+ } catch (RIFFInvalidFormatException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/DirectAudioDevice.java b/src/share/classes/com/sun/media/sound/DirectAudioDevice.java
index 754aebf6a..7a4289b1a 100644
--- a/src/share/classes/com/sun/media/sound/DirectAudioDevice.java
+++ b/src/share/classes/com/sun/media/sound/DirectAudioDevice.java
@@ -394,7 +394,12 @@ class DirectAudioDevice extends AbstractMixer {
private float leftGain, rightGain;
protected volatile boolean noService = false; // do not run the nService method
+ // Guards all native calls.
protected Object lockNative = new Object();
+ // Guards the lastOpened static variable in implOpen and implClose.
+ protected static Object lockLast = new Object();
+ // Keeps track of last opened line, see implOpen "trick".
+ protected static DirectDL lastOpened;
// CONSTRUCTOR
protected DirectDL(DataLine.Info info,
@@ -496,20 +501,50 @@ class DirectAudioDevice extends AbstractMixer {
// align buffer to full frames
bufferSize = ((int) bufferSize / format.getFrameSize()) * format.getFrameSize();
- id = nOpen(mixerIndex, deviceID, isSource,
- encoding,
- hardwareFormat.getSampleRate(),
- hardwareFormat.getSampleSizeInBits(),
- hardwareFormat.getFrameSize(),
- hardwareFormat.getChannels(),
- hardwareFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED),
- hardwareFormat.isBigEndian(),
- bufferSize);
+ synchronized(lockLast) {
+ id = nOpen(mixerIndex, deviceID, isSource,
+ encoding,
+ hardwareFormat.getSampleRate(),
+ hardwareFormat.getSampleSizeInBits(),
+ hardwareFormat.getFrameSize(),
+ hardwareFormat.getChannels(),
+ hardwareFormat.getEncoding().equals(
+ AudioFormat.Encoding.PCM_SIGNED),
+ hardwareFormat.isBigEndian(),
+ bufferSize);
+
+ if (id == 0) {
+ // Bah... Dirty trick. The most likely cause is an application
+ // already having a line open for this particular hardware
+ // format and forgetting about it. If so, silently close that
+ // implementation and try again. Unfortuantely we can only
+ // open one line per hardware format currently.
+ if (lastOpened != null
+ && hardwareFormat.matches(lastOpened.hardwareFormat)) {
+ lastOpened.implClose();
+ lastOpened = null;
+
+ id = nOpen(mixerIndex, deviceID, isSource,
+ encoding,
+ hardwareFormat.getSampleRate(),
+ hardwareFormat.getSampleSizeInBits(),
+ hardwareFormat.getFrameSize(),
+ hardwareFormat.getChannels(),
+ hardwareFormat.getEncoding().equals(
+ AudioFormat.Encoding.PCM_SIGNED),
+ hardwareFormat.isBigEndian(),
+ bufferSize);
+ }
- if (id == 0) {
- // TODO: nicer error messages...
- throw new LineUnavailableException("line with format "+format+" not supported.");
+ if (id == 0) {
+ // TODO: nicer error messages...
+ throw new LineUnavailableException(
+ "line with format "+format+" not supported.");
+ }
+ }
+ lastOpened = this;
}
+
this.bufferSize = nGetBufferSize(id, isSource);
if (this.bufferSize < 1) {
// this is an error!
@@ -580,12 +615,12 @@ class DirectAudioDevice extends AbstractMixer {
}
synchronized (lockNative) {
nStop(id, isSource);
- }
- // need to set doIO to false before notifying the
- // read/write thread, that's why isStartedRunning()
- // cannot be used
- doIO = false;
+ // need to set doIO to false before notifying the
+ // read/write thread, that's why isStartedRunning()
+ // cannot be used
+ doIO = false;
+ }
// wake up any waiting threads
synchronized(lock) {
lock.notifyAll();
@@ -614,8 +649,12 @@ class DirectAudioDevice extends AbstractMixer {
doIO = false;
long oldID = id;
id = 0;
- synchronized (lockNative) {
- nClose(oldID, isSource);
+ synchronized (lockLast) {
+ synchronized (lockNative) {
+ nClose(oldID, isSource);
+ if (lastOpened == this)
+ lastOpened = null;
+ }
}
bytePosition = 0;
softwareConversionSize = 0;
@@ -630,7 +669,8 @@ class DirectAudioDevice extends AbstractMixer {
}
int a = 0;
synchronized (lockNative) {
- a = nAvailable(id, isSource);
+ if (doIO)
+ a = nAvailable(id, isSource);
}
return a;
}
@@ -644,9 +684,9 @@ class DirectAudioDevice extends AbstractMixer {
int counter = 0;
long startPos = getLongFramePosition();
boolean posChanged = false;
- while (!drained && doIO) {
+ while (!drained) {
synchronized (lockNative) {
- if ((id == 0) || !nIsStillDraining(id, isSource))
+ if ((id == 0) || (!doIO) || !nIsStillDraining(id, isSource))
break;
}
// check every now and then for a new position
@@ -686,7 +726,7 @@ class DirectAudioDevice extends AbstractMixer {
lock.notifyAll();
}
synchronized (lockNative) {
- if (id != 0) {
+ if (id != 0 && doIO) {
// then flush native buffers
nFlush(id, isSource);
}
@@ -697,9 +737,10 @@ class DirectAudioDevice extends AbstractMixer {
// replacement for getFramePosition (see AbstractDataLine)
public long getLongFramePosition() {
- long pos;
+ long pos = 0;
synchronized (lockNative) {
- pos = nGetBytePosition(id, isSource, bytePosition);
+ if (doIO)
+ pos = nGetBytePosition(id, isSource, bytePosition);
}
// hack because ALSA sometimes reports wrong framepos
if (pos < 0) {
@@ -745,11 +786,12 @@ class DirectAudioDevice extends AbstractMixer {
}
int written = 0;
while (!flushing) {
- int thisWritten;
+ int thisWritten = 0;
synchronized (lockNative) {
- thisWritten = nWrite(id, b, off, len,
- softwareConversionSize,
- leftGain, rightGain);
+ if (doIO)
+ thisWritten = nWrite(id, b, off, len,
+ softwareConversionSize,
+ leftGain, rightGain);
if (thisWritten < 0) {
// error in native layer
break;
@@ -972,9 +1014,10 @@ class DirectAudioDevice extends AbstractMixer {
}
int read = 0;
while (doIO && !flushing) {
- int thisRead;
+ int thisRead = 0;
synchronized (lockNative) {
- thisRead = nRead(id, b, off, len, softwareConversionSize);
+ if (doIO)
+ thisRead = nRead(id, b, off, len, softwareConversionSize);
if (thisRead < 0) {
// error in native layer
break;
@@ -1209,7 +1252,8 @@ class DirectAudioDevice extends AbstractMixer {
// set new native position (if necessary)
// this must come after the flush!
synchronized (lockNative) {
- nSetBytePosition(id, isSource, frames * frameSize);
+ if (doIO)
+ nSetBytePosition(id, isSource, frames * frameSize);
}
if (Printer.debug) Printer.debug(" DirectClip.setFramePosition: "
diff --git a/src/share/classes/com/sun/media/sound/EmergencySoundbank.java b/src/share/classes/com/sun/media/sound/EmergencySoundbank.java
new file mode 100644
index 000000000..9e248641c
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/EmergencySoundbank.java
@@ -0,0 +1,2695 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.Random;
+
+import javax.sound.midi.Patch;
+import javax.sound.sampled.AudioFormat;
+
+/**
+ * Emergency Soundbank generator.
+ * Used when no other default soundbank can be found.
+ *
+ * @author Karl Helgason
+ */
+public class EmergencySoundbank {
+
+ private final static String[] general_midi_instruments = {
+ "Acoustic Grand Piano",
+ "Bright Acoustic Piano",
+ "Electric Grand Piano",
+ "Honky-tonk Piano",
+ "Electric Piano 1",
+ "Electric Piano 2",
+ "Harpsichord",
+ "Clavi",
+ "Celesta",
+ "Glockenspiel",
+ "Music Box",
+ "Vibraphone",
+ "Marimba",
+ "Xylophone",
+ "Tubular Bells",
+ "Dulcimer",
+ "Drawbar Organ",
+ "Percussive Organ",
+ "Rock Organ",
+ "Church Organ",
+ "Reed Organ",
+ "Accordion",
+ "Harmonica",
+ "Tango Accordion",
+ "Acoustic Guitar (nylon)",
+ "Acoustic Guitar (steel)",
+ "Electric Guitar (jazz)",
+ "Electric Guitar (clean)",
+ "Electric Guitar (muted)",
+ "Overdriven Guitar",
+ "Distortion Guitar",
+ "Guitar harmonics",
+ "Acoustic Bass",
+ "Electric Bass (finger)",
+ "Electric Bass (pick)",
+ "Fretless Bass",
+ "Slap Bass 1",
+ "Slap Bass 2",
+ "Synth Bass 1",
+ "Synth Bass 2",
+ "Violin",
+ "Viola",
+ "Cello",
+ "Contrabass",
+ "Tremolo Strings",
+ "Pizzicato Strings",
+ "Orchestral Harp",
+ "Timpani",
+ "String Ensemble 1",
+ "String Ensemble 2",
+ "SynthStrings 1",
+ "SynthStrings 2",
+ "Choir Aahs",
+ "Voice Oohs",
+ "Synth Voice",
+ "Orchestra Hit",
+ "Trumpet",
+ "Trombone",
+ "Tuba",
+ "Muted Trumpet",
+ "French Horn",
+ "Brass Section",
+ "SynthBrass 1",
+ "SynthBrass 2",
+ "Soprano Sax",
+ "Alto Sax",
+ "Tenor Sax",
+ "Baritone Sax",
+ "Oboe",
+ "English Horn",
+ "Bassoon",
+ "Clarinet",
+ "Piccolo",
+ "Flute",
+ "Recorder",
+ "Pan Flute",
+ "Blown Bottle",
+ "Shakuhachi",
+ "Whistle",
+ "Ocarina",
+ "Lead 1 (square)",
+ "Lead 2 (sawtooth)",
+ "Lead 3 (calliope)",
+ "Lead 4 (chiff)",
+ "Lead 5 (charang)",
+ "Lead 6 (voice)",
+ "Lead 7 (fifths)",
+ "Lead 8 (bass + lead)",
+ "Pad 1 (new age)",
+ "Pad 2 (warm)",
+ "Pad 3 (polysynth)",
+ "Pad 4 (choir)",
+ "Pad 5 (bowed)",
+ "Pad 6 (metallic)",
+ "Pad 7 (halo)",
+ "Pad 8 (sweep)",
+ "FX 1 (rain)",
+ "FX 2 (soundtrack)",
+ "FX 3 (crystal)",
+ "FX 4 (atmosphere)",
+ "FX 5 (brightness)",
+ "FX 6 (goblins)",
+ "FX 7 (echoes)",
+ "FX 8 (sci-fi)",
+ "Sitar",
+ "Banjo",
+ "Shamisen",
+ "Koto",
+ "Kalimba",
+ "Bag pipe",
+ "Fiddle",
+ "Shanai",
+ "Tinkle Bell",
+ "Agogo",
+ "Steel Drums",
+ "Woodblock",
+ "Taiko Drum",
+ "Melodic Tom",
+ "Synth Drum",
+ "Reverse Cymbal",
+ "Guitar Fret Noise",
+ "Breath Noise",
+ "Seashore",
+ "Bird Tweet",
+ "Telephone Ring",
+ "Helicopter",
+ "Applause",
+ "Gunshot"
+ };
+
+ public static SF2Soundbank createSoundbank() throws Exception {
+ SF2Soundbank sf2 = new SF2Soundbank();
+ sf2.setName("Emergency GM sound set");
+ sf2.setVendor("Generated");
+ sf2.setDescription("Emergency generated soundbank");
+
+ /*
+ * percussion instruments
+ */
+
+ SF2Layer bass_drum = new_bass_drum(sf2);
+ SF2Layer snare_drum = new_snare_drum(sf2);
+ SF2Layer tom = new_tom(sf2);
+ SF2Layer open_hihat = new_open_hihat(sf2);
+ SF2Layer closed_hihat = new_closed_hihat(sf2);
+ SF2Layer crash_cymbal = new_crash_cymbal(sf2);
+ SF2Layer side_stick = new_side_stick(sf2);
+
+ SF2Layer[] drums = new SF2Layer[128];
+ drums[35] = bass_drum;
+ drums[36] = bass_drum;
+ drums[38] = snare_drum;
+ drums[40] = snare_drum;
+ drums[41] = tom;
+ drums[43] = tom;
+ drums[45] = tom;
+ drums[47] = tom;
+ drums[48] = tom;
+ drums[50] = tom;
+ drums[42] = closed_hihat;
+ drums[44] = closed_hihat;
+ drums[46] = open_hihat;
+ drums[49] = crash_cymbal;
+ drums[51] = crash_cymbal;
+ drums[52] = crash_cymbal;
+ drums[55] = crash_cymbal;
+ drums[57] = crash_cymbal;
+ drums[59] = crash_cymbal;
+
+ // Use side_stick for missing drums:
+ drums[37] = side_stick;
+ drums[39] = side_stick;
+ drums[53] = side_stick;
+ drums[54] = side_stick;
+ drums[56] = side_stick;
+ drums[58] = side_stick;
+ drums[69] = side_stick;
+ drums[70] = side_stick;
+ drums[75] = side_stick;
+ drums[60] = side_stick;
+ drums[61] = side_stick;
+ drums[62] = side_stick;
+ drums[63] = side_stick;
+ drums[64] = side_stick;
+ drums[65] = side_stick;
+ drums[66] = side_stick;
+ drums[67] = side_stick;
+ drums[68] = side_stick;
+ drums[71] = side_stick;
+ drums[72] = side_stick;
+ drums[73] = side_stick;
+ drums[74] = side_stick;
+ drums[76] = side_stick;
+ drums[77] = side_stick;
+ drums[78] = side_stick;
+ drums[79] = side_stick;
+ drums[80] = side_stick;
+ drums[81] = side_stick;
+
+
+ SF2Instrument drum_instrument = new SF2Instrument(sf2);
+ drum_instrument.setName("Standard Kit");
+ drum_instrument.setPatch(new ModelPatch(0, 0, true));
+ sf2.addInstrument(drum_instrument);
+ for (int i = 0; i < drums.length; i++) {
+ if (drums[i] != null) {
+ SF2InstrumentRegion region = new SF2InstrumentRegion();
+ region.setLayer(drums[i]);
+ region.putBytes(SF2InstrumentRegion.GENERATOR_KEYRANGE,
+ new byte[]{(byte) i, (byte) i});
+ drum_instrument.getRegions().add(region);
+ }
+ }
+
+
+ /*
+ * melodic instruments
+ */
+
+ SF2Layer gpiano = new_gpiano(sf2);
+ SF2Layer gpiano2 = new_gpiano2(sf2);
+ SF2Layer gpiano_hammer = new_piano_hammer(sf2);
+ SF2Layer piano1 = new_piano1(sf2);
+ SF2Layer epiano1 = new_epiano1(sf2);
+ SF2Layer epiano2 = new_epiano2(sf2);
+
+ SF2Layer guitar = new_guitar1(sf2);
+ SF2Layer guitar_pick = new_guitar_pick(sf2);
+ SF2Layer guitar_dist = new_guitar_dist(sf2);
+ SF2Layer bass1 = new_bass1(sf2);
+ SF2Layer bass2 = new_bass2(sf2);
+ SF2Layer synthbass = new_synthbass(sf2);
+ SF2Layer string2 = new_string2(sf2);
+ SF2Layer orchhit = new_orchhit(sf2);
+ SF2Layer choir = new_choir(sf2);
+ SF2Layer solostring = new_solostring(sf2);
+ SF2Layer organ = new_organ(sf2);
+ SF2Layer ch_organ = new_ch_organ(sf2);
+ SF2Layer bell = new_bell(sf2);
+ SF2Layer flute = new_flute(sf2);
+
+ SF2Layer timpani = new_timpani(sf2);
+ SF2Layer melodic_toms = new_melodic_toms(sf2);
+ SF2Layer trumpet = new_trumpet(sf2);
+ SF2Layer trombone = new_trombone(sf2);
+ SF2Layer brass_section = new_brass_section(sf2);
+ SF2Layer horn = new_horn(sf2);
+ SF2Layer sax = new_sax(sf2);
+ SF2Layer oboe = new_oboe(sf2);
+ SF2Layer bassoon = new_bassoon(sf2);
+ SF2Layer clarinet = new_clarinet(sf2);
+ SF2Layer reverse_cymbal = new_reverse_cymbal(sf2);
+
+ SF2Layer defaultsound = piano1;
+
+ newInstrument(sf2, "Piano", new Patch(0, 0), gpiano, gpiano_hammer);
+ newInstrument(sf2, "Piano", new Patch(0, 1), gpiano2, gpiano_hammer);
+ newInstrument(sf2, "Piano", new Patch(0, 2), piano1);
+ {
+ SF2Instrument ins = newInstrument(sf2, "Honky-tonk Piano",
+ new Patch(0, 3), piano1, piano1);
+ SF2InstrumentRegion region = ins.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 80);
+ region.putInteger(SF2Region.GENERATOR_FINETUNE, 30);
+ region = ins.getRegions().get(1);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 30);
+ }
+ newInstrument(sf2, "Rhodes", new Patch(0, 4), epiano2);
+ newInstrument(sf2, "Rhodes", new Patch(0, 5), epiano2);
+ newInstrument(sf2, "Clavinet", new Patch(0, 6), epiano1);
+ newInstrument(sf2, "Clavinet", new Patch(0, 7), epiano1);
+ newInstrument(sf2, "Rhodes", new Patch(0, 8), epiano2);
+ newInstrument(sf2, "Bell", new Patch(0, 9), bell);
+ newInstrument(sf2, "Bell", new Patch(0, 10), bell);
+ newInstrument(sf2, "Vibraphone", new Patch(0, 11), bell);
+ newInstrument(sf2, "Marimba", new Patch(0, 12), bell);
+ newInstrument(sf2, "Marimba", new Patch(0, 13), bell);
+ newInstrument(sf2, "Bell", new Patch(0, 14), bell);
+ newInstrument(sf2, "Rock Organ", new Patch(0, 15), organ);
+ newInstrument(sf2, "Rock Organ", new Patch(0, 16), organ);
+ newInstrument(sf2, "Perc Organ", new Patch(0, 17), organ);
+ newInstrument(sf2, "Rock Organ", new Patch(0, 18), organ);
+ newInstrument(sf2, "Church Organ", new Patch(0, 19), ch_organ);
+ newInstrument(sf2, "Accordion", new Patch(0, 20), organ);
+ newInstrument(sf2, "Accordion", new Patch(0, 21), organ);
+ newInstrument(sf2, "Accordion", new Patch(0, 22), organ);
+ newInstrument(sf2, "Accordion", new Patch(0, 23), organ);
+ newInstrument(sf2, "Guitar", new Patch(0, 24), guitar, guitar_pick);
+ newInstrument(sf2, "Guitar", new Patch(0, 25), guitar, guitar_pick);
+ newInstrument(sf2, "Guitar", new Patch(0, 26), guitar, guitar_pick);
+ newInstrument(sf2, "Guitar", new Patch(0, 27), guitar, guitar_pick);
+ newInstrument(sf2, "Guitar", new Patch(0, 28), guitar, guitar_pick);
+ newInstrument(sf2, "Distorted Guitar", new Patch(0, 29), guitar_dist);
+ newInstrument(sf2, "Distorted Guitar", new Patch(0, 30), guitar_dist);
+ newInstrument(sf2, "Guitar", new Patch(0, 31), guitar, guitar_pick);
+ newInstrument(sf2, "Finger Bass", new Patch(0, 32), bass1);
+ newInstrument(sf2, "Finger Bass", new Patch(0, 33), bass1);
+ newInstrument(sf2, "Finger Bass", new Patch(0, 34), bass1);
+ newInstrument(sf2, "Frettless Bass", new Patch(0, 35), bass2);
+ newInstrument(sf2, "Frettless Bass", new Patch(0, 36), bass2);
+ newInstrument(sf2, "Frettless Bass", new Patch(0, 37), bass2);
+ newInstrument(sf2, "Synth Bass1", new Patch(0, 38), synthbass);
+ newInstrument(sf2, "Synth Bass2", new Patch(0, 39), synthbass);
+ newInstrument(sf2, "Solo String", new Patch(0, 40), string2, solostring);
+ newInstrument(sf2, "Solo String", new Patch(0, 41), string2, solostring);
+ newInstrument(sf2, "Solo String", new Patch(0, 42), string2, solostring);
+ newInstrument(sf2, "Solo String", new Patch(0, 43), string2, solostring);
+ newInstrument(sf2, "Solo String", new Patch(0, 44), string2, solostring);
+ newInstrument(sf2, "Def", new Patch(0, 45), defaultsound);
+ newInstrument(sf2, "Harp", new Patch(0, 46), bell);
+ newInstrument(sf2, "Timpani", new Patch(0, 47), timpani);
+ newInstrument(sf2, "Strings", new Patch(0, 48), string2);
+ SF2Instrument slow_strings =
+ newInstrument(sf2, "Slow Strings", new Patch(0, 49), string2);
+ SF2InstrumentRegion region = slow_strings.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, 2500);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 2000);
+ newInstrument(sf2, "Synth Strings", new Patch(0, 50), string2);
+ newInstrument(sf2, "Synth Strings", new Patch(0, 51), string2);
+
+
+ newInstrument(sf2, "Choir", new Patch(0, 52), choir);
+ newInstrument(sf2, "Choir", new Patch(0, 53), choir);
+ newInstrument(sf2, "Choir", new Patch(0, 54), choir);
+ {
+ SF2Instrument ins = newInstrument(sf2, "Orch Hit",
+ new Patch(0, 55), orchhit, orchhit, timpani);
+ region = ins.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_COARSETUNE, -12);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ }
+ newInstrument(sf2, "Trumpet", new Patch(0, 56), trumpet);
+ newInstrument(sf2, "Trombone", new Patch(0, 57), trombone);
+ newInstrument(sf2, "Trombone", new Patch(0, 58), trombone);
+ newInstrument(sf2, "Trumpet", new Patch(0, 59), trumpet);
+ newInstrument(sf2, "Horn", new Patch(0, 60), horn);
+ newInstrument(sf2, "Brass Section", new Patch(0, 61), brass_section);
+ newInstrument(sf2, "Brass Section", new Patch(0, 62), brass_section);
+ newInstrument(sf2, "Brass Section", new Patch(0, 63), brass_section);
+ newInstrument(sf2, "Sax", new Patch(0, 64), sax);
+ newInstrument(sf2, "Sax", new Patch(0, 65), sax);
+ newInstrument(sf2, "Sax", new Patch(0, 66), sax);
+ newInstrument(sf2, "Sax", new Patch(0, 67), sax);
+ newInstrument(sf2, "Oboe", new Patch(0, 68), oboe);
+ newInstrument(sf2, "Horn", new Patch(0, 69), horn);
+ newInstrument(sf2, "Bassoon", new Patch(0, 70), bassoon);
+ newInstrument(sf2, "Clarinet", new Patch(0, 71), clarinet);
+ newInstrument(sf2, "Flute", new Patch(0, 72), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 73), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 74), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 75), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 76), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 77), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 78), flute);
+ newInstrument(sf2, "Flute", new Patch(0, 79), flute);
+ newInstrument(sf2, "Organ", new Patch(0, 80), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 81), organ);
+ newInstrument(sf2, "Flute", new Patch(0, 82), flute);
+ newInstrument(sf2, "Organ", new Patch(0, 83), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 84), organ);
+ newInstrument(sf2, "Choir", new Patch(0, 85), choir);
+ newInstrument(sf2, "Organ", new Patch(0, 86), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 87), organ);
+ newInstrument(sf2, "Synth Strings", new Patch(0, 88), string2);
+ newInstrument(sf2, "Organ", new Patch(0, 89), organ);
+ newInstrument(sf2, "Def", new Patch(0, 90), defaultsound);
+ newInstrument(sf2, "Choir", new Patch(0, 91), choir);
+ newInstrument(sf2, "Organ", new Patch(0, 92), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 93), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 94), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 95), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 96), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 97), organ);
+ newInstrument(sf2, "Bell", new Patch(0, 98), bell);
+ newInstrument(sf2, "Organ", new Patch(0, 99), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 100), organ);
+ newInstrument(sf2, "Organ", new Patch(0, 101), organ);
+ newInstrument(sf2, "Def", new Patch(0, 102), defaultsound);
+ newInstrument(sf2, "Synth Strings", new Patch(0, 103), string2);
+ newInstrument(sf2, "Def", new Patch(0, 104), defaultsound);
+ newInstrument(sf2, "Def", new Patch(0, 105), defaultsound);
+ newInstrument(sf2, "Def", new Patch(0, 106), defaultsound);
+ newInstrument(sf2, "Def", new Patch(0, 107), defaultsound);
+ newInstrument(sf2, "Marimba", new Patch(0, 108), bell);
+ newInstrument(sf2, "Sax", new Patch(0, 109), sax);
+ newInstrument(sf2, "Solo String", new Patch(0, 110), string2, solostring);
+ newInstrument(sf2, "Oboe", new Patch(0, 111), oboe);
+ newInstrument(sf2, "Bell", new Patch(0, 112), bell);
+ newInstrument(sf2, "Melodic Toms", new Patch(0, 113), melodic_toms);
+ newInstrument(sf2, "Marimba", new Patch(0, 114), bell);
+ newInstrument(sf2, "Melodic Toms", new Patch(0, 115), melodic_toms);
+ newInstrument(sf2, "Melodic Toms", new Patch(0, 116), melodic_toms);
+ newInstrument(sf2, "Melodic Toms", new Patch(0, 117), melodic_toms);
+ newInstrument(sf2, "Reverse Cymbal", new Patch(0, 118), reverse_cymbal);
+ newInstrument(sf2, "Reverse Cymbal", new Patch(0, 119), reverse_cymbal);
+ newInstrument(sf2, "Guitar", new Patch(0, 120), guitar);
+ newInstrument(sf2, "Def", new Patch(0, 121), defaultsound);
+ {
+ SF2Instrument ins = newInstrument(sf2, "Seashore/Reverse Cymbal",
+ new Patch(0, 122), reverse_cymbal);
+ region = ins.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 18500);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 4500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, -4500);
+ }
+ {
+ SF2Instrument ins = newInstrument(sf2, "Bird/Flute",
+ new Patch(0, 123), flute);
+ region = ins.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_COARSETUNE, 24);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, -3000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ }
+ newInstrument(sf2, "Def", new Patch(0, 124), side_stick);
+ {
+ SF2Instrument ins = newInstrument(sf2, "Seashore/Reverse Cymbal",
+ new Patch(0, 125), reverse_cymbal);
+ region = ins.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 18500);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 4500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, -4500);
+ }
+ newInstrument(sf2, "Applause/crash_cymbal",
+ new Patch(0, 126), crash_cymbal);
+ newInstrument(sf2, "Gunshot/side_stick", new Patch(0, 127), side_stick);
+
+ for (SF2Instrument instrument : sf2.getInstruments()) {
+ Patch patch = instrument.getPatch();
+ if (patch instanceof ModelPatch) {
+ if (((ModelPatch) patch).isPercussion())
+ continue;
+ }
+ instrument.setName(general_midi_instruments[patch.getProgram()]);
+ }
+
+ return sf2;
+
+ }
+
+ public static SF2Layer new_bell(SF2Soundbank sf2) {
+ Random random = new Random(102030201);
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.01;
+ double end_w = 0.05;
+ double start_a = 0.2;
+ double end_a = 0.00001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ double detune = 1 + (random.nextDouble() * 2 - 1) * 0.01;
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1) * detune, w, a);
+ a *= a_step;
+ }
+ SF2Sample sample = newSimpleFFTSample(sf2, "EPiano", data, base);
+ SF2Layer layer = newLayer(sf2, "EPiano", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, 1200);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -9000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 16000);
+ return layer;
+ }
+
+ public static SF2Layer new_guitar1(SF2Soundbank sf2) {
+
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.01;
+ double end_w = 0.01;
+ double start_a = 2;
+ double end_a = 0.01;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+
+ double[] aa = new double[40];
+ for (int i = 0; i < 40; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] = 2;
+ aa[1] = 0.5;
+ aa[2] = 0.45;
+ aa[3] = 0.2;
+ aa[4] = 1;
+ aa[5] = 0.5;
+ aa[6] = 2;
+ aa[7] = 1;
+ aa[8] = 0.5;
+ aa[9] = 1;
+ aa[9] = 0.5;
+ aa[10] = 0.2;
+ aa[11] = 1;
+ aa[12] = 0.7;
+ aa[13] = 0.5;
+ aa[14] = 1;
+
+ for (int i = 0; i < 40; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Guitar", data, base);
+ SF2Layer layer = newLayer(sf2, "Guitar", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 2400);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -100);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -6000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 16000);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -20);
+ return layer;
+ }
+
+ public static SF2Layer new_guitar_dist(SF2Soundbank sf2) {
+
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.01;
+ double end_w = 0.01;
+ double start_a = 2;
+ double end_a = 0.01;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+
+ double[] aa = new double[40];
+ for (int i = 0; i < 40; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] = 5;
+ aa[1] = 2;
+ aa[2] = 0.45;
+ aa[3] = 0.2;
+ aa[4] = 1;
+ aa[5] = 0.5;
+ aa[6] = 2;
+ aa[7] = 1;
+ aa[8] = 0.5;
+ aa[9] = 1;
+ aa[9] = 0.5;
+ aa[10] = 0.2;
+ aa[11] = 1;
+ aa[12] = 0.7;
+ aa[13] = 0.5;
+ aa[14] = 1;
+
+ for (int i = 0; i < 40; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ }
+
+
+ SF2Sample sample = newSimpleFFTSample_dist(sf2, "Distorted Guitar",
+ data, base, 10000.0);
+
+
+ SF2Layer layer = newLayer(sf2, "Distorted Guitar", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ //region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 2400);
+ //region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 200);
+
+ //region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -100);
+ //region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ //region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -1000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 8000);
+ //region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -20);
+ return layer;
+ }
+
+ public static SF2Layer new_guitar_pick(SF2Soundbank sf2) {
+
+ double datab[];
+
+ // Make treble part
+ {
+ int m = 2;
+ int fftlen = 4096 * m;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5));
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 0; i < 2048 * m; i++) {
+ data[i] *= Math.exp(-Math.abs((i - 23) / ((double) m)) * 1.2)
+ + Math.exp(-Math.abs((i - 40) / ((double) m)) * 0.9);
+ }
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.8);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9994;
+ }
+ datab = data;
+
+ fadeUp(data, 80);
+ }
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Guitar Noise", datab);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Guitar Noise");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ //region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+// region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+/*
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, 0);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINMODENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -11000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 12000);
+ */
+
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_gpiano(SF2Soundbank sf2) {
+ //Random random = new Random(302030201);
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 15.0);
+
+ double[] aa = new double[30];
+ for (int i = 0; i < 30; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 2;
+ //aa[2] *= 0.1;
+ aa[4] *= 2;
+
+
+ aa[12] *= 0.9;
+ aa[13] *= 0.7;
+ for (int i = 14; i < 30; i++) {
+ aa[i] *= 0.5;
+ }
+
+
+ for (int i = 0; i < 30; i++) {
+ //double detune = 1 + (random.nextDouble()*2 - 1)*0.0001;
+ double w = 0.2;
+ double ai = aa[i];
+ if (i > 10) {
+ w = 5;
+ ai *= 10;
+ }
+ int adjust = 0;
+ if (i > 5) {
+ adjust = (i - 5) * 7;
+ }
+ complexGaussianDist(data, base * (i + 1) + adjust, w, ai);
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Grand Piano", data, base, 200);
+ SF2Layer layer = newLayer(sf2, "Grand Piano", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -7000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -5500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 18000);
+ return layer;
+ }
+
+ public static SF2Layer new_gpiano2(SF2Soundbank sf2) {
+ //Random random = new Random(302030201);
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 20.0);
+
+ double[] aa = new double[30];
+ for (int i = 0; i < 30; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 1;
+ //aa[2] *= 0.1;
+ aa[4] *= 2;
+
+
+ aa[12] *= 0.9;
+ aa[13] *= 0.7;
+ for (int i = 14; i < 30; i++) {
+ aa[i] *= 0.5;
+ }
+
+
+ for (int i = 0; i < 30; i++) {
+ //double detune = 1 + (random.nextDouble()*2 - 1)*0.0001;
+ double w = 0.2;
+ double ai = aa[i];
+ if (i > 10) {
+ w = 5;
+ ai *= 10;
+ }
+ int adjust = 0;
+ if (i > 5) {
+ adjust = (i - 5) * 7;
+ }
+ complexGaussianDist(data, base * (i + 1) + adjust, w, ai);
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Grand Piano", data, base, 200);
+ SF2Layer layer = newLayer(sf2, "Grand Piano", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -7000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -5500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 18000);
+ return layer;
+ }
+
+ public static SF2Layer new_piano_hammer(SF2Soundbank sf2) {
+
+ double datab[];
+
+ // Make treble part
+ {
+ int m = 2;
+ int fftlen = 4096 * m;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5));
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 0; i < 2048 * m; i++)
+ data[i] *= Math.exp(-Math.abs((i - 37) / ((double) m)) * 0.05);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.6);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9997;
+ }
+ datab = data;
+
+ fadeUp(data, 80);
+ }
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Piano Hammer", datab);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Piano Hammer");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ //region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+/*
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, 0);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINMODENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -11000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 12000);
+ */
+
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_piano1(SF2Soundbank sf2) {
+ //Random random = new Random(302030201);
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_a = 0.2;
+ double end_a = 0.0001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+
+ double[] aa = new double[30];
+ for (int i = 0; i < 30; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 5;
+ aa[2] *= 0.1;
+ aa[7] *= 5;
+
+
+ for (int i = 0; i < 30; i++) {
+ //double detune = 1 + (random.nextDouble()*2 - 1)*0.0001;
+ double w = 0.2;
+ double ai = aa[i];
+ if (i > 12) {
+ w = 5;
+ ai *= 10;
+ }
+ int adjust = 0;
+ if (i > 5) {
+ adjust = (i - 5) * 7;
+ }
+ complexGaussianDist(data, base * (i + 1) + adjust, w, ai);
+ }
+
+ complexGaussianDist(data, base * (15.5), 1, 0.1);
+ complexGaussianDist(data, base * (17.5), 1, 0.01);
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "EPiano", data, base, 200);
+ SF2Layer layer = newLayer(sf2, "EPiano", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -1200);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -5500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 16000);
+ return layer;
+ }
+
+ public static SF2Layer new_epiano1(SF2Soundbank sf2) {
+ Random random = new Random(302030201);
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.05;
+ double end_w = 0.05;
+ double start_a = 0.2;
+ double end_a = 0.0001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ double detune = 1 + (random.nextDouble() * 2 - 1) * 0.0001;
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1) * detune, w, a);
+ a *= a_step;
+ }
+
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "EPiano", data, base);
+ SF2Layer layer = newLayer(sf2, "EPiano", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, 1200);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -9000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 16000);
+ return layer;
+ }
+
+ public static SF2Layer new_epiano2(SF2Soundbank sf2) {
+ Random random = new Random(302030201);
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.01;
+ double end_w = 0.05;
+ double start_a = 0.2;
+ double end_a = 0.00001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ double detune = 1 + (random.nextDouble() * 2 - 1) * 0.0001;
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1) * detune, w, a);
+ a *= a_step;
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "EPiano", data, base);
+ SF2Layer layer = newLayer(sf2, "EPiano", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 8000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, 2400);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -9000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 16000);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ return layer;
+ }
+
+ public static SF2Layer new_bass1(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.05;
+ double end_w = 0.05;
+ double start_a = 0.2;
+ double end_a = 0.02;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 25.0);
+
+ double[] aa = new double[25];
+ for (int i = 0; i < 25; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 8;
+ aa[1] *= 4;
+ aa[3] *= 8;
+ aa[5] *= 8;
+
+ for (int i = 0; i < 25; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ }
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Bass", data, base);
+ SF2Layer layer = newLayer(sf2, "Bass", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -3000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 11000);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ return layer;
+ }
+
+ public static SF2Layer new_synthbass(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.05;
+ double end_w = 0.05;
+ double start_a = 0.2;
+ double end_a = 0.02;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 25.0);
+
+ double[] aa = new double[25];
+ for (int i = 0; i < 25; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 16;
+ aa[1] *= 4;
+ aa[3] *= 16;
+ aa[5] *= 8;
+
+ for (int i = 0; i < 25; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ }
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Bass", data, base);
+ SF2Layer layer = newLayer(sf2, "Bass", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -3000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, -3000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERQ, 100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 8000);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ return layer;
+ }
+
+ public static SF2Layer new_bass2(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 0.05;
+ double end_w = 0.05;
+ double start_a = 0.2;
+ double end_a = 0.002;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 25.0);
+
+ double[] aa = new double[25];
+ for (int i = 0; i < 25; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 8;
+ aa[1] *= 4;
+ aa[3] *= 8;
+ aa[5] *= 8;
+
+ for (int i = 0; i < 25; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ }
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Bass2", data, base);
+ SF2Layer layer = newLayer(sf2, "Bass2", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -8000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ return layer;
+ }
+
+ public static SF2Layer new_solostring(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 2;
+ double end_w = 2;
+ double start_a = 0.2;
+ double end_a = 0.01;
+
+ double[] aa = new double[18];
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < aa.length; i++) {
+ a *= a_step;
+ aa[i] = a;
+ }
+
+ aa[0] *= 5;
+ aa[1] *= 5;
+ aa[2] *= 5;
+ aa[3] *= 4;
+ aa[4] *= 4;
+ aa[5] *= 3;
+ aa[6] *= 3;
+ aa[7] *= 2;
+
+ for (int i = 0; i < aa.length; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, a);
+ }
+ SF2Sample sample = newSimpleFFTSample(sf2, "Strings", data, base);
+ SF2Layer layer = newLayer(sf2, "Strings", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -5000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ region.putInteger(SF2Region.GENERATOR_FREQVIBLFO, -1000);
+ region.putInteger(SF2Region.GENERATOR_VIBLFOTOPITCH, 15);
+ return layer;
+
+ }
+
+ public static SF2Layer new_orchhit(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 2;
+ double end_w = 80;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, a);
+ a *= a_step;
+ }
+ complexGaussianDist(data, base * 4, 300, 1);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Och Strings", data, base);
+ SF2Layer layer = newLayer(sf2, "Och Strings", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -5000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 200);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 200);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_string2(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 2;
+ double end_w = 80;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, a);
+ a *= a_step;
+ }
+ SF2Sample sample = newSimpleFFTSample(sf2, "Strings", data, base);
+ SF2Layer layer = newLayer(sf2, "Strings", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -5000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_choir(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 25;
+ double start_w = 2;
+ double end_w = 80;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ double[] aa = new double[40];
+ for (int i = 0; i < aa.length; i++) {
+ a *= a_step;
+ aa[i] = a;
+ }
+
+ aa[5] *= 0.1;
+ aa[6] *= 0.01;
+ aa[7] *= 0.1;
+ aa[8] *= 0.1;
+
+ for (int i = 0; i < aa.length; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ }
+ SF2Sample sample = newSimpleFFTSample(sf2, "Strings", data, base);
+ SF2Layer layer = newLayer(sf2, "Strings", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -5000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_organ(SF2Soundbank sf2) {
+ Random random = new Random(102030201);
+ int x = 1;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+ double start_w = 0.01;
+ double end_w = 0.01;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+
+ for (int i = 0; i < 12; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w,
+ a * (0.5 + 3 * (random.nextDouble())));
+ a *= a_step;
+ }
+ SF2Sample sample = newSimpleFFTSample(sf2, "Organ", data, base);
+ SF2Layer layer = newLayer(sf2, "Organ", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_ch_organ(SF2Soundbank sf2) {
+ int x = 1;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+ double start_w = 0.01;
+ double end_w = 0.01;
+ double start_a = 0.2;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 60.0);
+
+ double[] aa = new double[60];
+ for (int i = 0; i < aa.length; i++) {
+ a *= a_step;
+ aa[i] = a;
+ }
+
+ aa[0] *= 5;
+ aa[1] *= 2;
+ aa[2] = 0;
+ aa[4] = 0;
+ aa[5] = 0;
+ aa[7] *= 7;
+ aa[9] = 0;
+ aa[10] = 0;
+ aa[12] = 0;
+ aa[15] *= 7;
+ aa[18] = 0;
+ aa[20] = 0;
+ aa[24] = 0;
+ aa[27] *= 5;
+ aa[29] = 0;
+ aa[30] = 0;
+ aa[33] = 0;
+ aa[36] *= 4;
+ aa[37] = 0;
+ aa[39] = 0;
+ aa[42] = 0;
+ aa[43] = 0;
+ aa[47] = 0;
+ aa[50] *= 4;
+ aa[52] = 0;
+ aa[55] = 0;
+ aa[57] = 0;
+
+
+ aa[10] *= 0.1;
+ aa[11] *= 0.1;
+ aa[12] *= 0.1;
+ aa[13] *= 0.1;
+
+ aa[17] *= 0.1;
+ aa[18] *= 0.1;
+ aa[19] *= 0.1;
+ aa[20] *= 0.1;
+
+ for (int i = 0; i < 60; i++) {
+ double w = start_w + (end_w - start_w) * (i / 40.0);
+ complexGaussianDist(data, base * (i + 1), w, aa[i]);
+ a *= a_step;
+ }
+ SF2Sample sample = newSimpleFFTSample(sf2, "Organ", data, base);
+ SF2Layer layer = newLayer(sf2, "Organ", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -10000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ return layer;
+
+ }
+
+ public static SF2Layer new_flute(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ complexGaussianDist(data, base * 1, 0.001, 0.5);
+ complexGaussianDist(data, base * 2, 0.001, 0.5);
+ complexGaussianDist(data, base * 3, 0.001, 0.5);
+ complexGaussianDist(data, base * 4, 0.01, 0.5);
+
+ complexGaussianDist(data, base * 4, 100, 120);
+ complexGaussianDist(data, base * 6, 100, 40);
+ complexGaussianDist(data, base * 8, 100, 80);
+
+ complexGaussianDist(data, base * 5, 0.001, 0.05);
+ complexGaussianDist(data, base * 6, 0.001, 0.06);
+ complexGaussianDist(data, base * 7, 0.001, 0.04);
+ complexGaussianDist(data, base * 8, 0.005, 0.06);
+ complexGaussianDist(data, base * 9, 0.005, 0.06);
+ complexGaussianDist(data, base * 10, 0.01, 0.1);
+ complexGaussianDist(data, base * 11, 0.08, 0.7);
+ complexGaussianDist(data, base * 12, 0.08, 0.6);
+ complexGaussianDist(data, base * 13, 0.08, 0.6);
+ complexGaussianDist(data, base * 14, 0.08, 0.6);
+ complexGaussianDist(data, base * 15, 0.08, 0.5);
+ complexGaussianDist(data, base * 16, 0.08, 0.5);
+ complexGaussianDist(data, base * 17, 0.08, 0.2);
+
+
+ complexGaussianDist(data, base * 1, 10, 8);
+ complexGaussianDist(data, base * 2, 10, 8);
+ complexGaussianDist(data, base * 3, 10, 8);
+ complexGaussianDist(data, base * 4, 10, 8);
+ complexGaussianDist(data, base * 5, 10, 8);
+ complexGaussianDist(data, base * 6, 20, 9);
+ complexGaussianDist(data, base * 7, 20, 9);
+ complexGaussianDist(data, base * 8, 20, 9);
+ complexGaussianDist(data, base * 9, 20, 8);
+ complexGaussianDist(data, base * 10, 30, 8);
+ complexGaussianDist(data, base * 11, 30, 9);
+ complexGaussianDist(data, base * 12, 30, 9);
+ complexGaussianDist(data, base * 13, 30, 8);
+ complexGaussianDist(data, base * 14, 30, 8);
+ complexGaussianDist(data, base * 15, 30, 7);
+ complexGaussianDist(data, base * 16, 30, 7);
+ complexGaussianDist(data, base * 17, 30, 6);
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Flute", data, base);
+ SF2Layer layer = newLayer(sf2, "Flute", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_horn(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ double start_a = 0.5;
+ double end_a = 0.00000000001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ if (i == 0)
+ complexGaussianDist(data, base * (i + 1), 0.1, a * 0.2);
+ else
+ complexGaussianDist(data, base * (i + 1), 0.1, a);
+ a *= a_step;
+ }
+
+ complexGaussianDist(data, base * 2, 100, 1);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Horn", data, base);
+ SF2Layer layer = newLayer(sf2, "Horn", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -500);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, 5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 4500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_trumpet(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ double start_a = 0.5;
+ double end_a = 0.00001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 80.0);
+ double[] aa = new double[80];
+ for (int i = 0; i < 80; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 0.05;
+ aa[1] *= 0.2;
+ aa[2] *= 0.5;
+ aa[3] *= 0.85;
+
+ for (int i = 0; i < 80; i++) {
+ complexGaussianDist(data, base * (i + 1), 0.1, aa[i]);
+ }
+
+ complexGaussianDist(data, base * 5, 300, 3);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Trumpet", data, base);
+ SF2Layer layer = newLayer(sf2, "Trumpet", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -10000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 0);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -4000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, -2500);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, 5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 4500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERQ, 10);
+ return layer;
+
+ }
+
+ public static SF2Layer new_brass_section(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ double start_a = 0.5;
+ double end_a = 0.005;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 30.0);
+ double[] aa = new double[30];
+ for (int i = 0; i < 30; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 0.8;
+ aa[1] *= 0.9;
+
+ double w = 5;
+ for (int i = 0; i < 30; i++) {
+ complexGaussianDist(data, base * (i + 1), 0.1 * w, aa[i] * w);
+ w += 6; //*= w_step;
+ }
+
+ complexGaussianDist(data, base * 6, 300, 2);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Brass Section", data, base);
+ SF2Layer layer = newLayer(sf2, "Brass Section", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -9200);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -3000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, 5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 4500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_trombone(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ double start_a = 0.5;
+ double end_a = 0.001;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 80.0);
+ double[] aa = new double[80];
+ for (int i = 0; i < 80; i++) {
+ aa[i] = a;
+ a *= a_step;
+ }
+
+ aa[0] *= 0.3;
+ aa[1] *= 0.7;
+
+ for (int i = 0; i < 80; i++) {
+ complexGaussianDist(data, base * (i + 1), 0.1, aa[i]);
+ }
+
+ complexGaussianDist(data, base * 6, 300, 2);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Trombone", data, base);
+ SF2Layer layer = newLayer(sf2, "Trombone", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -8000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -2000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, 5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 4500);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERQ, 10);
+ return layer;
+
+ }
+
+ public static SF2Layer new_sax(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ double start_a = 0.5;
+ double end_a = 0.01;
+ double a = start_a;
+ double a_step = Math.pow(end_a / start_a, 1.0 / 40.0);
+ for (int i = 0; i < 40; i++) {
+ if (i == 0 || i == 2)
+ complexGaussianDist(data, base * (i + 1), 0.1, a * 4);
+ else
+ complexGaussianDist(data, base * (i + 1), 0.1, a);
+ a *= a_step;
+ }
+
+ complexGaussianDist(data, base * 4, 200, 1);
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Sax", data, base);
+ SF2Layer layer = newLayer(sf2, "Sax", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+
+ region.putInteger(SF2Region.GENERATOR_ATTACKMODENV, -3000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEMODENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_MODENVTOFILTERFC, 5000);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 4500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_oboe(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ complexGaussianDist(data, base * 5, 100, 80);
+
+
+ complexGaussianDist(data, base * 1, 0.01, 0.53);
+ complexGaussianDist(data, base * 2, 0.01, 0.51);
+ complexGaussianDist(data, base * 3, 0.01, 0.48);
+ complexGaussianDist(data, base * 4, 0.01, 0.49);
+ complexGaussianDist(data, base * 5, 0.01, 5);
+ complexGaussianDist(data, base * 6, 0.01, 0.51);
+ complexGaussianDist(data, base * 7, 0.01, 0.50);
+ complexGaussianDist(data, base * 8, 0.01, 0.59);
+ complexGaussianDist(data, base * 9, 0.01, 0.61);
+ complexGaussianDist(data, base * 10, 0.01, 0.52);
+ complexGaussianDist(data, base * 11, 0.01, 0.49);
+ complexGaussianDist(data, base * 12, 0.01, 0.51);
+ complexGaussianDist(data, base * 13, 0.01, 0.48);
+ complexGaussianDist(data, base * 14, 0.01, 0.51);
+ complexGaussianDist(data, base * 15, 0.01, 0.46);
+ complexGaussianDist(data, base * 16, 0.01, 0.35);
+ complexGaussianDist(data, base * 17, 0.01, 0.20);
+ complexGaussianDist(data, base * 18, 0.01, 0.10);
+ complexGaussianDist(data, base * 19, 0.01, 0.5);
+ complexGaussianDist(data, base * 20, 0.01, 0.1);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Oboe", data, base);
+ SF2Layer layer = newLayer(sf2, "Oboe", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_bassoon(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ complexGaussianDist(data, base * 2, 100, 40);
+ complexGaussianDist(data, base * 4, 100, 20);
+
+ complexGaussianDist(data, base * 1, 0.01, 0.53);
+ complexGaussianDist(data, base * 2, 0.01, 5);
+ complexGaussianDist(data, base * 3, 0.01, 0.51);
+ complexGaussianDist(data, base * 4, 0.01, 0.48);
+ complexGaussianDist(data, base * 5, 0.01, 1.49);
+ complexGaussianDist(data, base * 6, 0.01, 0.51);
+ complexGaussianDist(data, base * 7, 0.01, 0.50);
+ complexGaussianDist(data, base * 8, 0.01, 0.59);
+ complexGaussianDist(data, base * 9, 0.01, 0.61);
+ complexGaussianDist(data, base * 10, 0.01, 0.52);
+ complexGaussianDist(data, base * 11, 0.01, 0.49);
+ complexGaussianDist(data, base * 12, 0.01, 0.51);
+ complexGaussianDist(data, base * 13, 0.01, 0.48);
+ complexGaussianDist(data, base * 14, 0.01, 0.51);
+ complexGaussianDist(data, base * 15, 0.01, 0.46);
+ complexGaussianDist(data, base * 16, 0.01, 0.35);
+ complexGaussianDist(data, base * 17, 0.01, 0.20);
+ complexGaussianDist(data, base * 18, 0.01, 0.10);
+ complexGaussianDist(data, base * 19, 0.01, 0.5);
+ complexGaussianDist(data, base * 20, 0.01, 0.1);
+
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Flute", data, base);
+ SF2Layer layer = newLayer(sf2, "Flute", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_clarinet(SF2Soundbank sf2) {
+ int x = 8;
+ int fftsize = 4096 * x;
+ double[] data = new double[fftsize * 2];
+ double base = x * 15;
+
+ complexGaussianDist(data, base * 1, 0.001, 0.5);
+ complexGaussianDist(data, base * 2, 0.001, 0.02);
+ complexGaussianDist(data, base * 3, 0.001, 0.2);
+ complexGaussianDist(data, base * 4, 0.01, 0.1);
+
+ complexGaussianDist(data, base * 4, 100, 60);
+ complexGaussianDist(data, base * 6, 100, 20);
+ complexGaussianDist(data, base * 8, 100, 20);
+
+ complexGaussianDist(data, base * 5, 0.001, 0.1);
+ complexGaussianDist(data, base * 6, 0.001, 0.09);
+ complexGaussianDist(data, base * 7, 0.001, 0.02);
+ complexGaussianDist(data, base * 8, 0.005, 0.16);
+ complexGaussianDist(data, base * 9, 0.005, 0.96);
+ complexGaussianDist(data, base * 10, 0.01, 0.9);
+ complexGaussianDist(data, base * 11, 0.08, 1.2);
+ complexGaussianDist(data, base * 12, 0.08, 1.8);
+ complexGaussianDist(data, base * 13, 0.08, 1.6);
+ complexGaussianDist(data, base * 14, 0.08, 1.2);
+ complexGaussianDist(data, base * 15, 0.08, 0.9);
+ complexGaussianDist(data, base * 16, 0.08, 0.5);
+ complexGaussianDist(data, base * 17, 0.08, 0.2);
+
+
+ complexGaussianDist(data, base * 1, 10, 8);
+ complexGaussianDist(data, base * 2, 10, 8);
+ complexGaussianDist(data, base * 3, 10, 8);
+ complexGaussianDist(data, base * 4, 10, 8);
+ complexGaussianDist(data, base * 5, 10, 8);
+ complexGaussianDist(data, base * 6, 20, 9);
+ complexGaussianDist(data, base * 7, 20, 9);
+ complexGaussianDist(data, base * 8, 20, 9);
+ complexGaussianDist(data, base * 9, 20, 8);
+ complexGaussianDist(data, base * 10, 30, 8);
+ complexGaussianDist(data, base * 11, 30, 9);
+ complexGaussianDist(data, base * 12, 30, 9);
+ complexGaussianDist(data, base * 13, 30, 8);
+ complexGaussianDist(data, base * 14, 30, 8);
+ complexGaussianDist(data, base * 15, 30, 7);
+ complexGaussianDist(data, base * 16, 30, 7);
+ complexGaussianDist(data, base * 17, 30, 6);
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Clarinet", data, base);
+ SF2Layer layer = newLayer(sf2, "Clarinet", sample);
+ SF2Region region = layer.getRegions().get(0);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -6000);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 4000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, -100);
+ region.putInteger(SF2Region.GENERATOR_INITIALFILTERFC, 9500);
+ return layer;
+
+ }
+
+ public static SF2Layer new_timpani(SF2Soundbank sf2) {
+
+ double datab[];
+ double datah[];
+
+ // Make Bass Part
+ {
+ int fftlen = 4096 * 8;
+ double[] data = new double[2 * fftlen];
+ double base = 48;
+ complexGaussianDist(data, base * 2, 0.2, 1);
+ complexGaussianDist(data, base * 3, 0.2, 0.7);
+ complexGaussianDist(data, base * 5, 10, 1);
+ complexGaussianDist(data, base * 6, 9, 1);
+ complexGaussianDist(data, base * 8, 15, 1);
+ complexGaussianDist(data, base * 9, 18, 0.8);
+ complexGaussianDist(data, base * 11, 21, 0.5);
+ complexGaussianDist(data, base * 13, 28, 0.3);
+ complexGaussianDist(data, base * 14, 22, 0.1);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.5);
+ data = realPart(data);
+
+ double d_len = data.length;
+ for (int i = 0; i < data.length; i++) {
+ double g = (1.0 - (i / d_len));
+ data[i] *= g * g;
+ }
+ fadeUp(data, 40);
+ datab = data;
+ }
+
+ // Make treble part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2) {
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ }
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024 * 4; i < 2048 * 4; i++)
+ data[i] = 1.0 - (i - 4096) / 4096.0;
+ for (int i = 0; i < 300; i++) {
+ double g = (1.0 - (i / 300.0));
+ data[i] *= 1.0 + 20 * g * g;
+ }
+ for (int i = 0; i < 24; i++)
+ data[i] = 0;
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9998;
+ }
+ datah = data;
+ }
+
+ for (int i = 0; i < datah.length; i++)
+ datab[i] += datah[i] * 0.02;
+
+ normalize(datab, 0.9);
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Timpani", datab);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Timpani");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_melodic_toms(SF2Soundbank sf2) {
+
+ double datab[];
+ double datah[];
+
+ // Make Bass Part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ complexGaussianDist(data, 30, 0.5, 1);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.8);
+ data = realPart(data);
+
+ double d_len = data.length;
+ for (int i = 0; i < data.length; i++)
+ data[i] *= (1.0 - (i / d_len));
+ datab = data;
+ }
+
+ // Make treble part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024 * 4; i < 2048 * 4; i++)
+ data[i] = 1.0 - (i - 4096) / 4096.0;
+ for (int i = 0; i < 200; i++) {
+ double g = (1.0 - (i / 200.0));
+ data[i] *= 1.0 + 20 * g * g;
+ }
+ for (int i = 0; i < 30; i++)
+ data[i] = 0;
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9996;
+ }
+ datah = data;
+ }
+
+ for (int i = 0; i < datah.length; i++)
+ datab[i] += datah[i] * 0.5;
+ for (int i = 0; i < 5; i++)
+ datab[i] *= i / 5.0;
+
+ normalize(datab, 0.99);
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Melodic Toms", datab);
+ sample.setOriginalPitch(63);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Melodic Toms");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ //region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_reverse_cymbal(SF2Soundbank sf2) {
+ double datah[];
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5));
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 0; i < 100; i++)
+ data[i] = 0;
+
+ for (int i = 0; i < 512 * 2; i++) {
+ double gain = (i / (512.0 * 2.0));
+ data[i] = 1 - gain;
+ }
+ datah = data;
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Reverse Cymbal",
+ datah, 100, 20);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Reverse Cymbal");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_ATTACKVOLENV, -200);
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, -12000);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, -1000);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_snare_drum(SF2Soundbank sf2) {
+
+ double datab[];
+ double datah[];
+
+ // Make Bass Part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ complexGaussianDist(data, 24, 0.5, 1);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.5);
+ data = realPart(data);
+
+ double d_len = data.length;
+ for (int i = 0; i < data.length; i++)
+ data[i] *= (1.0 - (i / d_len));
+ datab = data;
+ }
+
+ // Make treble part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024 * 4; i < 2048 * 4; i++)
+ data[i] = 1.0 - (i - 4096) / 4096.0;
+ for (int i = 0; i < 300; i++) {
+ double g = (1.0 - (i / 300.0));
+ data[i] *= 1.0 + 20 * g * g;
+ }
+ for (int i = 0; i < 24; i++)
+ data[i] = 0;
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9998;
+ }
+ datah = data;
+ }
+
+ for (int i = 0; i < datah.length; i++)
+ datab[i] += datah[i];
+ for (int i = 0; i < 5; i++)
+ datab[i] *= i / 5.0;
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Snare Drum", datab);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Snare Drum");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_bass_drum(SF2Soundbank sf2) {
+
+ double datab[];
+ double datah[];
+
+ // Make Bass Part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ complexGaussianDist(data, 1.8 * 5 + 1, 2, 1);
+ complexGaussianDist(data, 1.8 * 9 + 1, 2, 1);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double d_len = data.length;
+ for (int i = 0; i < data.length; i++)
+ data[i] *= (1.0 - (i / d_len));
+ datab = data;
+ }
+
+ // Make treble part
+ {
+ int fftlen = 4096;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024; i < 2048; i++)
+ data[i] = 1.0 - (i - 1024) / 1024.0;
+ for (int i = 0; i < 512; i++)
+ data[i] = 10 * i / 512.0;
+ for (int i = 0; i < 10; i++)
+ data[i] = 0;
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.999;
+ }
+ datah = data;
+ }
+
+ for (int i = 0; i < datah.length; i++)
+ datab[i] += datah[i] * 0.5;
+ for (int i = 0; i < 5; i++)
+ datab[i] *= i / 5.0;
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Bass Drum", datab);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Bass Drum");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_tom(SF2Soundbank sf2) {
+
+ double datab[];
+ double datah[];
+
+ // Make Bass Part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ complexGaussianDist(data, 30, 0.5, 1);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.8);
+ data = realPart(data);
+
+ double d_len = data.length;
+ for (int i = 0; i < data.length; i++)
+ data[i] *= (1.0 - (i / d_len));
+ datab = data;
+ }
+
+ // Make treble part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024 * 4; i < 2048 * 4; i++)
+ data[i] = 1.0 - (i - 4096) / 4096.0;
+ for (int i = 0; i < 200; i++) {
+ double g = (1.0 - (i / 200.0));
+ data[i] *= 1.0 + 20 * g * g;
+ }
+ for (int i = 0; i < 30; i++)
+ data[i] = 0;
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9996;
+ }
+ datah = data;
+ }
+
+ for (int i = 0; i < datah.length; i++)
+ datab[i] += datah[i] * 0.5;
+ for (int i = 0; i < 5; i++)
+ datab[i] *= i / 5.0;
+
+ normalize(datab, 0.99);
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Tom", datab);
+ sample.setOriginalPitch(50);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Tom");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ //region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -100);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_closed_hihat(SF2Soundbank sf2) {
+ double datah[];
+
+ // Make treble part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024 * 4; i < 2048 * 4; i++)
+ data[i] = 1.0 - (i - 4096) / 4096.0;
+ for (int i = 0; i < 2048; i++)
+ data[i] = 0.2 + 0.8 * (i / 2048.0);
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9996;
+ }
+ datah = data;
+ }
+
+ for (int i = 0; i < 5; i++)
+ datah[i] *= i / 5.0;
+ SF2Sample sample = newSimpleDrumSample(sf2, "Closed Hi-Hat", datah);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Closed Hi-Hat");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_EXCLUSIVECLASS, 1);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_open_hihat(SF2Soundbank sf2) {
+ double datah[];
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5));
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 0; i < 200; i++)
+ data[i] = 0;
+ for (int i = 0; i < 2048 * 4; i++) {
+ double gain = (i / (2048.0 * 4.0));
+ data[i] = gain;
+ }
+ datah = data;
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Open Hi-Hat", datah, 1000, 5);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Open Hi-Hat");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 1500);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 1500);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_EXCLUSIVECLASS, 1);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_crash_cymbal(SF2Soundbank sf2) {
+ double datah[];
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5));
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 0; i < 100; i++)
+ data[i] = 0;
+ for (int i = 0; i < 512 * 2; i++) {
+ double gain = (i / (512.0 * 2.0));
+ data[i] = gain;
+ }
+ datah = data;
+ }
+
+ SF2Sample sample = newSimpleFFTSample(sf2, "Crash Cymbal", datah, 1000, 5);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Crash Cymbal");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_DECAYVOLENV, 1800);
+ region.putInteger(SF2Region.GENERATOR_SAMPLEMODES, 1);
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 1800);
+ region.putInteger(SF2Region.GENERATOR_SUSTAINVOLENV, 1000);
+ region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+ }
+
+ public static SF2Layer new_side_stick(SF2Soundbank sf2) {
+ double datab[];
+
+ // Make treble part
+ {
+ int fftlen = 4096 * 4;
+ double[] data = new double[2 * fftlen];
+ Random random = new Random(3049912);
+ for (int i = 0; i < data.length; i += 2)
+ data[i] = (2.0 * (random.nextDouble() - 0.5)) * 0.1;
+ fft(data);
+ // Remove all negative frequency
+ for (int i = fftlen / 2; i < data.length; i++)
+ data[i] = 0;
+ for (int i = 1024 * 4; i < 2048 * 4; i++)
+ data[i] = 1.0 - (i - 4096) / 4096.0;
+ for (int i = 0; i < 200; i++) {
+ double g = (1.0 - (i / 200.0));
+ data[i] *= 1.0 + 20 * g * g;
+ }
+ for (int i = 0; i < 30; i++)
+ data[i] = 0;
+ randomPhase(data, new Random(3049912));
+ ifft(data);
+ normalize(data, 0.9);
+ data = realPart(data);
+ double gain = 1.0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= gain;
+ gain *= 0.9996;
+ }
+ datab = data;
+ }
+
+ for (int i = 0; i < 10; i++)
+ datab[i] *= i / 10.0;
+
+ SF2Sample sample = newSimpleDrumSample(sf2, "Side Stick", datab);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName("Side Stick");
+
+ SF2GlobalRegion global = new SF2GlobalRegion();
+ layer.setGlobalZone(global);
+ sf2.addResource(layer);
+
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.putInteger(SF2Region.GENERATOR_RELEASEVOLENV, 12000);
+ region.putInteger(SF2Region.GENERATOR_SCALETUNING, 0);
+ region.putInteger(SF2Region.GENERATOR_INITIALATTENUATION, -50);
+ region.setSample(sample);
+ layer.getRegions().add(region);
+
+ return layer;
+
+ }
+
+ public static SF2Sample newSimpleFFTSample(SF2Soundbank sf2, String name,
+ double[] data, double base) {
+ return newSimpleFFTSample(sf2, name, data, base, 10);
+ }
+
+ public static SF2Sample newSimpleFFTSample(SF2Soundbank sf2, String name,
+ double[] data, double base, int fadeuptime) {
+
+ int fftsize = data.length / 2;
+ AudioFormat format = new AudioFormat(44100, 16, 1, true, false);
+ double basefreq = (base / fftsize) * format.getSampleRate() * 0.5;
+
+ randomPhase(data);
+ ifft(data);
+ data = realPart(data);
+ normalize(data, 0.9);
+ float[] fdata = toFloat(data);
+ fdata = loopExtend(fdata, fdata.length + 512);
+ fadeUp(fdata, fadeuptime);
+ byte[] bdata = toBytes(fdata, format);
+
+ /*
+ * Create SoundFont2 sample.
+ */
+ SF2Sample sample = new SF2Sample(sf2);
+ sample.setName(name);
+ sample.setData(bdata);
+ sample.setStartLoop(256);
+ sample.setEndLoop(fftsize + 256);
+ sample.setSampleRate((long) format.getSampleRate());
+ double orgnote = (69 + 12)
+ + (12 * Math.log(basefreq / 440.0) / Math.log(2));
+ sample.setOriginalPitch((int) orgnote);
+ sample.setPitchCorrection((byte) (-(orgnote - (int) orgnote) * 100.0));
+ sf2.addResource(sample);
+
+ return sample;
+ }
+
+ public static SF2Sample newSimpleFFTSample_dist(SF2Soundbank sf2,
+ String name, double[] data, double base, double preamp) {
+
+ int fftsize = data.length / 2;
+ AudioFormat format = new AudioFormat(44100, 16, 1, true, false);
+ double basefreq = (base / fftsize) * format.getSampleRate() * 0.5;
+
+ randomPhase(data);
+ ifft(data);
+ data = realPart(data);
+
+ for (int i = 0; i < data.length; i++) {
+ data[i] = (1 - Math.exp(-Math.abs(data[i] * preamp)))
+ * Math.signum(data[i]);
+ }
+
+ normalize(data, 0.9);
+ float[] fdata = toFloat(data);
+ fdata = loopExtend(fdata, fdata.length + 512);
+ fadeUp(fdata, 80);
+ byte[] bdata = toBytes(fdata, format);
+
+ /*
+ * Create SoundFont2 sample.
+ */
+ SF2Sample sample = new SF2Sample(sf2);
+ sample.setName(name);
+ sample.setData(bdata);
+ sample.setStartLoop(256);
+ sample.setEndLoop(fftsize + 256);
+ sample.setSampleRate((long) format.getSampleRate());
+ double orgnote = (69 + 12)
+ + (12 * Math.log(basefreq / 440.0) / Math.log(2));
+ sample.setOriginalPitch((int) orgnote);
+ sample.setPitchCorrection((byte) (-(orgnote - (int) orgnote) * 100.0));
+ sf2.addResource(sample);
+
+ return sample;
+ }
+
+ public static SF2Sample newSimpleDrumSample(SF2Soundbank sf2, String name,
+ double[] data) {
+
+ int fftsize = data.length;
+ AudioFormat format = new AudioFormat(44100, 16, 1, true, false);
+
+ byte[] bdata = toBytes(toFloat(realPart(data)), format);
+
+ /*
+ * Create SoundFont2 sample.
+ */
+ SF2Sample sample = new SF2Sample(sf2);
+ sample.setName(name);
+ sample.setData(bdata);
+ sample.setStartLoop(256);
+ sample.setEndLoop(fftsize + 256);
+ sample.setSampleRate((long) format.getSampleRate());
+ sample.setOriginalPitch(60);
+ sf2.addResource(sample);
+
+ return sample;
+ }
+
+ public static SF2Layer newLayer(SF2Soundbank sf2, String name, SF2Sample sample) {
+ SF2LayerRegion region = new SF2LayerRegion();
+ region.setSample(sample);
+
+ SF2Layer layer = new SF2Layer(sf2);
+ layer.setName(name);
+ layer.getRegions().add(region);
+ sf2.addResource(layer);
+
+ return layer;
+ }
+
+ public static SF2Instrument newInstrument(SF2Soundbank sf2, String name,
+ Patch patch, SF2Layer... layers) {
+
+ /*
+ * Create SoundFont2 instrument.
+ */
+ SF2Instrument ins = new SF2Instrument(sf2);
+ ins.setPatch(patch);
+ ins.setName(name);
+ sf2.addInstrument(ins);
+
+ /*
+ * Create region for instrument.
+ */
+ for (int i = 0; i < layers.length; i++) {
+ SF2InstrumentRegion insregion = new SF2InstrumentRegion();
+ insregion.setLayer(layers[i]);
+ ins.getRegions().add(insregion);
+ }
+
+ return ins;
+ }
+
+ static public void ifft(double[] data) {
+ new FFT(data.length / 2, 1).transform(data);
+ }
+
+ static public void fft(double[] data) {
+ new FFT(data.length / 2, -1).transform(data);
+ }
+
+ public static void complexGaussianDist(double[] cdata, double m,
+ double s, double v) {
+ for (int x = 0; x < cdata.length / 4; x++) {
+ cdata[x * 2] += v * (1.0 / (s * Math.sqrt(2 * Math.PI))
+ * Math.exp((-1.0 / 2.0) * Math.pow((x - m) / s, 2.0)));
+ }
+ }
+
+ static public void randomPhase(double[] data) {
+ for (int i = 0; i < data.length; i += 2) {
+ double phase = Math.random() * 2 * Math.PI;
+ double d = data[i];
+ data[i] = Math.sin(phase) * d;
+ data[i + 1] = Math.cos(phase) * d;
+ }
+ }
+
+ static public void randomPhase(double[] data, Random random) {
+ for (int i = 0; i < data.length; i += 2) {
+ double phase = random.nextDouble() * 2 * Math.PI;
+ double d = data[i];
+ data[i] = Math.sin(phase) * d;
+ data[i + 1] = Math.cos(phase) * d;
+ }
+ }
+
+ static public void normalize(double[] data, double target) {
+ double maxvalue = 0;
+ for (int i = 0; i < data.length; i++) {
+ if (data[i] > maxvalue)
+ maxvalue = data[i];
+ if (-data[i] > maxvalue)
+ maxvalue = -data[i];
+ }
+ if (maxvalue == 0)
+ return;
+ double gain = target / maxvalue;
+ for (int i = 0; i < data.length; i++)
+ data[i] *= gain;
+ }
+
+ static public void normalize(float[] data, double target) {
+ double maxvalue = 0.5;
+ for (int i = 0; i < data.length; i++) {
+ if (data[i * 2] > maxvalue)
+ maxvalue = data[i * 2];
+ if (-data[i * 2] > maxvalue)
+ maxvalue = -data[i * 2];
+ }
+ double gain = target / maxvalue;
+ for (int i = 0; i < data.length; i++)
+ data[i * 2] *= gain;
+ }
+
+ static public double[] realPart(double[] in) {
+ double[] out = new double[in.length / 2];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = in[i * 2];
+ }
+ return out;
+ }
+
+ static public double[] imgPart(double[] in) {
+ double[] out = new double[in.length / 2];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = in[i * 2];
+ }
+ return out;
+ }
+
+ static public float[] toFloat(double[] in) {
+ float[] out = new float[in.length];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = (float) in[i];
+ }
+ return out;
+ }
+
+ static public byte[] toBytes(float[] in, AudioFormat format) {
+ byte[] out = new byte[in.length * format.getFrameSize()];
+ return AudioFloatConverter.getConverter(format).toByteArray(in, out);
+ }
+
+ static public void fadeUp(double[] data, int samples) {
+ double dsamples = samples;
+ for (int i = 0; i < samples; i++)
+ data[i] *= i / dsamples;
+ }
+
+ static public void fadeUp(float[] data, int samples) {
+ double dsamples = samples;
+ for (int i = 0; i < samples; i++)
+ data[i] *= i / dsamples;
+ }
+
+ static public double[] loopExtend(double[] data, int newsize) {
+ double[] outdata = new double[newsize];
+ int p_len = data.length;
+ int p_ps = 0;
+ for (int i = 0; i < outdata.length; i++) {
+ outdata[i] = data[p_ps];
+ p_ps++;
+ if (p_ps == p_len)
+ p_ps = 0;
+ }
+ return outdata;
+ }
+
+ static public float[] loopExtend(float[] data, int newsize) {
+ float[] outdata = new float[newsize];
+ int p_len = data.length;
+ int p_ps = 0;
+ for (int i = 0; i < outdata.length; i++) {
+ outdata[i] = data[p_ps];
+ p_ps++;
+ if (p_ps == p_len)
+ p_ps = 0;
+ }
+ return outdata;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/FFT.java b/src/share/classes/com/sun/media/sound/FFT.java
new file mode 100644
index 000000000..2e297e127
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/FFT.java
@@ -0,0 +1,748 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Fast Fourier Transformer.
+ *
+ * @author Karl Helgason
+ */
+public final class FFT {
+
+ private double[] w;
+ private int fftFrameSize;
+ private int sign;
+ private int[] bitm_array;
+ private int fftFrameSize2;
+
+ // Sign = -1 is FFT, 1 is IFFT (inverse FFT)
+ // Data = Interlaced double array to be transformed.
+ // The order is: real (sin), complex (cos)
+ // Framesize must be power of 2
+ public FFT(int fftFrameSize, int sign) {
+ w = computeTwiddleFactors(fftFrameSize, sign);
+
+ this.fftFrameSize = fftFrameSize;
+ this.sign = sign;
+ fftFrameSize2 = fftFrameSize << 1;
+
+ // Pre-process Bit-Reversal
+ bitm_array = new int[fftFrameSize2];
+ for (int i = 2; i < fftFrameSize2; i += 2) {
+ int j;
+ int bitm;
+ for (bitm = 2, j = 0; bitm < fftFrameSize2; bitm <<= 1) {
+ if ((i & bitm) != 0)
+ j++;
+ j <<= 1;
+ }
+ bitm_array[i] = j;
+ }
+
+ }
+
+ public void transform(double[] data) {
+ bitreversal(data);
+ calc(fftFrameSize, data, sign, w);
+ }
+
+ private final static double[] computeTwiddleFactors(int fftFrameSize,
+ int sign) {
+
+ int imax = (int) (Math.log(fftFrameSize) / Math.log(2.));
+
+ double[] warray = new double[(fftFrameSize - 1) * 4];
+ int w_index = 0;
+
+ for (int i = 0, nstep = 2; i < imax; i++) {
+ int jmax = nstep;
+ nstep <<= 1;
+
+ double wr = 1.0;
+ double wi = 0.0;
+
+ double arg = Math.PI / (jmax >> 1);
+ double wfr = Math.cos(arg);
+ double wfi = sign * Math.sin(arg);
+
+ for (int j = 0; j < jmax; j += 2) {
+ warray[w_index++] = wr;
+ warray[w_index++] = wi;
+
+ double tempr = wr;
+ wr = tempr * wfr - wi * wfi;
+ wi = tempr * wfi + wi * wfr;
+ }
+ }
+
+ // PRECOMPUTATION of wwr1, wwi1 for factor 4 Decomposition (3 * complex
+ // operators and 8 +/- complex operators)
+ {
+ w_index = 0;
+ int w_index2 = warray.length >> 1;
+ for (int i = 0, nstep = 2; i < (imax - 1); i++) {
+ int jmax = nstep;
+ nstep *= 2;
+
+ int ii = w_index + jmax;
+ for (int j = 0; j < jmax; j += 2) {
+ double wr = warray[w_index++];
+ double wi = warray[w_index++];
+ double wr1 = warray[ii++];
+ double wi1 = warray[ii++];
+ warray[w_index2++] = wr * wr1 - wi * wi1;
+ warray[w_index2++] = wr * wi1 + wi * wr1;
+ }
+ }
+
+ }
+
+ return warray;
+ }
+
+ private final static void calc(int fftFrameSize, double[] data, int sign,
+ double[] w) {
+
+ final int fftFrameSize2 = fftFrameSize << 1;
+
+ int nstep = 2;
+
+ if (nstep >= fftFrameSize2)
+ return;
+ int i = nstep - 2;
+ if (sign == -1)
+ calcF4F(fftFrameSize, data, i, nstep, w);
+ else
+ calcF4I(fftFrameSize, data, i, nstep, w);
+
+ }
+
+ private final static void calcF2E(int fftFrameSize, double[] data, int i,
+ int nstep, double[] w) {
+ int jmax = nstep;
+ for (int n = 0; n < jmax; n += 2) {
+ double wr = w[i++];
+ double wi = w[i++];
+ int m = n + jmax;
+ double datam_r = data[m];
+ double datam_i = data[m + 1];
+ double datan_r = data[n];
+ double datan_i = data[n + 1];
+ double tempr = datam_r * wr - datam_i * wi;
+ double tempi = datam_r * wi + datam_i * wr;
+ data[m] = datan_r - tempr;
+ data[m + 1] = datan_i - tempi;
+ data[n] = datan_r + tempr;
+ data[n + 1] = datan_i + tempi;
+ }
+ return;
+
+ }
+
+ // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
+ // complex operators
+ private final static void calcF4F(int fftFrameSize, double[] data, int i,
+ int nstep, double[] w) {
+ final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
+ // Factor-4 Decomposition
+
+ int w_len = w.length >> 1;
+ while (nstep < fftFrameSize2) {
+
+ if (nstep << 2 == fftFrameSize2) {
+ // Goto Factor-4 Final Decomposition
+ // calcF4E(data, i, nstep, -1, w);
+ calcF4FE(fftFrameSize, data, i, nstep, w);
+ return;
+ }
+ int jmax = nstep;
+ int nnstep = nstep << 1;
+ if (nnstep == fftFrameSize2) {
+ // Factor-4 Decomposition not possible
+ calcF2E(fftFrameSize, data, i, nstep, w);
+ return;
+ }
+ nstep <<= 2;
+ int ii = i + jmax;
+ int iii = i + w_len;
+
+ {
+ i += 2;
+ ii += 2;
+ iii += 2;
+
+ for (int n = 0; n < fftFrameSize2; n += nstep) {
+ int m = n + jmax;
+
+ double datam1_r = data[m];
+ double datam1_i = data[m + 1];
+ double datan1_r = data[n];
+ double datan1_i = data[n + 1];
+
+ n += nnstep;
+ m += nnstep;
+ double datam2_r = data[m];
+ double datam2_i = data[m + 1];
+ double datan2_r = data[n];
+ double datan2_i = data[n + 1];
+
+ double tempr = datam1_r;
+ double tempi = datam1_i;
+
+ datam1_r = datan1_r - tempr;
+ datam1_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ double n2w1r = datan2_r;
+ double n2w1i = datan2_i;
+ double m2ww1r = datam2_r;
+ double m2ww1i = datam2_i;
+
+ tempr = m2ww1r - n2w1r;
+ tempi = m2ww1i - n2w1i;
+
+ datam2_r = datam1_r + tempi;
+ datam2_i = datam1_i - tempr;
+ datam1_r = datam1_r - tempi;
+ datam1_i = datam1_i + tempr;
+
+ tempr = n2w1r + m2ww1r;
+ tempi = n2w1i + m2ww1i;
+
+ datan2_r = datan1_r - tempr;
+ datan2_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ data[m] = datam2_r;
+ data[m + 1] = datam2_i;
+ data[n] = datan2_r;
+ data[n + 1] = datan2_i;
+
+ n -= nnstep;
+ m -= nnstep;
+ data[m] = datam1_r;
+ data[m + 1] = datam1_i;
+ data[n] = datan1_r;
+ data[n + 1] = datan1_i;
+
+ }
+ }
+
+ for (int j = 2; j < jmax; j += 2) {
+ double wr = w[i++];
+ double wi = w[i++];
+ double wr1 = w[ii++];
+ double wi1 = w[ii++];
+ double wwr1 = w[iii++];
+ double wwi1 = w[iii++];
+ // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
+ // precomputed!!!
+ // double wwi1 = wr * wi1 + wi * wr1;
+
+ for (int n = j; n < fftFrameSize2; n += nstep) {
+ int m = n + jmax;
+
+ double datam1_r = data[m];
+ double datam1_i = data[m + 1];
+ double datan1_r = data[n];
+ double datan1_i = data[n + 1];
+
+ n += nnstep;
+ m += nnstep;
+ double datam2_r = data[m];
+ double datam2_i = data[m + 1];
+ double datan2_r = data[n];
+ double datan2_i = data[n + 1];
+
+ double tempr = datam1_r * wr - datam1_i * wi;
+ double tempi = datam1_r * wi + datam1_i * wr;
+
+ datam1_r = datan1_r - tempr;
+ datam1_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ double n2w1r = datan2_r * wr1 - datan2_i * wi1;
+ double n2w1i = datan2_r * wi1 + datan2_i * wr1;
+ double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
+ double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
+
+ tempr = m2ww1r - n2w1r;
+ tempi = m2ww1i - n2w1i;
+
+ datam2_r = datam1_r + tempi;
+ datam2_i = datam1_i - tempr;
+ datam1_r = datam1_r - tempi;
+ datam1_i = datam1_i + tempr;
+
+ tempr = n2w1r + m2ww1r;
+ tempi = n2w1i + m2ww1i;
+
+ datan2_r = datan1_r - tempr;
+ datan2_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ data[m] = datam2_r;
+ data[m + 1] = datam2_i;
+ data[n] = datan2_r;
+ data[n + 1] = datan2_i;
+
+ n -= nnstep;
+ m -= nnstep;
+ data[m] = datam1_r;
+ data[m + 1] = datam1_i;
+ data[n] = datan1_r;
+ data[n + 1] = datan1_i;
+ }
+ }
+
+ i += jmax << 1;
+
+ }
+
+ calcF2E(fftFrameSize, data, i, nstep, w);
+
+ }
+
+ // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
+ // complex operators
+ private final static void calcF4I(int fftFrameSize, double[] data, int i,
+ int nstep, double[] w) {
+ final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
+ // Factor-4 Decomposition
+
+ int w_len = w.length >> 1;
+ while (nstep < fftFrameSize2) {
+
+ if (nstep << 2 == fftFrameSize2) {
+ // Goto Factor-4 Final Decomposition
+ // calcF4E(data, i, nstep, 1, w);
+ calcF4IE(fftFrameSize, data, i, nstep, w);
+ return;
+ }
+ int jmax = nstep;
+ int nnstep = nstep << 1;
+ if (nnstep == fftFrameSize2) {
+ // Factor-4 Decomposition not possible
+ calcF2E(fftFrameSize, data, i, nstep, w);
+ return;
+ }
+ nstep <<= 2;
+ int ii = i + jmax;
+ int iii = i + w_len;
+ {
+ i += 2;
+ ii += 2;
+ iii += 2;
+
+ for (int n = 0; n < fftFrameSize2; n += nstep) {
+ int m = n + jmax;
+
+ double datam1_r = data[m];
+ double datam1_i = data[m + 1];
+ double datan1_r = data[n];
+ double datan1_i = data[n + 1];
+
+ n += nnstep;
+ m += nnstep;
+ double datam2_r = data[m];
+ double datam2_i = data[m + 1];
+ double datan2_r = data[n];
+ double datan2_i = data[n + 1];
+
+ double tempr = datam1_r;
+ double tempi = datam1_i;
+
+ datam1_r = datan1_r - tempr;
+ datam1_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ double n2w1r = datan2_r;
+ double n2w1i = datan2_i;
+ double m2ww1r = datam2_r;
+ double m2ww1i = datam2_i;
+
+ tempr = n2w1r - m2ww1r;
+ tempi = n2w1i - m2ww1i;
+
+ datam2_r = datam1_r + tempi;
+ datam2_i = datam1_i - tempr;
+ datam1_r = datam1_r - tempi;
+ datam1_i = datam1_i + tempr;
+
+ tempr = n2w1r + m2ww1r;
+ tempi = n2w1i + m2ww1i;
+
+ datan2_r = datan1_r - tempr;
+ datan2_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ data[m] = datam2_r;
+ data[m + 1] = datam2_i;
+ data[n] = datan2_r;
+ data[n + 1] = datan2_i;
+
+ n -= nnstep;
+ m -= nnstep;
+ data[m] = datam1_r;
+ data[m + 1] = datam1_i;
+ data[n] = datan1_r;
+ data[n + 1] = datan1_i;
+
+ }
+
+ }
+ for (int j = 2; j < jmax; j += 2) {
+ double wr = w[i++];
+ double wi = w[i++];
+ double wr1 = w[ii++];
+ double wi1 = w[ii++];
+ double wwr1 = w[iii++];
+ double wwi1 = w[iii++];
+ // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
+ // precomputed!!!
+ // double wwi1 = wr * wi1 + wi * wr1;
+
+ for (int n = j; n < fftFrameSize2; n += nstep) {
+ int m = n + jmax;
+
+ double datam1_r = data[m];
+ double datam1_i = data[m + 1];
+ double datan1_r = data[n];
+ double datan1_i = data[n + 1];
+
+ n += nnstep;
+ m += nnstep;
+ double datam2_r = data[m];
+ double datam2_i = data[m + 1];
+ double datan2_r = data[n];
+ double datan2_i = data[n + 1];
+
+ double tempr = datam1_r * wr - datam1_i * wi;
+ double tempi = datam1_r * wi + datam1_i * wr;
+
+ datam1_r = datan1_r - tempr;
+ datam1_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ double n2w1r = datan2_r * wr1 - datan2_i * wi1;
+ double n2w1i = datan2_r * wi1 + datan2_i * wr1;
+ double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
+ double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
+
+ tempr = n2w1r - m2ww1r;
+ tempi = n2w1i - m2ww1i;
+
+ datam2_r = datam1_r + tempi;
+ datam2_i = datam1_i - tempr;
+ datam1_r = datam1_r - tempi;
+ datam1_i = datam1_i + tempr;
+
+ tempr = n2w1r + m2ww1r;
+ tempi = n2w1i + m2ww1i;
+
+ datan2_r = datan1_r - tempr;
+ datan2_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ data[m] = datam2_r;
+ data[m + 1] = datam2_i;
+ data[n] = datan2_r;
+ data[n + 1] = datan2_i;
+
+ n -= nnstep;
+ m -= nnstep;
+ data[m] = datam1_r;
+ data[m + 1] = datam1_i;
+ data[n] = datan1_r;
+ data[n + 1] = datan1_i;
+
+ }
+ }
+
+ i += jmax << 1;
+
+ }
+
+ calcF2E(fftFrameSize, data, i, nstep, w);
+
+ }
+
+ // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
+ // complex operators
+ private final static void calcF4FE(int fftFrameSize, double[] data, int i,
+ int nstep, double[] w) {
+ final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
+ // Factor-4 Decomposition
+
+ int w_len = w.length >> 1;
+ while (nstep < fftFrameSize2) {
+
+ int jmax = nstep;
+ int nnstep = nstep << 1;
+ if (nnstep == fftFrameSize2) {
+ // Factor-4 Decomposition not possible
+ calcF2E(fftFrameSize, data, i, nstep, w);
+ return;
+ }
+ nstep <<= 2;
+ int ii = i + jmax;
+ int iii = i + w_len;
+ for (int n = 0; n < jmax; n += 2) {
+ double wr = w[i++];
+ double wi = w[i++];
+ double wr1 = w[ii++];
+ double wi1 = w[ii++];
+ double wwr1 = w[iii++];
+ double wwi1 = w[iii++];
+ // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
+ // precomputed!!!
+ // double wwi1 = wr * wi1 + wi * wr1;
+
+ int m = n + jmax;
+
+ double datam1_r = data[m];
+ double datam1_i = data[m + 1];
+ double datan1_r = data[n];
+ double datan1_i = data[n + 1];
+
+ n += nnstep;
+ m += nnstep;
+ double datam2_r = data[m];
+ double datam2_i = data[m + 1];
+ double datan2_r = data[n];
+ double datan2_i = data[n + 1];
+
+ double tempr = datam1_r * wr - datam1_i * wi;
+ double tempi = datam1_r * wi + datam1_i * wr;
+
+ datam1_r = datan1_r - tempr;
+ datam1_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ double n2w1r = datan2_r * wr1 - datan2_i * wi1;
+ double n2w1i = datan2_r * wi1 + datan2_i * wr1;
+ double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
+ double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
+
+ tempr = m2ww1r - n2w1r;
+ tempi = m2ww1i - n2w1i;
+
+ datam2_r = datam1_r + tempi;
+ datam2_i = datam1_i - tempr;
+ datam1_r = datam1_r - tempi;
+ datam1_i = datam1_i + tempr;
+
+ tempr = n2w1r + m2ww1r;
+ tempi = n2w1i + m2ww1i;
+
+ datan2_r = datan1_r - tempr;
+ datan2_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ data[m] = datam2_r;
+ data[m + 1] = datam2_i;
+ data[n] = datan2_r;
+ data[n + 1] = datan2_i;
+
+ n -= nnstep;
+ m -= nnstep;
+ data[m] = datam1_r;
+ data[m + 1] = datam1_i;
+ data[n] = datan1_r;
+ data[n + 1] = datan1_i;
+
+ }
+
+ i += jmax << 1;
+
+ }
+
+ }
+
+ // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
+ // complex operators
+ private final static void calcF4IE(int fftFrameSize, double[] data, int i,
+ int nstep, double[] w) {
+ final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
+ // Factor-4 Decomposition
+
+ int w_len = w.length >> 1;
+ while (nstep < fftFrameSize2) {
+
+ int jmax = nstep;
+ int nnstep = nstep << 1;
+ if (nnstep == fftFrameSize2) {
+ // Factor-4 Decomposition not possible
+ calcF2E(fftFrameSize, data, i, nstep, w);
+ return;
+ }
+ nstep <<= 2;
+ int ii = i + jmax;
+ int iii = i + w_len;
+ for (int n = 0; n < jmax; n += 2) {
+ double wr = w[i++];
+ double wi = w[i++];
+ double wr1 = w[ii++];
+ double wi1 = w[ii++];
+ double wwr1 = w[iii++];
+ double wwi1 = w[iii++];
+ // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
+ // precomputed!!!
+ // double wwi1 = wr * wi1 + wi * wr1;
+
+ int m = n + jmax;
+
+ double datam1_r = data[m];
+ double datam1_i = data[m + 1];
+ double datan1_r = data[n];
+ double datan1_i = data[n + 1];
+
+ n += nnstep;
+ m += nnstep;
+ double datam2_r = data[m];
+ double datam2_i = data[m + 1];
+ double datan2_r = data[n];
+ double datan2_i = data[n + 1];
+
+ double tempr = datam1_r * wr - datam1_i * wi;
+ double tempi = datam1_r * wi + datam1_i * wr;
+
+ datam1_r = datan1_r - tempr;
+ datam1_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ double n2w1r = datan2_r * wr1 - datan2_i * wi1;
+ double n2w1i = datan2_r * wi1 + datan2_i * wr1;
+ double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
+ double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
+
+ tempr = n2w1r - m2ww1r;
+ tempi = n2w1i - m2ww1i;
+
+ datam2_r = datam1_r + tempi;
+ datam2_i = datam1_i - tempr;
+ datam1_r = datam1_r - tempi;
+ datam1_i = datam1_i + tempr;
+
+ tempr = n2w1r + m2ww1r;
+ tempi = n2w1i + m2ww1i;
+
+ datan2_r = datan1_r - tempr;
+ datan2_i = datan1_i - tempi;
+ datan1_r = datan1_r + tempr;
+ datan1_i = datan1_i + tempi;
+
+ data[m] = datam2_r;
+ data[m + 1] = datam2_i;
+ data[n] = datan2_r;
+ data[n + 1] = datan2_i;
+
+ n -= nnstep;
+ m -= nnstep;
+ data[m] = datam1_r;
+ data[m + 1] = datam1_i;
+ data[n] = datan1_r;
+ data[n + 1] = datan1_i;
+
+ }
+
+ i += jmax << 1;
+
+ }
+
+ }
+
+ private final void bitreversal(double[] data) {
+ if (fftFrameSize < 4)
+ return;
+
+ int inverse = fftFrameSize2 - 2;
+ for (int i = 0; i < fftFrameSize; i += 4) {
+ int j = bitm_array[i];
+
+ // Performing Bit-Reversal, even v.s. even, O(2N)
+ if (i < j) {
+
+ int n = i;
+ int m = j;
+
+ // COMPLEX: SWAP(data[n], data[m])
+ // Real Part
+ double tempr = data[n];
+ data[n] = data[m];
+ data[m] = tempr;
+ // Imagery Part
+ n++;
+ m++;
+ double tempi = data[n];
+ data[n] = data[m];
+ data[m] = tempi;
+
+ n = inverse - i;
+ m = inverse - j;
+
+ // COMPLEX: SWAP(data[n], data[m])
+ // Real Part
+ tempr = data[n];
+ data[n] = data[m];
+ data[m] = tempr;
+ // Imagery Part
+ n++;
+ m++;
+ tempi = data[n];
+ data[n] = data[m];
+ data[m] = tempi;
+ }
+
+ // Performing Bit-Reversal, odd v.s. even, O(N)
+
+ int m = j + fftFrameSize; // bitm_array[i+2];
+ // COMPLEX: SWAP(data[n], data[m])
+ // Real Part
+ int n = i + 2;
+ double tempr = data[n];
+ data[n] = data[m];
+ data[m] = tempr;
+ // Imagery Part
+ n++;
+ m++;
+ double tempi = data[n];
+ data[n] = data[m];
+ data[m] = tempi;
+ }
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/InvalidDataException.java b/src/share/classes/com/sun/media/sound/InvalidDataException.java
new file mode 100644
index 000000000..ab46ef370
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/InvalidDataException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+
+/**
+ * This exception is used when a file contains illegal or unexpected data.
+ *
+ * @author Karl Helgason
+ */
+public class InvalidDataException extends IOException {
+
+ private static final long serialVersionUID = 1L;
+
+ public InvalidDataException() {
+ super("Invalid Data!");
+ }
+
+ public InvalidDataException(String s) {
+ super(s);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/InvalidFormatException.java b/src/share/classes/com/sun/media/sound/InvalidFormatException.java
new file mode 100644
index 000000000..96aaa0225
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/InvalidFormatException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This exception is used when a reader is used to read file of a format
+ * it doesn't unterstand or support.
+ *
+ * @author Karl Helgason
+ */
+public class InvalidFormatException extends InvalidDataException {
+
+ private static final long serialVersionUID = 1L;
+
+ public InvalidFormatException() {
+ super("Invalid format!");
+ }
+
+ public InvalidFormatException(String s) {
+ super(s);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/JARSoundbankReader.java b/src/share/classes/com/sun/media/sound/JARSoundbankReader.java
new file mode 100644
index 000000000..442fe72c3
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/JARSoundbankReader.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.spi.SoundbankReader;
+
+/**
+ * JarSoundbankReader is used to read sounbank object from jar files.
+ *
+ * @author Karl Helgason
+ */
+public class JARSoundbankReader extends SoundbankReader {
+
+ public boolean isZIP(URL url) {
+ boolean ok = false;
+ try {
+ InputStream stream = url.openStream();
+ try {
+ byte[] buff = new byte[4];
+ ok = stream.read(buff) == 4;
+ if (ok) {
+ ok = (buff[0] == 0x50
+ && buff[1] == 0x4b
+ && buff[2] == 0x03
+ && buff[3] == 0x04);
+ }
+ } finally {
+ stream.close();
+ }
+ } catch (IOException e) {
+ }
+ return ok;
+ }
+
+ public Soundbank getSoundbank(URL url)
+ throws InvalidMidiDataException, IOException {
+ if (!isZIP(url))
+ return null;
+ ArrayList<Soundbank> soundbanks = new ArrayList<Soundbank>();
+ URLClassLoader ucl = URLClassLoader.newInstance(new URL[]{url});
+ InputStream stream = ucl.getResourceAsStream(
+ "META-INF/services/javax.sound.midi.Soundbank");
+ if (stream == null)
+ return null;
+ try
+ {
+ BufferedReader r = new BufferedReader(new InputStreamReader(stream));
+ String line = r.readLine();
+ while (line != null) {
+ if (!line.startsWith("#")) {
+ try {
+ Class c = Class.forName(line.trim(), true, ucl);
+ Object o = c.newInstance();
+ if (o instanceof Soundbank) {
+ soundbanks.add((Soundbank) o);
+ }
+ } catch (ClassNotFoundException e) {
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ }
+ }
+ line = r.readLine();
+ }
+ }
+ finally
+ {
+ stream.close();
+ }
+ if (soundbanks.size() == 0)
+ return null;
+ if (soundbanks.size() == 1)
+ return soundbanks.get(0);
+ SimpleSoundbank sbk = new SimpleSoundbank();
+ for (Soundbank soundbank : soundbanks)
+ sbk.addAllInstruments(soundbank);
+ return sbk;
+ }
+
+ public Soundbank getSoundbank(InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ return null;
+ }
+
+ public Soundbank getSoundbank(File file)
+ throws InvalidMidiDataException, IOException {
+ return getSoundbank(file.toURI().toURL());
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelAbstractChannelMixer.java b/src/share/classes/com/sun/media/sound/ModelAbstractChannelMixer.java
new file mode 100644
index 000000000..37b14fe1a
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelAbstractChannelMixer.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * ModelAbstractChannelMixer is ready for use class to implement
+ * ModelChannelMixer interface.
+ *
+ * @author Karl Helgason
+ */
+public abstract class ModelAbstractChannelMixer implements ModelChannelMixer {
+
+ public abstract boolean process(float[][] buffer, int offset, int len);
+
+ public abstract void stop();
+
+ public void allNotesOff() {
+ }
+
+ public void allSoundOff() {
+ }
+
+ public void controlChange(int controller, int value) {
+ }
+
+ public int getChannelPressure() {
+ return 0;
+ }
+
+ public int getController(int controller) {
+ return 0;
+ }
+
+ public boolean getMono() {
+ return false;
+ }
+
+ public boolean getMute() {
+ return false;
+ }
+
+ public boolean getOmni() {
+ return false;
+ }
+
+ public int getPitchBend() {
+ return 0;
+ }
+
+ public int getPolyPressure(int noteNumber) {
+ return 0;
+ }
+
+ public int getProgram() {
+ return 0;
+ }
+
+ public boolean getSolo() {
+ return false;
+ }
+
+ public boolean localControl(boolean on) {
+ return false;
+ }
+
+ public void noteOff(int noteNumber) {
+ }
+
+ public void noteOff(int noteNumber, int velocity) {
+ }
+
+ public void noteOn(int noteNumber, int velocity) {
+ }
+
+ public void programChange(int program) {
+ }
+
+ public void programChange(int bank, int program) {
+ }
+
+ public void resetAllControllers() {
+ }
+
+ public void setChannelPressure(int pressure) {
+ }
+
+ public void setMono(boolean on) {
+ }
+
+ public void setMute(boolean mute) {
+ }
+
+ public void setOmni(boolean on) {
+ }
+
+ public void setPitchBend(int bend) {
+ }
+
+ public void setPolyPressure(int noteNumber, int pressure) {
+ }
+
+ public void setSolo(boolean soloState) {
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelAbstractOscillator.java b/src/share/classes/com/sun/media/sound/ModelAbstractOscillator.java
new file mode 100644
index 000000000..5d5c7e937
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelAbstractOscillator.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import javax.sound.midi.Instrument;
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.Patch;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.SoundbankResource;
+import javax.sound.midi.VoiceStatus;
+
+/**
+ * A abstract class used to simplify creating custom ModelOscillator.
+ *
+ * @author Karl Helgason
+ */
+public abstract class ModelAbstractOscillator
+ implements ModelOscillator, ModelOscillatorStream, Soundbank {
+
+ protected float pitch = 6000;
+ protected float samplerate;
+ protected MidiChannel channel;
+ protected VoiceStatus voice;
+ protected int noteNumber;
+ protected int velocity;
+ protected boolean on = false;
+
+ public void init() {
+ }
+
+ public void close() throws IOException {
+ }
+
+ public void noteOff(int velocity) {
+ on = false;
+ }
+
+ public void noteOn(MidiChannel channel, VoiceStatus voice, int noteNumber,
+ int velocity) {
+ this.channel = channel;
+ this.voice = voice;
+ this.noteNumber = noteNumber;
+ this.velocity = velocity;
+ on = true;
+ }
+
+ public int read(float[][] buffer, int offset, int len) throws IOException {
+ return -1;
+ }
+
+ public MidiChannel getChannel() {
+ return channel;
+ }
+
+ public VoiceStatus getVoice() {
+ return voice;
+ }
+
+ public int getNoteNumber() {
+ return noteNumber;
+ }
+
+ public int getVelocity() {
+ return velocity;
+ }
+
+ public boolean isOn() {
+ return on;
+ }
+
+ public void setPitch(float pitch) {
+ this.pitch = pitch;
+ }
+
+ public float getPitch() {
+ return pitch;
+ }
+
+ public void setSampleRate(float samplerate) {
+ this.samplerate = samplerate;
+ }
+
+ public float getSampleRate() {
+ return samplerate;
+ }
+
+ public float getAttenuation() {
+ return 0;
+ }
+
+ public int getChannels() {
+ return 1;
+ }
+
+ public String getName() {
+ return getClass().getName();
+ }
+
+ public Patch getPatch() {
+ return new Patch(0, 0);
+ }
+
+ public ModelOscillatorStream open(float samplerate) {
+ ModelAbstractOscillator oscs;
+ try {
+ oscs = this.getClass().newInstance();
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException(e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ }
+ oscs.setSampleRate(samplerate);
+ oscs.init();
+ return oscs;
+ }
+
+ public ModelPerformer getPerformer() {
+ // Create performer for my custom oscillirator
+ ModelPerformer performer = new ModelPerformer();
+ performer.getOscillators().add(this);
+ return performer;
+
+ }
+
+ public ModelInstrument getInstrument() {
+ // Create Instrument object around my performer
+ SimpleInstrument ins = new SimpleInstrument();
+ ins.setName(getName());
+ ins.add(getPerformer());
+ ins.setPatch(getPatch());
+ return ins;
+
+ }
+
+ public Soundbank getSoundBank() {
+ // Create Soundbank object around the instrument
+ SimpleSoundbank sbk = new SimpleSoundbank();
+ sbk.addInstrument(getInstrument());
+ return sbk;
+ }
+
+ public String getDescription() {
+ return getName();
+ }
+
+ public Instrument getInstrument(Patch patch) {
+ Instrument ins = getInstrument();
+ Patch p = ins.getPatch();
+ if (p.getBank() != patch.getBank())
+ return null;
+ if (p.getProgram() != patch.getProgram())
+ return null;
+ if (p instanceof ModelPatch && patch instanceof ModelPatch) {
+ if (((ModelPatch)p).isPercussion()
+ != ((ModelPatch)patch).isPercussion()) {
+ return null;
+ }
+ }
+ return ins;
+ }
+
+ public Instrument[] getInstruments() {
+ return new Instrument[]{getInstrument()};
+ }
+
+ public SoundbankResource[] getResources() {
+ return new SoundbankResource[0];
+ }
+
+ public String getVendor() {
+ return null;
+ }
+
+ public String getVersion() {
+ return null;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelByteBuffer.java b/src/share/classes/com/sun/media/sound/ModelByteBuffer.java
new file mode 100644
index 000000000..16c1125fb
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelByteBuffer.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Collection;
+
+/**
+ * This class is a pointer to a binary array either in memory or on disk.
+ *
+ * @author Karl Helgason
+ */
+public class ModelByteBuffer {
+
+ private ModelByteBuffer root = this;
+ private File file;
+ private long fileoffset;
+ private byte[] buffer;
+ private long offset;
+ private final long len;
+
+ private class RandomFileInputStream extends InputStream {
+
+ private RandomAccessFile raf;
+ private long left;
+ private long mark = 0;
+ private long markleft = 0;
+
+ public RandomFileInputStream() throws IOException {
+ raf = new RandomAccessFile(root.file, "r");
+ raf.seek(root.fileoffset + arrayOffset());
+ left = capacity();
+ }
+
+ public int available() throws IOException {
+ if (left > Integer.MAX_VALUE)
+ return Integer.MAX_VALUE;
+ return (int)left;
+ }
+
+ public synchronized void mark(int readlimit) {
+ try {
+ mark = raf.getFilePointer();
+ markleft = left;
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+ }
+
+ public boolean markSupported() {
+ return true;
+ }
+
+ public synchronized void reset() throws IOException {
+ raf.seek(mark);
+ left = markleft;
+ }
+
+ public long skip(long n) throws IOException {
+ if( n < 0)
+ return 0;
+ if (n > left)
+ n = left;
+ long p = raf.getFilePointer();
+ raf.seek(p + n);
+ left -= n;
+ return n;
+ }
+
+ public int read(byte b[], int off, int len) throws IOException {
+ if (len > left)
+ len = (int)left;
+ if (left == 0)
+ return -1;
+ len = raf.read(b, off, len);
+ if (len == -1)
+ return -1;
+ left -= len;
+ return len;
+ }
+
+ public int read(byte[] b) throws IOException {
+ int len = b.length;
+ if (len > left)
+ len = (int)left;
+ if (left == 0)
+ return -1;
+ len = raf.read(b, 0, len);
+ if (len == -1)
+ return -1;
+ left -= len;
+ return len;
+ }
+
+ public int read() throws IOException {
+ if (left == 0)
+ return -1;
+ int b = raf.read();
+ if (b == -1)
+ return -1;
+ left--;
+ return b;
+ }
+
+ public void close() throws IOException {
+ raf.close();
+ }
+ }
+
+ private ModelByteBuffer(ModelByteBuffer parent,
+ long beginIndex, long endIndex, boolean independent) {
+ this.root = parent.root;
+ this.offset = 0;
+ long parent_len = parent.len;
+ if (beginIndex < 0)
+ beginIndex = 0;
+ if (beginIndex > parent_len)
+ beginIndex = parent_len;
+ if (endIndex < 0)
+ endIndex = 0;
+ if (endIndex > parent_len)
+ endIndex = parent_len;
+ if (beginIndex > endIndex)
+ beginIndex = endIndex;
+ offset = beginIndex;
+ len = endIndex - beginIndex;
+ if (independent) {
+ buffer = root.buffer;
+ if (root.file != null) {
+ file = root.file;
+ fileoffset = root.fileoffset + arrayOffset();
+ offset = 0;
+ } else
+ offset = arrayOffset();
+ root = this;
+ }
+ }
+
+ public ModelByteBuffer(byte[] buffer) {
+ this.buffer = buffer;
+ this.offset = 0;
+ this.len = buffer.length;
+ }
+
+ public ModelByteBuffer(byte[] buffer, int offset, int len) {
+ this.buffer = buffer;
+ this.offset = offset;
+ this.len = len;
+ }
+
+ public ModelByteBuffer(File file) {
+ this.file = file;
+ this.fileoffset = 0;
+ this.len = file.length();
+ }
+
+ public ModelByteBuffer(File file, long offset, long len) {
+ this.file = file;
+ this.fileoffset = offset;
+ this.len = len;
+ }
+
+ public void writeTo(OutputStream out) throws IOException {
+ if (root.file != null && root.buffer == null) {
+ InputStream is = getInputStream();
+ byte[] buff = new byte[1024];
+ int ret;
+ while ((ret = is.read(buff)) != -1)
+ out.write(buff, 0, ret);
+ } else
+ out.write(array(), (int) arrayOffset(), (int) capacity());
+ }
+
+ public InputStream getInputStream() {
+ if (root.file != null && root.buffer == null) {
+ try {
+ return new RandomFileInputStream();
+ } catch (IOException e) {
+ //e.printStackTrace();
+ return null;
+ }
+ }
+ return new ByteArrayInputStream(array(),
+ (int)arrayOffset(), (int)capacity());
+ }
+
+ public ModelByteBuffer subbuffer(long beginIndex) {
+ return subbuffer(beginIndex, capacity());
+ }
+
+ public ModelByteBuffer subbuffer(long beginIndex, long endIndex) {
+ return subbuffer(beginIndex, endIndex, false);
+ }
+
+ public ModelByteBuffer subbuffer(long beginIndex, long endIndex,
+ boolean independent) {
+ return new ModelByteBuffer(this, beginIndex, endIndex, independent);
+ }
+
+ public byte[] array() {
+ return root.buffer;
+ }
+
+ public long arrayOffset() {
+ if (root != this)
+ return root.arrayOffset() + offset;
+ return offset;
+ }
+
+ public long capacity() {
+ return len;
+ }
+
+ public ModelByteBuffer getRoot() {
+ return root;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public long getFilePointer() {
+ return fileoffset;
+ }
+
+ public static void loadAll(Collection<ModelByteBuffer> col)
+ throws IOException {
+ File selfile = null;
+ RandomAccessFile raf = null;
+ try {
+ for (ModelByteBuffer mbuff : col) {
+ mbuff = mbuff.root;
+ if (mbuff.file == null)
+ continue;
+ if (mbuff.buffer != null)
+ continue;
+ if (selfile == null || !selfile.equals(mbuff.file)) {
+ if (raf != null) {
+ raf.close();
+ raf = null;
+ }
+ selfile = mbuff.file;
+ raf = new RandomAccessFile(mbuff.file, "r");
+ }
+ raf.seek(mbuff.fileoffset);
+ byte[] buffer = new byte[(int) mbuff.capacity()];
+
+ int read = 0;
+ int avail = buffer.length;
+ while (read != avail) {
+ if (avail - read > 65536) {
+ raf.readFully(buffer, read, 65536);
+ read += 65536;
+ } else {
+ raf.readFully(buffer, read, avail - read);
+ read = avail;
+ }
+
+ }
+
+ mbuff.buffer = buffer;
+ mbuff.offset = 0;
+ }
+ } finally {
+ if (raf != null)
+ raf.close();
+ }
+ }
+
+ public void load() throws IOException {
+ if (root != this) {
+ root.load();
+ return;
+ }
+ if (buffer != null)
+ return;
+ if (file == null) {
+ throw new IllegalStateException(
+ "No file associated with this ByteBuffer!");
+ }
+
+ DataInputStream is = new DataInputStream(getInputStream());
+ buffer = new byte[(int) capacity()];
+ offset = 0;
+ is.readFully(buffer);
+ is.close();
+
+ }
+
+ public void unload() {
+ if (root != this) {
+ root.unload();
+ return;
+ }
+ if (file == null) {
+ throw new IllegalStateException(
+ "No file associated with this ByteBuffer!");
+ }
+ root.buffer = null;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java b/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java
new file mode 100644
index 000000000..d7da52a76
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.AudioFormat.Encoding;
+
+/**
+ * Wavetable oscillator for pre-loaded data.
+ *
+ * @author Karl Helgason
+ */
+public class ModelByteBufferWavetable implements ModelWavetable {
+
+ private class Buffer8PlusInputStream extends InputStream {
+
+ private boolean bigendian;
+ private int framesize_pc;
+ int pos = 0;
+ int pos2 = 0;
+ int markpos = 0;
+ int markpos2 = 0;
+
+ public Buffer8PlusInputStream() {
+ framesize_pc = format.getFrameSize() / format.getChannels();
+ bigendian = format.isBigEndian();
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ int avail = available();
+ if (avail <= 0)
+ return -1;
+ if (len > avail)
+ len = avail;
+ byte[] buff1 = buffer.array();
+ byte[] buff2 = buffer8.array();
+ pos += buffer.arrayOffset();
+ pos2 += buffer8.arrayOffset();
+ if (bigendian) {
+ for (int i = 0; i < len; i += (framesize_pc + 1)) {
+ System.arraycopy(buff1, pos, b, i, framesize_pc);
+ System.arraycopy(buff2, pos2, b, i + framesize_pc, 1);
+ pos += framesize_pc;
+ pos2 += 1;
+ }
+ } else {
+ for (int i = 0; i < len; i += (framesize_pc + 1)) {
+ System.arraycopy(buff2, pos2, b, i, 1);
+ System.arraycopy(buff1, pos, b, i + 1, framesize_pc);
+ pos += framesize_pc;
+ pos2 += 1;
+ }
+ }
+ pos -= buffer.arrayOffset();
+ pos2 -= buffer8.arrayOffset();
+ return len;
+ }
+
+ public long skip(long n) throws IOException {
+ int avail = available();
+ if (avail <= 0)
+ return -1;
+ if (n > avail)
+ n = avail;
+ pos += (n / (framesize_pc + 1)) * (framesize_pc);
+ pos2 += n / (framesize_pc + 1);
+ return super.skip(n);
+ }
+
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ int ret = read(b, 0, 1);
+ if (ret == -1)
+ return -1;
+ return 0 & 0xFF;
+ }
+
+ public boolean markSupported() {
+ return true;
+ }
+
+ public int available() throws IOException {
+ return (int)buffer.capacity() + (int)buffer8.capacity() - pos - pos2;
+ }
+
+ public synchronized void mark(int readlimit) {
+ markpos = pos;
+ markpos2 = pos2;
+ }
+
+ public synchronized void reset() throws IOException {
+ pos = markpos;
+ pos2 = markpos2;
+
+ }
+ }
+
+ private float loopStart = -1;
+ private float loopLength = -1;
+ private ModelByteBuffer buffer;
+ private ModelByteBuffer buffer8 = null;
+ private AudioFormat format = null;
+ private float pitchcorrection = 0;
+ private float attenuation = 0;
+ private int loopType = LOOP_TYPE_OFF;
+
+ public ModelByteBufferWavetable(ModelByteBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ public ModelByteBufferWavetable(ModelByteBuffer buffer,
+ float pitchcorrection) {
+ this.buffer = buffer;
+ this.pitchcorrection = pitchcorrection;
+ }
+
+ public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format) {
+ this.format = format;
+ this.buffer = buffer;
+ }
+
+ public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format,
+ float pitchcorrection) {
+ this.format = format;
+ this.buffer = buffer;
+ this.pitchcorrection = pitchcorrection;
+ }
+
+ public void set8BitExtensionBuffer(ModelByteBuffer buffer) {
+ buffer8 = buffer;
+ }
+
+ public ModelByteBuffer get8BitExtensionBuffer() {
+ return buffer8;
+ }
+
+ public ModelByteBuffer getBuffer() {
+ return buffer;
+ }
+
+ public AudioFormat getFormat() {
+ if (format == null) {
+ if (buffer == null)
+ return null;
+ InputStream is = buffer.getInputStream();
+ AudioFormat format = null;
+ try {
+ format = AudioSystem.getAudioFileFormat(is).getFormat();
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ try {
+ is.close();
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+ return format;
+ }
+ return format;
+ }
+
+ public AudioFloatInputStream openStream() {
+ if (buffer == null)
+ return null;
+ if (format == null) {
+ InputStream is = buffer.getInputStream();
+ AudioInputStream ais = null;
+ try {
+ ais = AudioSystem.getAudioInputStream(is);
+ } catch (Exception e) {
+ //e.printStackTrace();
+ return null;
+ }
+ return AudioFloatInputStream.getInputStream(ais);
+ }
+ if (buffer.array() == null) {
+ return AudioFloatInputStream.getInputStream(new AudioInputStream(
+ buffer.getInputStream(), format, buffer.capacity()));
+ }
+ if (buffer8 != null) {
+ if (format.getEncoding().equals(Encoding.PCM_SIGNED)
+ || format.getEncoding().equals(Encoding.PCM_UNSIGNED)) {
+ InputStream is = new Buffer8PlusInputStream();
+ AudioFormat format2 = new AudioFormat(
+ format.getEncoding(),
+ format.getSampleRate(),
+ format.getSampleSizeInBits() + 8,
+ format.getChannels(),
+ format.getFrameSize() + (1 * format.getChannels()),
+ format.getFrameRate(),
+ format.isBigEndian());
+
+ AudioInputStream ais = new AudioInputStream(is, format2,
+ buffer.capacity() / format.getFrameSize());
+ return AudioFloatInputStream.getInputStream(ais);
+ }
+ }
+ return AudioFloatInputStream.getInputStream(format, buffer.array(),
+ (int)buffer.arrayOffset(), (int)buffer.capacity());
+ }
+
+ public int getChannels() {
+ return getFormat().getChannels();
+ }
+
+ public ModelOscillatorStream open(float samplerate) {
+ // ModelWavetableOscillator doesn't support ModelOscillatorStream
+ return null;
+ }
+
+ // attenuation is in cB
+ public float getAttenuation() {
+ return attenuation;
+ }
+ // attenuation is in cB
+ public void setAttenuation(float attenuation) {
+ this.attenuation = attenuation;
+ }
+
+ public float getLoopLength() {
+ return loopLength;
+ }
+
+ public void setLoopLength(float loopLength) {
+ this.loopLength = loopLength;
+ }
+
+ public float getLoopStart() {
+ return loopStart;
+ }
+
+ public void setLoopStart(float loopStart) {
+ this.loopStart = loopStart;
+ }
+
+ public void setLoopType(int loopType) {
+ this.loopType = loopType;
+ }
+
+ public int getLoopType() {
+ return loopType;
+ }
+
+ public float getPitchcorrection() {
+ return pitchcorrection;
+ }
+
+ public void setPitchcorrection(float pitchcorrection) {
+ this.pitchcorrection = pitchcorrection;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelChannelMixer.java b/src/share/classes/com/sun/media/sound/ModelChannelMixer.java
new file mode 100644
index 000000000..1f6dfd6e9
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelChannelMixer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.MidiChannel;
+
+/**
+ * ModelChannelMixer is used to process channel voice mix output before going
+ * to master output.<br>
+ * It can be used to:<br>
+ * <ul>
+ * <li>Implement non-voice oriented instruments.</li>
+ * <li>Add insert effect to instruments; for example distortion effect.</li>
+ * </ui>
+ * <p>
+ * <b>Warning! Classes that implements ModelChannelMixer must be thread-safe.</b>
+ *
+ * @author Karl Helgason
+ */
+public interface ModelChannelMixer extends MidiChannel {
+
+ // Used to process input audio from voices mix.
+ public boolean process(float[][] buffer, int offset, int len);
+
+ // Is used to trigger that this mixer is not be used
+ // and it should fade out.
+ public void stop();
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelConnectionBlock.java b/src/share/classes/com/sun/media/sound/ModelConnectionBlock.java
new file mode 100644
index 000000000..349d6b42b
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelConnectionBlock.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Connection blocks are used to connect source variable
+ * to a destination variable.
+ * For example Note On velocity can be connected to output gain.
+ * In DLS this is called articulator and in SoundFonts (SF2) a modulator.
+ *
+ * @author Karl Helgason
+ */
+public class ModelConnectionBlock {
+
+ //
+ // source1 * source2 * scale -> destination
+ //
+ private final static ModelSource[] no_sources = new ModelSource[0];
+ private ModelSource[] sources = no_sources;
+ private double scale = 1;
+ private ModelDestination destination;
+
+ public ModelConnectionBlock() {
+ }
+
+ public ModelConnectionBlock(double scale, ModelDestination destination) {
+ this.scale = scale;
+ this.destination = destination;
+ }
+
+ public ModelConnectionBlock(ModelSource source,
+ ModelDestination destination) {
+ if (source != null) {
+ this.sources = new ModelSource[1];
+ this.sources[0] = source;
+ }
+ this.destination = destination;
+ }
+
+ public ModelConnectionBlock(ModelSource source, double scale,
+ ModelDestination destination) {
+ if (source != null) {
+ this.sources = new ModelSource[1];
+ this.sources[0] = source;
+ }
+ this.scale = scale;
+ this.destination = destination;
+ }
+
+ public ModelConnectionBlock(ModelSource source, ModelSource control,
+ ModelDestination destination) {
+ if (source != null) {
+ if (control == null) {
+ this.sources = new ModelSource[1];
+ this.sources[0] = source;
+ } else {
+ this.sources = new ModelSource[2];
+ this.sources[0] = source;
+ this.sources[1] = control;
+ }
+ }
+ this.destination = destination;
+ }
+
+ public ModelConnectionBlock(ModelSource source, ModelSource control,
+ double scale, ModelDestination destination) {
+ if (source != null) {
+ if (control == null) {
+ this.sources = new ModelSource[1];
+ this.sources[0] = source;
+ } else {
+ this.sources = new ModelSource[2];
+ this.sources[0] = source;
+ this.sources[1] = control;
+ }
+ }
+ this.scale = scale;
+ this.destination = destination;
+ }
+
+ public ModelDestination getDestination() {
+ return destination;
+ }
+
+ public void setDestination(ModelDestination destination) {
+ this.destination = destination;
+ }
+
+ public double getScale() {
+ return scale;
+ }
+
+ public void setScale(double scale) {
+ this.scale = scale;
+ }
+
+ public ModelSource[] getSources() {
+ return sources;
+ }
+
+ public void setSources(ModelSource[] source) {
+ this.sources = source;
+ }
+
+ public void addSource(ModelSource source) {
+ ModelSource[] oldsources = sources;
+ sources = new ModelSource[oldsources.length + 1];
+ for (int i = 0; i < oldsources.length; i++) {
+ sources[i] = oldsources[i];
+ }
+ sources[sources.length - 1] = source;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelDestination.java b/src/share/classes/com/sun/media/sound/ModelDestination.java
new file mode 100644
index 000000000..e8c1db41e
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelDestination.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This class is used to identify destinations in connection blocks,
+ * see ModelConnectionBlock.
+ *
+ * @author Karl Helgason
+ */
+public class ModelDestination {
+
+ public static final ModelIdentifier DESTINATION_NONE = null;
+ public static final ModelIdentifier DESTINATION_KEYNUMBER
+ = new ModelIdentifier("noteon", "keynumber");
+ public static final ModelIdentifier DESTINATION_VELOCITY
+ = new ModelIdentifier("noteon", "velocity");
+ public static final ModelIdentifier DESTINATION_PITCH
+ = new ModelIdentifier("osc", "pitch"); // cent
+ public static final ModelIdentifier DESTINATION_GAIN
+ = new ModelIdentifier("mixer", "gain"); // cB
+ public static final ModelIdentifier DESTINATION_PAN
+ = new ModelIdentifier("mixer", "pan"); // 0.1 %
+ public static final ModelIdentifier DESTINATION_REVERB
+ = new ModelIdentifier("mixer", "reverb"); // 0.1 %
+ public static final ModelIdentifier DESTINATION_CHORUS
+ = new ModelIdentifier("mixer", "chorus"); // 0.1 %
+ public static final ModelIdentifier DESTINATION_LFO1_DELAY
+ = new ModelIdentifier("lfo", "delay", 0); // timecent
+ public static final ModelIdentifier DESTINATION_LFO1_FREQ
+ = new ModelIdentifier("lfo", "freq", 0); // cent
+ public static final ModelIdentifier DESTINATION_LFO2_DELAY
+ = new ModelIdentifier("lfo", "delay", 1); // timecent
+ public static final ModelIdentifier DESTINATION_LFO2_FREQ
+ = new ModelIdentifier("lfo", "freq", 1); // cent
+ public static final ModelIdentifier DESTINATION_EG1_DELAY
+ = new ModelIdentifier("eg", "delay", 0); // timecent
+ public static final ModelIdentifier DESTINATION_EG1_ATTACK
+ = new ModelIdentifier("eg", "attack", 0); // timecent
+ public static final ModelIdentifier DESTINATION_EG1_HOLD
+ = new ModelIdentifier("eg", "hold", 0); // timecent
+ public static final ModelIdentifier DESTINATION_EG1_DECAY
+ = new ModelIdentifier("eg", "decay", 0); // timecent
+ public static final ModelIdentifier DESTINATION_EG1_SUSTAIN
+ = new ModelIdentifier("eg", "sustain", 0);
+ // 0.1 % (I want this to be value not %)
+ public static final ModelIdentifier DESTINATION_EG1_RELEASE
+ = new ModelIdentifier("eg", "release", 0); // timecent
+ public static final ModelIdentifier DESTINATION_EG1_SHUTDOWN
+ = new ModelIdentifier("eg", "shutdown", 0); // timecent
+ public static final ModelIdentifier DESTINATION_EG2_DELAY
+ = new ModelIdentifier("eg", "delay", 1); // timecent
+ public static final ModelIdentifier DESTINATION_EG2_ATTACK
+ = new ModelIdentifier("eg", "attack", 1); // timecent
+ public static final ModelIdentifier DESTINATION_EG2_HOLD
+ = new ModelIdentifier("eg", "hold", 1); // 0.1 %
+ public static final ModelIdentifier DESTINATION_EG2_DECAY
+ = new ModelIdentifier("eg", "decay", 1); // timecent
+ public static final ModelIdentifier DESTINATION_EG2_SUSTAIN
+ = new ModelIdentifier("eg", "sustain", 1);
+ // 0.1 % ( I want this to be value not %)
+ public static final ModelIdentifier DESTINATION_EG2_RELEASE
+ = new ModelIdentifier("eg", "release", 1); // timecent
+ public static final ModelIdentifier DESTINATION_EG2_SHUTDOWN
+ = new ModelIdentifier("eg", "shutdown", 1); // timecent
+ public static final ModelIdentifier DESTINATION_FILTER_FREQ
+ = new ModelIdentifier("filter", "freq", 0); // cent
+ public static final ModelIdentifier DESTINATION_FILTER_Q
+ = new ModelIdentifier("filter", "q", 0); // cB
+ private ModelIdentifier destination = DESTINATION_NONE;
+ private ModelTransform transform = new ModelStandardTransform();
+
+ public ModelDestination() {
+ }
+
+ public ModelDestination(ModelIdentifier id) {
+ destination = id;
+ }
+
+ public ModelIdentifier getIdentifier() {
+ return destination;
+ }
+
+ public void setIdentifier(ModelIdentifier destination) {
+ this.destination = destination;
+ }
+
+ public ModelTransform getTransform() {
+ return transform;
+ }
+
+ public void setTransform(ModelTransform transform) {
+ this.transform = transform;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelDirectedPlayer.java b/src/share/classes/com/sun/media/sound/ModelDirectedPlayer.java
new file mode 100644
index 000000000..cdcfda5f3
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelDirectedPlayer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * ModelDirectedPlayer is the one who is directed by ModelDirector
+ * to play ModelPerformer objects.
+ *
+ * @author Karl Helgason
+ */
+public interface ModelDirectedPlayer {
+
+ public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks);
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelDirector.java b/src/share/classes/com/sun/media/sound/ModelDirector.java
new file mode 100644
index 000000000..c7e7e914f
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelDirector.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A director chooses what performers should be played for each note on
+ * and note off events.
+ *
+ * ModelInstrument can implement custom performer who chooses what performers
+ * to play for example by sustain pedal is off or on.
+ *
+ * The default director (ModelStandardDirector) chooses performers
+ * by there keyfrom,keyto,velfrom,velto properties.
+ *
+ * @author Karl Helgason
+ */
+public interface ModelDirector {
+
+ public void noteOn(int noteNumber, int velocity);
+
+ public void noteOff(int noteNumber, int velocity);
+
+ public void close();
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelIdentifier.java b/src/share/classes/com/sun/media/sound/ModelIdentifier.java
new file mode 100644
index 000000000..9900a679f
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelIdentifier.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This class stores the identity of source and destinations in connection
+ * blocks, see ModelConnectionBlock.
+ *
+ * @author Karl Helgason
+ */
+public class ModelIdentifier {
+
+ /*
+ * Object Variable
+ * ------ --------
+ *
+ * // INPUT parameters
+ * noteon keynumber 7 bit midi value
+ * velocity 7 bit midi vale
+ * on 1 or 0
+ *
+ * midi pitch 14 bit midi value
+ * channel_pressure 7 bit midi value
+ * poly_pressure 7 bit midi value
+ *
+ * midi_cc 0 (midi control #0 7 bit midi value
+ * 1 (midi control #1 7 bit midi value
+ * ...
+ * 127 (midi control #127 7 bit midi value
+ *
+ * midi_rpn 0 (midi rpn control #0) 14 bit midi value
+ * 1 (midi rpn control #1) 14 bit midi value
+ * ....
+ *
+ * // DAHDSR envelope generator
+ * eg (null)
+ * delay timecent
+ * attack timecent
+ * hold timecent
+ * decay timecent
+ * sustain 0.1 %
+ * release timecent
+ *
+ * // Low frequency oscillirator (sine wave)
+ * lfo (null)
+ * delay timcent
+ * freq cent
+ *
+ * // Resonance LowPass Filter 6dB slope
+ * filter (null) (output/input)
+ * freq cent
+ * q cB
+ *
+ * // The oscillator with preloaded wavetable data
+ * osc (null)
+ * pitch cent
+ *
+ * // Output mixer pins
+ * mixer gain cB
+ * pan 0.1 %
+ * reverb 0.1 %
+ * chorus 0.1 %
+ *
+ */
+ private String object = null;
+ private String variable = null;
+ private int instance = 0;
+
+ public ModelIdentifier(String object) {
+ this.object = object;
+ }
+
+ public ModelIdentifier(String object, int instance) {
+ this.object = object;
+ this.instance = instance;
+ }
+
+ public ModelIdentifier(String object, String variable) {
+ this.object = object;
+ this.variable = variable;
+
+ }
+
+ public ModelIdentifier(String object, String variable, int instance) {
+ this.object = object;
+ this.variable = variable;
+ this.instance = instance;
+
+ }
+
+ public int getInstance() {
+ return instance;
+ }
+
+ public void setInstance(int instance) {
+ this.instance = instance;
+ }
+
+ public String getObject() {
+ return object;
+ }
+
+ public void setObject(String object) {
+ this.object = object;
+ }
+
+ public String getVariable() {
+ return variable;
+ }
+
+ public void setVariable(String variable) {
+ this.variable = variable;
+ }
+
+ public int hashCode() {
+ int hashcode = instance;
+ if(object != null) hashcode |= object.hashCode();
+ if(variable != null) hashcode |= variable.hashCode();
+ return hashcode;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ModelIdentifier))
+ return false;
+
+ ModelIdentifier mobj = (ModelIdentifier)obj;
+ if ((object == null) != (mobj.object == null))
+ return false;
+ if ((variable == null) != (mobj.variable == null))
+ return false;
+ if (mobj.getInstance() != getInstance())
+ return false;
+ if (!(object == null || object.equals(mobj.object)))
+ return false;
+ if (!(variable == null || variable.equals(mobj.variable)))
+ return false;
+ return true;
+ }
+
+ public String toString() {
+ if (variable == null) {
+ return object + "[" + instance + "]";
+ } else {
+ return object + "[" + instance + "]" + "." + variable;
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelInstrument.java b/src/share/classes/com/sun/media/sound/ModelInstrument.java
new file mode 100644
index 000000000..37d0e583e
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelInstrument.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.Instrument;
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.Patch;
+import javax.sound.midi.Soundbank;
+import javax.sound.sampled.AudioFormat;
+
+/**
+ * The model instrument class.
+ *
+ * <p>The main methods to override are:<br>
+ * getPerformer, getDirector, getChannelMixer.
+ *
+ * <p>Performers are used to define what voices which will
+ * playback when using the instrument.<br>
+ *
+ * ChannelMixer is used to add channel-wide processing
+ * on voices output or to define non-voice oriented instruments.<br>
+ *
+ * Director is used to change how the synthesizer
+ * chooses what performers to play on midi events.
+ *
+ * @author Karl Helgason
+ */
+public abstract class ModelInstrument extends Instrument {
+
+ protected ModelInstrument(Soundbank soundbank, Patch patch, String name,
+ Class<?> dataClass) {
+ super(soundbank, patch, name, dataClass);
+ }
+
+ public ModelDirector getDirector(ModelPerformer[] performers,
+ MidiChannel channel, ModelDirectedPlayer player) {
+ return new ModelStandardDirector(performers, player);
+ }
+
+ public ModelPerformer[] getPerformers() {
+ return new ModelPerformer[0];
+ }
+
+ public ModelChannelMixer getChannelMixer(MidiChannel channel,
+ AudioFormat format) {
+ return null;
+ }
+
+ // Get General MIDI 2 Alias patch for this instrument.
+ public Patch getPatchAlias() {
+ Patch patch = getPatch();
+ int program = patch.getProgram();
+ int bank = patch.getBank();
+ if (bank != 0)
+ return patch;
+ boolean percussion = false;
+ if (getPatch() instanceof ModelPatch)
+ percussion = ((ModelPatch)getPatch()).isPercussion();
+ if (percussion)
+ return new Patch(0x78 << 7, program);
+ else
+ return new Patch(0x79 << 7, program);
+ }
+
+ // Return name of all the keys.
+ // This information is generated from ModelPerformer.getName()
+ // returned from getPerformers().
+ public String[] getKeys() {
+ String[] keys = new String[128];
+ for (ModelPerformer performer : getPerformers()) {
+ for (int k = performer.getKeyFrom(); k <= performer.getKeyTo(); k++) {
+ if (k >= 0 && k < 128 && keys[k] == null) {
+ String name = performer.getName();
+ if (name == null)
+ name = "untitled";
+ keys[k] = name;
+ }
+ }
+ }
+ return keys;
+ }
+
+ // Return what channels this instrument will probably response
+ // on General MIDI synthesizer.
+ public boolean[] getChannels() {
+ boolean percussion = false;
+ if (getPatch() instanceof ModelPatch)
+ percussion = ((ModelPatch)getPatch()).isPercussion();
+
+ // Check if instrument is percussion.
+ if (percussion) {
+ boolean[] ch = new boolean[16];
+ for (int i = 0; i < ch.length; i++)
+ ch[i] = false;
+ ch[9] = true;
+ return ch;
+ }
+
+ // Check if instrument uses General MIDI 2 default banks.
+ int bank = getPatch().getBank();
+ if (bank >> 7 == 0x78 || bank >> 7 == 0x79) {
+ boolean[] ch = new boolean[16];
+ for (int i = 0; i < ch.length; i++)
+ ch[i] = true;
+ return ch;
+ }
+
+ boolean[] ch = new boolean[16];
+ for (int i = 0; i < ch.length; i++)
+ ch[i] = true;
+ ch[9] = false;
+ return ch;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelInstrumentComparator.java b/src/share/classes/com/sun/media/sound/ModelInstrumentComparator.java
new file mode 100644
index 000000000..7268971f0
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelInstrumentComparator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.Comparator;
+import javax.sound.midi.Instrument;
+import javax.sound.midi.Patch;
+
+/**
+ * Instrument comparator class.
+ * Used to order instrument by program, bank, percussion.
+ *
+ * @author Karl Helgason
+ */
+public class ModelInstrumentComparator implements Comparator<Instrument> {
+
+ public int compare(Instrument arg0, Instrument arg1) {
+ Patch p0 = arg0.getPatch();
+ Patch p1 = arg1.getPatch();
+ int a = p0.getBank() * 128 + p0.getProgram();
+ int b = p1.getBank() * 128 + p1.getProgram();
+ if (p0 instanceof ModelPatch) {
+ a += ((ModelPatch)p0).isPercussion() ? 2097152 : 0;
+ }
+ if (p1 instanceof ModelPatch) {
+ b += ((ModelPatch)p1).isPercussion() ? 2097152 : 0;
+ }
+ return a - b;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelMappedInstrument.java b/src/share/classes/com/sun/media/sound/ModelMappedInstrument.java
new file mode 100644
index 000000000..6064852de
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelMappedInstrument.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.Patch;
+import javax.sound.sampled.AudioFormat;
+
+/**
+ * This class is used to map instrument to another patch.
+ *
+ * @author Karl Helgason
+ */
+public class ModelMappedInstrument extends ModelInstrument {
+
+ private ModelInstrument ins;
+
+ public ModelMappedInstrument(ModelInstrument ins, Patch patch) {
+ super(ins.getSoundbank(), patch, ins.getName(), ins.getDataClass());
+ this.ins = ins;
+ }
+
+ public Object getData() {
+ return ins.getData();
+ }
+
+ public ModelPerformer[] getPerformers() {
+ return ins.getPerformers();
+ }
+
+ public ModelDirector getDirector(ModelPerformer[] performers,
+ MidiChannel channel, ModelDirectedPlayer player) {
+ return ins.getDirector(performers, channel, player);
+ }
+
+ public ModelChannelMixer getChannelMixer(MidiChannel channel,
+ AudioFormat format) {
+ return ins.getChannelMixer(channel, format);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelOscillator.java b/src/share/classes/com/sun/media/sound/ModelOscillator.java
new file mode 100644
index 000000000..3312473d2
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelOscillator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This interface is used for oscillators.
+ * See example in ModelDefaultOscillator which is a wavetable oscillator.
+ *
+ * @author Karl Helgason
+ */
+public interface ModelOscillator {
+
+ public int getChannels();
+
+ /**
+ * Attenuation is in cB.
+ * @return
+ */
+ public float getAttenuation();
+
+ public ModelOscillatorStream open(float samplerate);
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelOscillatorStream.java b/src/share/classes/com/sun/media/sound/ModelOscillatorStream.java
new file mode 100644
index 000000000..10ec5da46
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelOscillatorStream.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.VoiceStatus;
+
+/**
+ * This interface is used for audio streams from ModelOscillator.
+ *
+ * @author Karl Helgason
+ */
+public interface ModelOscillatorStream {
+
+ public void setPitch(float pitch); // Pitch is in cents!
+
+ public void noteOn(MidiChannel channel, VoiceStatus voice, int noteNumber,
+ int velocity);
+
+ public void noteOff(int velocity);
+
+ public int read(float[][] buffer, int offset, int len) throws IOException;
+
+ public void close() throws IOException;
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelPatch.java b/src/share/classes/com/sun/media/sound/ModelPatch.java
new file mode 100644
index 000000000..6b4c5b01d
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelPatch.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.Patch;
+
+/**
+ * A extended patch object that has isPercussion function.
+ * Which is necessary to identify percussion instruments
+ * from melodic instruments.
+ *
+ * @author Karl Helgason
+ */
+public class ModelPatch extends Patch {
+
+ private boolean percussion = false;
+
+ public ModelPatch(int bank, int program) {
+ super(bank, program);
+ }
+
+ public ModelPatch(int bank, int program, boolean percussion) {
+ super(bank, program);
+ this.percussion = percussion;
+ }
+
+ public boolean isPercussion() {
+ return percussion;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelPerformer.java b/src/share/classes/com/sun/media/sound/ModelPerformer.java
new file mode 100644
index 000000000..6ba48674c
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelPerformer.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to define how to synthesize audio in universal maner
+ * for both SF2 and DLS instruments.
+ *
+ * @author Karl Helgason
+ */
+public class ModelPerformer {
+
+ private List<ModelOscillator> oscillators = new ArrayList<ModelOscillator>();
+ private List<ModelConnectionBlock> connectionBlocks
+ = new ArrayList<ModelConnectionBlock>();
+ private int keyFrom = 0;
+ private int keyTo = 127;
+ private int velFrom = 0;
+ private int velTo = 127;
+ private int exclusiveClass = 0;
+ private boolean releaseTrigger = false;
+ private boolean selfNonExclusive = false;
+ private Object userObject = null;
+ private boolean addDefaultConnections = true;
+ private String name = null;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<ModelConnectionBlock> getConnectionBlocks() {
+ return connectionBlocks;
+ }
+
+ public void setConnectionBlocks(List<ModelConnectionBlock> connectionBlocks) {
+ this.connectionBlocks = connectionBlocks;
+ }
+
+ public List<ModelOscillator> getOscillators() {
+ return oscillators;
+ }
+
+ public int getExclusiveClass() {
+ return exclusiveClass;
+ }
+
+ public void setExclusiveClass(int exclusiveClass) {
+ this.exclusiveClass = exclusiveClass;
+ }
+
+ public boolean isSelfNonExclusive() {
+ return selfNonExclusive;
+ }
+
+ public void setSelfNonExclusive(boolean selfNonExclusive) {
+ this.selfNonExclusive = selfNonExclusive;
+ }
+
+ public int getKeyFrom() {
+ return keyFrom;
+ }
+
+ public void setKeyFrom(int keyFrom) {
+ this.keyFrom = keyFrom;
+ }
+
+ public int getKeyTo() {
+ return keyTo;
+ }
+
+ public void setKeyTo(int keyTo) {
+ this.keyTo = keyTo;
+ }
+
+ public int getVelFrom() {
+ return velFrom;
+ }
+
+ public void setVelFrom(int velFrom) {
+ this.velFrom = velFrom;
+ }
+
+ public int getVelTo() {
+ return velTo;
+ }
+
+ public void setVelTo(int velTo) {
+ this.velTo = velTo;
+ }
+
+ public boolean isReleaseTriggered() {
+ return releaseTrigger;
+ }
+
+ public void setReleaseTriggered(boolean value) {
+ this.releaseTrigger = value;
+ }
+
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ public void setUserObject(Object object) {
+ userObject = object;
+ }
+
+ public boolean isDefaultConnectionsEnabled() {
+ return addDefaultConnections;
+ }
+
+ public void setDefaultConnectionsEnabled(boolean addDefaultConnections) {
+ this.addDefaultConnections = addDefaultConnections;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelSource.java b/src/share/classes/com/sun/media/sound/ModelSource.java
new file mode 100644
index 000000000..1ad5f3e24
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelSource.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This class is used to identify sources in connection blocks,
+ * see ModelConnectionBlock.
+ *
+ * @author Karl Helgason
+ */
+public class ModelSource {
+
+ public static final ModelIdentifier SOURCE_NONE = null;
+ public static final ModelIdentifier SOURCE_NOTEON_KEYNUMBER =
+ new ModelIdentifier("noteon", "keynumber"); // midi keynumber
+ public static final ModelIdentifier SOURCE_NOTEON_VELOCITY =
+ new ModelIdentifier("noteon", "velocity"); // midi velocity
+ public static final ModelIdentifier SOURCE_EG1 =
+ new ModelIdentifier("eg", null, 0);
+ public static final ModelIdentifier SOURCE_EG2 =
+ new ModelIdentifier("eg", null, 1);
+ public static final ModelIdentifier SOURCE_LFO1 =
+ new ModelIdentifier("lfo", null, 0);
+ public static final ModelIdentifier SOURCE_LFO2 =
+ new ModelIdentifier("lfo", null, 1);
+ public static final ModelIdentifier SOURCE_MIDI_PITCH =
+ new ModelIdentifier("midi", "pitch", 0); // (0..16383)
+ public static final ModelIdentifier SOURCE_MIDI_CHANNEL_PRESSURE =
+ new ModelIdentifier("midi", "channel_pressure", 0); // (0..127)
+// public static final ModelIdentifier SOURCE_MIDI_MONO_PRESSURE =
+// new ModelIdentifier("midi","mono_pressure",0); // (0..127)
+ public static final ModelIdentifier SOURCE_MIDI_POLY_PRESSURE =
+ new ModelIdentifier("midi", "poly_pressure", 0); // (0..127)
+ public static final ModelIdentifier SOURCE_MIDI_CC_0 =
+ new ModelIdentifier("midi_cc", "0", 0); // (0..127)
+ public static final ModelIdentifier SOURCE_MIDI_RPN_0 =
+ new ModelIdentifier("midi_rpn", "0", 0); // (0..16383)
+ private ModelIdentifier source = SOURCE_NONE;
+ private ModelTransform transform;
+
+ public ModelSource() {
+ this.transform = new ModelStandardTransform();
+ }
+
+ public ModelSource(ModelIdentifier id) {
+ source = id;
+ this.transform = new ModelStandardTransform();
+ }
+
+ public ModelSource(ModelIdentifier id, boolean direction) {
+ source = id;
+ this.transform = new ModelStandardTransform(direction);
+ }
+
+ public ModelSource(ModelIdentifier id, boolean direction, boolean polarity) {
+ source = id;
+ this.transform = new ModelStandardTransform(direction, polarity);
+ }
+
+ public ModelSource(ModelIdentifier id, boolean direction, boolean polarity,
+ int transform) {
+ source = id;
+ this.transform =
+ new ModelStandardTransform(direction, polarity, transform);
+ }
+
+ public ModelSource(ModelIdentifier id, ModelTransform transform) {
+ source = id;
+ this.transform = transform;
+ }
+
+ public ModelIdentifier getIdentifier() {
+ return source;
+ }
+
+ public void setIdentifier(ModelIdentifier source) {
+ this.source = source;
+ }
+
+ public ModelTransform getTransform() {
+ return transform;
+ }
+
+ public void setTransform(ModelTransform transform) {
+ this.transform = transform;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelStandardDirector.java b/src/share/classes/com/sun/media/sound/ModelStandardDirector.java
new file mode 100644
index 000000000..f9c9fc21b
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelStandardDirector.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A standard director who chooses performers
+ * by there keyfrom,keyto,velfrom,velto properties.
+ *
+ * @author Karl Helgason
+ */
+public class ModelStandardDirector implements ModelDirector {
+
+ ModelPerformer[] performers;
+ ModelDirectedPlayer player;
+ boolean noteOnUsed = false;
+ boolean noteOffUsed = false;
+
+ public ModelStandardDirector(ModelPerformer[] performers,
+ ModelDirectedPlayer player) {
+ this.performers = performers;
+ this.player = player;
+ for (int i = 0; i < performers.length; i++) {
+ ModelPerformer p = performers[i];
+ if (p.isReleaseTriggered()) {
+ noteOffUsed = true;
+ } else {
+ noteOnUsed = true;
+ }
+ }
+ }
+
+ public void close() {
+ }
+
+ public void noteOff(int noteNumber, int velocity) {
+ if (!noteOffUsed)
+ return;
+ for (int i = 0; i < performers.length; i++) {
+ ModelPerformer p = performers[i];
+ if (p.getKeyFrom() <= noteNumber && p.getKeyTo() >= noteNumber) {
+ if (p.getVelFrom() <= velocity && p.getVelTo() >= velocity) {
+ if (p.isReleaseTriggered()) {
+ player.play(i, null);
+ }
+ }
+ }
+ }
+ }
+
+ public void noteOn(int noteNumber, int velocity) {
+ if (!noteOnUsed)
+ return;
+ for (int i = 0; i < performers.length; i++) {
+ ModelPerformer p = performers[i];
+ if (p.getKeyFrom() <= noteNumber && p.getKeyTo() >= noteNumber) {
+ if (p.getVelFrom() <= velocity && p.getVelTo() >= velocity) {
+ if (!p.isReleaseTriggered()) {
+ player.play(i, null);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelStandardTransform.java b/src/share/classes/com/sun/media/sound/ModelStandardTransform.java
new file mode 100644
index 000000000..8cac033b3
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelStandardTransform.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A standard transformer used in connection blocks.
+ * It expects input values to be between 0 and 1.
+ *
+ * The result of the transform is
+ * between 0 and 1 if polarity = unipolar and
+ * between -1 and 1 if polarity = bipolar.
+ *
+ * These constraints only applies to Concave, Convex and Switch transforms.
+ *
+ * @author Karl Helgason
+ */
+public class ModelStandardTransform implements ModelTransform {
+
+ public static final boolean DIRECTION_MIN2MAX = false;
+ public static final boolean DIRECTION_MAX2MIN = true;
+ public static final boolean POLARITY_UNIPOLAR = false;
+ public static final boolean POLARITY_BIPOLAR = true;
+ public static final int TRANSFORM_LINEAR = 0;
+ // concave: output = (20*log10(127^2/value^2)) / 96
+ public static final int TRANSFORM_CONCAVE = 1;
+ // convex: same as concave except that start and end point are reversed.
+ public static final int TRANSFORM_CONVEX = 2;
+ // switch: if value > avg(max,min) then max else min
+ public static final int TRANSFORM_SWITCH = 3;
+ public static final int TRANSFORM_ABSOLUTE = 4;
+ private boolean direction = DIRECTION_MIN2MAX;
+ private boolean polarity = POLARITY_UNIPOLAR;
+ private int transform = TRANSFORM_LINEAR;
+
+ public ModelStandardTransform() {
+ }
+
+ public ModelStandardTransform(boolean direction) {
+ this.direction = direction;
+ }
+
+ public ModelStandardTransform(boolean direction, boolean polarity) {
+ this.direction = direction;
+ this.polarity = polarity;
+ }
+
+ public ModelStandardTransform(boolean direction, boolean polarity,
+ int transform) {
+ this.direction = direction;
+ this.polarity = polarity;
+ this.transform = transform;
+ }
+
+ public double transform(double value) {
+ double s;
+ double a;
+ if (direction == DIRECTION_MAX2MIN)
+ value = 1.0 - value;
+ if (polarity == POLARITY_BIPOLAR)
+ value = value * 2.0 - 1.0;
+ switch (transform) {
+ case TRANSFORM_CONCAVE:
+ s = Math.signum(value);
+ a = Math.abs(value);
+ a = -((5.0 / 12.0) / Math.log(10)) * Math.log(1.0 - a);
+ if (a < 0)
+ a = 0;
+ else if (a > 1)
+ a = 1;
+ return s * a;
+ case TRANSFORM_CONVEX:
+ s = Math.signum(value);
+ a = Math.abs(value);
+ a = 1.0 + ((5.0 / 12.0) / Math.log(10)) * Math.log(a);
+ if (a < 0)
+ a = 0;
+ else if (a > 1)
+ a = 1;
+ return s * a;
+ case TRANSFORM_SWITCH:
+ if (polarity == POLARITY_BIPOLAR)
+ return (value > 0) ? 1 : -1;
+ else
+ return (value > 0.5) ? 1 : 0;
+ case TRANSFORM_ABSOLUTE:
+ return Math.abs(value);
+ default:
+ break;
+ }
+
+ return value;
+ }
+
+ public boolean getDirection() {
+ return direction;
+ }
+
+ public void setDirection(boolean direction) {
+ this.direction = direction;
+ }
+
+ public boolean getPolarity() {
+ return polarity;
+ }
+
+ public void setPolarity(boolean polarity) {
+ this.polarity = polarity;
+ }
+
+ public int getTransform() {
+ return transform;
+ }
+
+ public void setTransform(int transform) {
+ this.transform = transform;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelTransform.java b/src/share/classes/com/sun/media/sound/ModelTransform.java
new file mode 100644
index 000000000..8b12938ba
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelTransform.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Model transform interface.
+ *
+ * @author Karl Helgason
+ */
+public interface ModelTransform {
+
+ abstract public double transform(double value);
+}
diff --git a/src/share/classes/com/sun/media/sound/ModelWavetable.java b/src/share/classes/com/sun/media/sound/ModelWavetable.java
new file mode 100644
index 000000000..b162ce1a7
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/ModelWavetable.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This is a wavetable oscillator interface.
+ *
+ * @author Karl Helgason
+ */
+public interface ModelWavetable extends ModelOscillator {
+
+ public static final int LOOP_TYPE_OFF = 0;
+ public static final int LOOP_TYPE_FORWARD = 1;
+ public static final int LOOP_TYPE_RELEASE = 2;
+ public static final int LOOP_TYPE_PINGPONG = 4;
+ public static final int LOOP_TYPE_REVERSE = 8;
+
+ public AudioFloatInputStream openStream();
+
+ public float getLoopLength();
+
+ public float getLoopStart();
+
+ public int getLoopType();
+
+ public float getPitchcorrection();
+}
diff --git a/src/share/classes/com/sun/media/sound/Platform.java b/src/share/classes/com/sun/media/sound/Platform.java
index 7dc55a50b..c29c0becf 100644
--- a/src/share/classes/com/sun/media/sound/Platform.java
+++ b/src/share/classes/com/sun/media/sound/Platform.java
@@ -42,8 +42,6 @@ class Platform {
// native library we need to load
private static final String libNameMain = "jsound";
- private static final String libNameMain2 = "jsoundhs";
-
private static final String libNameALSA = "jsoundalsa";
private static final String libNameDSound = "jsoundds";
@@ -158,9 +156,8 @@ class Platform {
if(Printer.trace)Printer.trace(">>Platform.loadLibraries");
try {
- // load the main libraries
+ // load the main library
JSSecurityManager.loadLibrary(libNameMain);
- JSSecurityManager.loadLibrary(libNameMain2);
// just for the heck of it...
loadedLibs |= LIB_MAIN;
} catch (SecurityException e) {
diff --git a/src/share/classes/com/sun/media/sound/RIFFInvalidDataException.java b/src/share/classes/com/sun/media/sound/RIFFInvalidDataException.java
new file mode 100644
index 000000000..0202b00ac
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/RIFFInvalidDataException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This exception is used when a RIFF file contains illegal or unexpected data.
+ *
+ * @author Karl Helgason
+ */
+public class RIFFInvalidDataException extends InvalidDataException {
+
+ private static final long serialVersionUID = 1L;
+
+ public RIFFInvalidDataException() {
+ super("Invalid Data!");
+ }
+
+ public RIFFInvalidDataException(String s) {
+ super(s);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/RIFFInvalidFormatException.java b/src/share/classes/com/sun/media/sound/RIFFInvalidFormatException.java
new file mode 100644
index 000000000..edab090e1
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/RIFFInvalidFormatException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * This exception is used when a reader is used to read RIFF file of a format it
+ * doesn't unterstand or support.
+ *
+ * @author Karl Helgason
+ */
+public class RIFFInvalidFormatException extends InvalidFormatException {
+
+ private static final long serialVersionUID = 1L;
+
+ public RIFFInvalidFormatException() {
+ super("Invalid format!");
+ }
+
+ public RIFFInvalidFormatException(String s) {
+ super(s);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/RIFFReader.java b/src/share/classes/com/sun/media/sound/RIFFReader.java
new file mode 100644
index 000000000..3433bdecb
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/RIFFReader.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Resource Interchange File Format (RIFF) stream decoder.
+ *
+ * @author Karl Helgason
+ */
+public class RIFFReader extends InputStream {
+
+ private RIFFReader root;
+ private long filepointer = 0;
+ private String fourcc;
+ private String riff_type = null;
+ private long ckSize = 0;
+ private InputStream stream;
+ private long avail;
+ private RIFFReader lastiterator = null;
+
+ public RIFFReader(InputStream stream) throws IOException {
+
+ if (stream instanceof RIFFReader)
+ root = ((RIFFReader)stream).root;
+ else
+ root = this;
+
+ this.stream = stream;
+ avail = Integer.MAX_VALUE;
+ ckSize = Integer.MAX_VALUE;
+
+ // Check for RIFF null paddings,
+ int b;
+ while (true) {
+ b = read();
+ if (b == -1) {
+ fourcc = ""; // don't put null value into fourcc,
+ // because it is expected to
+ // always contain a string value
+ riff_type = null;
+ avail = 0;
+ return;
+ }
+ if (b != 0)
+ break;
+ }
+
+ byte[] fourcc = new byte[4];
+ fourcc[0] = (byte) b;
+ readFully(fourcc, 1, 3);
+ this.fourcc = new String(fourcc, "ascii");
+ ckSize = readUnsignedInt();
+
+ avail = this.ckSize;
+
+ if (getFormat().equals("RIFF") || getFormat().equals("LIST")) {
+ byte[] format = new byte[4];
+ readFully(format);
+ this.riff_type = new String(format, "ascii");
+ }
+ }
+
+ public long getFilePointer() throws IOException {
+ return root.filepointer;
+ }
+
+ public boolean hasNextChunk() throws IOException {
+ if (lastiterator != null)
+ lastiterator.finish();
+ return avail != 0;
+ }
+
+ public RIFFReader nextChunk() throws IOException {
+ if (lastiterator != null)
+ lastiterator.finish();
+ if (avail == 0)
+ return null;
+ lastiterator = new RIFFReader(this);
+ return lastiterator;
+ }
+
+ public String getFormat() {
+ return fourcc;
+ }
+
+ public String getType() {
+ return riff_type;
+ }
+
+ public long getSize() {
+ return ckSize;
+ }
+
+ public int read() throws IOException {
+ if (avail == 0)
+ return -1;
+ int b = stream.read();
+ if (b == -1)
+ return -1;
+ avail--;
+ filepointer++;
+ return b;
+ }
+
+ public int read(byte[] b, int offset, int len) throws IOException {
+ if (avail == 0)
+ return -1;
+ if (len > avail) {
+ int rlen = stream.read(b, offset, (int)avail);
+ if (rlen != -1)
+ filepointer += rlen;
+ avail = 0;
+ return rlen;
+ } else {
+ int ret = stream.read(b, offset, len);
+ if (ret == -1)
+ return -1;
+ avail -= ret;
+ filepointer += ret;
+ return ret;
+ }
+ }
+
+ public final void readFully(byte b[]) throws IOException {
+ readFully(b, 0, b.length);
+ }
+
+ public final void readFully(byte b[], int off, int len) throws IOException {
+ if (len < 0)
+ throw new IndexOutOfBoundsException();
+ while (len > 0) {
+ int s = read(b, off, len);
+ if (s < 0)
+ throw new EOFException();
+ if (s == 0)
+ Thread.yield();
+ off += s;
+ len -= s;
+ }
+ }
+
+ public final long skipBytes(long n) throws IOException {
+ if (n < 0)
+ return 0;
+ long skipped = 0;
+ while (skipped != n) {
+ long s = skip(n - skipped);
+ if (s < 0)
+ break;
+ if (s == 0)
+ Thread.yield();
+ skipped += s;
+ }
+ return skipped;
+ }
+
+ public long skip(long n) throws IOException {
+ if (avail == 0)
+ return -1;
+ if (n > avail) {
+ long len = stream.skip(avail);
+ if (len != -1)
+ filepointer += len;
+ avail = 0;
+ return len;
+ } else {
+ long ret = stream.skip(n);
+ if (ret == -1)
+ return -1;
+ avail -= ret;
+ filepointer += ret;
+ return ret;
+ }
+ }
+
+ public int available() {
+ return (int)avail;
+ }
+
+ public void finish() throws IOException {
+ if (avail != 0) {
+ skipBytes(avail);
+ }
+ }
+
+ // Read ASCII chars from stream
+ public String readString(int len) throws IOException {
+ byte[] buff = new byte[len];
+ readFully(buff);
+ for (int i = 0; i < buff.length; i++) {
+ if (buff[i] == 0) {
+ return new String(buff, 0, i, "ascii");
+ }
+ }
+ return new String(buff, "ascii");
+ }
+
+ // Read 8 bit signed integer from stream
+ public byte readByte() throws IOException {
+ int ch = read();
+ if (ch < 0)
+ throw new EOFException();
+ return (byte) ch;
+ }
+
+ // Read 16 bit signed integer from stream
+ public short readShort() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+ if (ch1 < 0)
+ throw new EOFException();
+ if (ch2 < 0)
+ throw new EOFException();
+ return (short)(ch1 | (ch2 << 8));
+ }
+
+ // Read 32 bit signed integer from stream
+ public int readInt() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+ int ch3 = read();
+ int ch4 = read();
+ if (ch1 < 0)
+ throw new EOFException();
+ if (ch2 < 0)
+ throw new EOFException();
+ if (ch3 < 0)
+ throw new EOFException();
+ if (ch4 < 0)
+ throw new EOFException();
+ return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
+ }
+
+ // Read 64 bit signed integer from stream
+ public long readLong() throws IOException {
+ long ch1 = read();
+ long ch2 = read();
+ long ch3 = read();
+ long ch4 = read();
+ long ch5 = read();
+ long ch6 = read();
+ long ch7 = read();
+ long ch8 = read();
+ if (ch1 < 0)
+ throw new EOFException();
+ if (ch2 < 0)
+ throw new EOFException();
+ if (ch3 < 0)
+ throw new EOFException();
+ if (ch4 < 0)
+ throw new EOFException();
+ if (ch5 < 0)
+ throw new EOFException();
+ if (ch6 < 0)
+ throw new EOFException();
+ if (ch7 < 0)
+ throw new EOFException();
+ if (ch8 < 0)
+ throw new EOFException();
+ return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24)
+ | (ch5 << 32) | (ch6 << 40) | (ch7 << 48) | (ch8 << 56);
+ }
+
+ // Read 8 bit unsigned integer from stream
+ public int readUnsignedByte() throws IOException {
+ int ch = read();
+ if (ch < 0)
+ throw new EOFException();
+ return ch;
+ }
+
+ // Read 16 bit unsigned integer from stream
+ public int readUnsignedShort() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+ if (ch1 < 0)
+ throw new EOFException();
+ if (ch2 < 0)
+ throw new EOFException();
+ return ch1 | (ch2 << 8);
+ }
+
+ // Read 32 bit unsigned integer from stream
+ public long readUnsignedInt() throws IOException {
+ long ch1 = read();
+ long ch2 = read();
+ long ch3 = read();
+ long ch4 = read();
+ if (ch1 < 0)
+ throw new EOFException();
+ if (ch2 < 0)
+ throw new EOFException();
+ if (ch3 < 0)
+ throw new EOFException();
+ if (ch4 < 0)
+ throw new EOFException();
+ return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
+ }
+
+ public void close() throws IOException {
+ finish();
+ if (this == root)
+ stream.close();
+ stream = null;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/RIFFWriter.java b/src/share/classes/com/sun/media/sound/RIFFWriter.java
new file mode 100644
index 000000000..0c59f36a6
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/RIFFWriter.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * Resource Interchange File Format (RIFF) stream encoder.
+ *
+ * @author Karl Helgason
+ */
+public class RIFFWriter extends OutputStream {
+
+ private interface RandomAccessWriter {
+
+ public void seek(long chunksizepointer) throws IOException;
+
+ public long getPointer() throws IOException;
+
+ public void close() throws IOException;
+
+ public void write(int b) throws IOException;
+
+ public void write(byte[] b, int off, int len) throws IOException;
+
+ public void write(byte[] bytes) throws IOException;
+
+ public long length() throws IOException;
+
+ public void setLength(long i) throws IOException;
+ }
+
+ private static class RandomAccessFileWriter implements RandomAccessWriter {
+
+ RandomAccessFile raf;
+
+ public RandomAccessFileWriter(File file) throws FileNotFoundException {
+ this.raf = new RandomAccessFile(file, "rw");
+ }
+
+ public RandomAccessFileWriter(String name) throws FileNotFoundException {
+ this.raf = new RandomAccessFile(name, "rw");
+ }
+
+ public void seek(long chunksizepointer) throws IOException {
+ raf.seek(chunksizepointer);
+ }
+
+ public long getPointer() throws IOException {
+ return raf.getFilePointer();
+ }
+
+ public void close() throws IOException {
+ raf.close();
+ }
+
+ public void write(int b) throws IOException {
+ raf.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ raf.write(b, off, len);
+ }
+
+ public void write(byte[] bytes) throws IOException {
+ raf.write(bytes);
+ }
+
+ public long length() throws IOException {
+ return raf.length();
+ }
+
+ public void setLength(long i) throws IOException {
+ raf.setLength(i);
+ }
+ }
+
+ private static class RandomAccessByteWriter implements RandomAccessWriter {
+
+ byte[] buff = new byte[32];
+ int length = 0;
+ int pos = 0;
+ byte[] s;
+ OutputStream stream;
+
+ public RandomAccessByteWriter(OutputStream stream) {
+ this.stream = stream;
+ }
+
+ public void seek(long chunksizepointer) throws IOException {
+ pos = (int) chunksizepointer;
+ }
+
+ public long getPointer() throws IOException {
+ return pos;
+ }
+
+ public void close() throws IOException {
+ stream.write(buff, 0, length);
+ stream.close();
+ }
+
+ public void write(int b) throws IOException {
+ if (s == null)
+ s = new byte[1];
+ s[0] = (byte)b;
+ write(s, 0, 1);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ int newsize = pos + len;
+ if (newsize > length)
+ setLength(newsize);
+ int end = off + len;
+ for (int i = off; i < end; i++) {
+ buff[pos++] = b[i];
+ }
+ }
+
+ public void write(byte[] bytes) throws IOException {
+ write(bytes, 0, bytes.length);
+ }
+
+ public long length() throws IOException {
+ return length;
+ }
+
+ public void setLength(long i) throws IOException {
+ length = (int) i;
+ if (length > buff.length) {
+ int newlen = Math.max(buff.length << 1, length);
+ byte[] newbuff = new byte[newlen];
+ System.arraycopy(buff, 0, newbuff, 0, buff.length);
+ buff = newbuff;
+ }
+ }
+ }
+ private int chunktype = 0; // 0=RIFF, 1=LIST; 2=CHUNK
+ private RandomAccessWriter raf;
+ private long chunksizepointer;
+ private long startpointer;
+ private RIFFWriter childchunk = null;
+ private boolean open = true;
+ private boolean writeoverride = false;
+
+ public RIFFWriter(String name, String format) throws IOException {
+ this(new RandomAccessFileWriter(name), format, 0);
+ }
+
+ public RIFFWriter(File file, String format) throws IOException {
+ this(new RandomAccessFileWriter(file), format, 0);
+ }
+
+ public RIFFWriter(OutputStream stream, String format) throws IOException {
+ this(new RandomAccessByteWriter(stream), format, 0);
+ }
+
+ private RIFFWriter(RandomAccessWriter raf, String format, int chunktype)
+ throws IOException {
+ if (chunktype == 0)
+ if (raf.length() != 0)
+ raf.setLength(0);
+ this.raf = raf;
+ if (raf.getPointer() % 2 != 0)
+ raf.write(0);
+
+ if (chunktype == 0)
+ raf.write("RIFF".getBytes("ascii"));
+ else if (chunktype == 1)
+ raf.write("LIST".getBytes("ascii"));
+ else
+ raf.write((format + " ").substring(0, 4).getBytes("ascii"));
+
+ chunksizepointer = raf.getPointer();
+ this.chunktype = 2;
+ writeUnsignedInt(0);
+ this.chunktype = chunktype;
+ startpointer = raf.getPointer();
+ if (chunktype != 2)
+ raf.write((format + " ").substring(0, 4).getBytes("ascii"));
+
+ }
+
+ public void seek(long pos) throws IOException {
+ raf.seek(pos);
+ }
+
+ public long getFilePointer() throws IOException {
+ return raf.getPointer();
+ }
+
+ public void setWriteOverride(boolean writeoverride) {
+ this.writeoverride = writeoverride;
+ }
+
+ public boolean getWriteOverride() {
+ return writeoverride;
+ }
+
+ public void close() throws IOException {
+ if (!open)
+ return;
+ if (childchunk != null) {
+ childchunk.close();
+ childchunk = null;
+ }
+
+ int bakchunktype = chunktype;
+ long fpointer = raf.getPointer();
+ raf.seek(chunksizepointer);
+ chunktype = 2;
+ writeUnsignedInt(fpointer - startpointer);
+
+ if (bakchunktype == 0)
+ raf.close();
+ else
+ raf.seek(fpointer);
+ open = false;
+ raf = null;
+ }
+
+ public void write(int b) throws IOException {
+ if (!writeoverride) {
+ if (chunktype != 2) {
+ throw new IllegalArgumentException(
+ "Only chunks can write bytes!");
+ }
+ if (childchunk != null) {
+ childchunk.close();
+ childchunk = null;
+ }
+ }
+ raf.write(b);
+ }
+
+ public void write(byte b[], int off, int len) throws IOException {
+ if (!writeoverride) {
+ if (chunktype != 2) {
+ throw new IllegalArgumentException(
+ "Only chunks can write bytes!");
+ }
+ if (childchunk != null) {
+ childchunk.close();
+ childchunk = null;
+ }
+ }
+ raf.write(b, off, len);
+ }
+
+ public RIFFWriter writeList(String format) throws IOException {
+ if (chunktype == 2) {
+ throw new IllegalArgumentException(
+ "Only LIST and RIFF can write lists!");
+ }
+ if (childchunk != null) {
+ childchunk.close();
+ childchunk = null;
+ }
+ childchunk = new RIFFWriter(this.raf, format, 1);
+ return childchunk;
+ }
+
+ public RIFFWriter writeChunk(String format) throws IOException {
+ if (chunktype == 2) {
+ throw new IllegalArgumentException(
+ "Only LIST and RIFF can write chunks!");
+ }
+ if (childchunk != null) {
+ childchunk.close();
+ childchunk = null;
+ }
+ childchunk = new RIFFWriter(this.raf, format, 2);
+ return childchunk;
+ }
+
+ // Write ASCII chars to stream
+ public void writeString(String string) throws IOException {
+ byte[] buff = string.getBytes();
+ write(buff);
+ }
+
+ // Write ASCII chars to stream
+ public void writeString(String string, int len) throws IOException {
+ byte[] buff = string.getBytes();
+ if (buff.length > len)
+ write(buff, 0, len);
+ else {
+ write(buff);
+ for (int i = buff.length; i < len; i++)
+ write(0);
+ }
+ }
+
+ // Write 8 bit signed integer to stream
+ public void writeByte(int b) throws IOException {
+ write(b);
+ }
+
+ // Write 16 bit signed integer to stream
+ public void writeShort(short b) throws IOException {
+ write((b >>> 0) & 0xFF);
+ write((b >>> 8) & 0xFF);
+ }
+
+ // Write 32 bit signed integer to stream
+ public void writeInt(int b) throws IOException {
+ write((b >>> 0) & 0xFF);
+ write((b >>> 8) & 0xFF);
+ write((b >>> 16) & 0xFF);
+ write((b >>> 24) & 0xFF);
+ }
+
+ // Write 64 bit signed integer to stream
+ public void writeLong(long b) throws IOException {
+ write((int) (b >>> 0) & 0xFF);
+ write((int) (b >>> 8) & 0xFF);
+ write((int) (b >>> 16) & 0xFF);
+ write((int) (b >>> 24) & 0xFF);
+ write((int) (b >>> 32) & 0xFF);
+ write((int) (b >>> 40) & 0xFF);
+ write((int) (b >>> 48) & 0xFF);
+ write((int) (b >>> 56) & 0xFF);
+ }
+
+ // Write 8 bit unsigned integer to stream
+ public void writeUnsignedByte(int b) throws IOException {
+ writeByte((byte) b);
+ }
+
+ // Write 16 bit unsigned integer to stream
+ public void writeUnsignedShort(int b) throws IOException {
+ writeShort((short) b);
+ }
+
+ // Write 32 bit unsigned integer to stream
+ public void writeUnsignedInt(long b) throws IOException {
+ writeInt((int) b);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/RealTimeSequencer.java b/src/share/classes/com/sun/media/sound/RealTimeSequencer.java
index 82ef73c8c..bdb092cbf 100644
--- a/src/share/classes/com/sun/media/sound/RealTimeSequencer.java
+++ b/src/share/classes/com/sun/media/sound/RealTimeSequencer.java
@@ -54,10 +54,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
private final static boolean DEBUG_PUMP = false;
private final static boolean DEBUG_PUMP_ALL = false;
-
- /** if true, we bridge RMF files over to the old MixerSequencer */
- private final static boolean RMF = true;
-
/**
* Event Dispatcher thread. Should be using a shared event
* dispatcher instance with a factory in EventDispatcher
@@ -145,9 +141,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
private ArrayList controllerEventListeners = new ArrayList();
- /** for RMF media we need the RMF sequencer */
- private MixerSequencer seqBridge = null;
-
/** automatic connection support */
private boolean autoConnect = false;
@@ -220,21 +213,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
playThread.setSequence(sequence);
}
}
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.close();
- seqBridge = null;
- }
- // if previous file was an RMF, but this file is not RMF,
- // then need to call implOpen again!
- if (isOpen() && sequence != null && playThread == null) {
- try {
- implOpen();
- } catch (MidiUnavailableException mue) {
- if (Printer.err) mue.printStackTrace();
- }
- }
- }
if (Printer.trace) Printer.trace("<< RealTimeSequencer: setSequence(" + sequence +") completed");
}
@@ -249,52 +227,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
return;
}
- // need to be able to detect RMF
- if (RMF) {
- MidiFileFormat fileFormat = MidiSystem.getMidiFileFormat(stream); // can throw IOException, InvalidMidiDataException
- int type = fileFormat.getType();
- int resolution = fileFormat.getResolution();
- if (Printer.debug) Printer.debug("Got file with type="+type+" and resolution="+resolution);
- if (resolution == MidiFileFormat.UNKNOWN_LENGTH) {
- // seems to be RMF
- if (seqBridge == null) {
- try {
- seqBridge = new MixerSequencer();
- if (isOpen()) {
- seqBridge.open();
- }
- } catch (MidiUnavailableException mue) {
- // uhum, strange situation. Need to cast to InvalidMidiDataException
- throw new InvalidMidiDataException(mue.getMessage());
- }
- }
- seqBridge.setSequence(stream);
- // propagate state
- seqBridge.setTempoFactor(getTempoFactor());
-
- // propagate listeners
- synchronized(metaEventListeners) {
- for (int i = 0 ; i < metaEventListeners.size(); i++) {
- seqBridge.addMetaEventListener((MetaEventListener) (metaEventListeners.get(i)));
- }
- }
- synchronized(controllerEventListeners) {
- for (int i = 0 ; i < controllerEventListeners.size(); i++) {
- ControllerListElement cve = (ControllerListElement) (controllerEventListeners.get(i));
- seqBridge.addControllerEventListener(cve.listener, cve.controllers);
- }
- }
- // disable the current sequence of RealTimeSequencer
- //setSequence((Sequence) null); -> will remove bridge again!
- this.sequence = null;
- return;
- }
- if (seqBridge != null) {
- seqBridge.close();
- seqBridge = null;
- }
- }
-
Sequence seq = MidiSystem.getSequence(stream); // can throw IOException, InvalidMidiDataException
setSequence(seq);
@@ -305,22 +237,11 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public Sequence getSequence() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getSequence();
- }
- }
return sequence;
}
public synchronized void start() {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.start();
- return;
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: start()");
// sequencer not open: throw an exception
@@ -346,12 +267,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized void stop() {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.stop();
- return;
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: stop()");
if (!isOpen()) {
@@ -373,23 +288,11 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public boolean isRunning() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.isRunning();
- }
- }
return running;
}
public void startRecording() {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.startRecording();
- return;
- }
- }
-
if (!isOpen()) {
throw new IllegalStateException("Sequencer not open");
}
@@ -400,13 +303,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void stopRecording() {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.stopRecording();
- return;
- }
- }
-
if (!isOpen()) {
throw new IllegalStateException("Sequencer not open");
}
@@ -415,23 +311,11 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public boolean isRecording() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.isRecording();
- }
- }
return recording;
}
public void recordEnable(Track track, int channel) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.recordEnable(track, channel);
- return;
- }
- }
-
if (!findTrack(track)) {
throw new IllegalArgumentException("Track does not exist in the current sequence");
}
@@ -449,13 +333,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void recordDisable(Track track) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.recordDisable(track);
- return;
- }
- }
-
synchronized(recordingTracks) {
RecordingTrack rc = RecordingTrack.get(recordingTracks, track);
if (rc != null) {
@@ -482,11 +359,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public float getTempoInBPM() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTempoInBPM();
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoInBPM() ");
return (float) MidiUtils.convertTempo(getTempoInMPQ());
@@ -494,12 +366,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void setTempoInBPM(float bpm) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setTempoInBPM(bpm);
- return;
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoInBPM() ");
if (bpm <= 0) {
// should throw IllegalArgumentException
@@ -511,12 +377,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public float getTempoInMPQ() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTempoInMPQ();
- }
- }
-
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoInMPQ() ");
if (needCaching()) {
@@ -537,12 +397,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void setTempoInMPQ(float mpq) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setTempoInMPQ(mpq);
- return;
- }
- }
if (mpq <= 0) {
// should throw IllegalArgumentException
mpq = 1.0f;
@@ -564,12 +418,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void setTempoFactor(float factor) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setTempoFactor(factor);
- return;
- }
- }
if (factor <= 0) {
// should throw IllegalArgumentException
return;
@@ -588,11 +436,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public float getTempoFactor() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTempoFactor();
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoFactor() ");
if (needCaching()) {
@@ -606,11 +449,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public long getTickLength() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTickLength();
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTickLength() ");
if (sequence == null) {
@@ -622,11 +460,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized long getTickPosition() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTickPosition();
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTickPosition() ");
if (getDataPump() == null || sequence == null) {
@@ -638,12 +471,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized void setTickPosition(long tick) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setTickPosition(tick);
- return;
- }
- }
if (tick < 0) {
// should throw IllegalArgumentException
return;
@@ -667,12 +494,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public long getMicrosecondLength() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getMicrosecondLength();
- }
- }
-
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getMicrosecondLength() ");
if (sequence == null) {
@@ -684,12 +505,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public long getMicrosecondPosition() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getMicrosecondPosition();
- }
- }
-
if (Printer.trace) Printer.trace(">> RealTimeSequencer: getMicrosecondPosition() ");
if (getDataPump() == null || sequence == null) {
@@ -702,13 +517,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void setMicrosecondPosition(long microseconds) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setMicrosecondPosition(microseconds);
- return;
- }
- }
-
if (microseconds < 0) {
// should throw IllegalArgumentException
return;
@@ -734,33 +542,16 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void setMasterSyncMode(Sequencer.SyncMode sync) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setMasterSyncMode(sync);
- return;
- }
- }
// not supported
}
public Sequencer.SyncMode getMasterSyncMode() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getMasterSyncMode();
- }
- }
return masterSyncMode;
}
public Sequencer.SyncMode[] getMasterSyncModes() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getMasterSyncModes();
- }
- }
-
Sequencer.SyncMode[] returnedModes = new Sequencer.SyncMode[masterSyncModes.length];
System.arraycopy(masterSyncModes, 0, returnedModes, 0, masterSyncModes.length);
return returnedModes;
@@ -768,33 +559,16 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void setSlaveSyncMode(Sequencer.SyncMode sync) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setSlaveSyncMode(sync);
- return;
- }
- }
// not supported
}
public Sequencer.SyncMode getSlaveSyncMode() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getSlaveSyncMode();
- }
- }
return slaveSyncMode;
}
public Sequencer.SyncMode[] getSlaveSyncModes() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getSlaveSyncModes();
- }
- }
-
Sequencer.SyncMode[] returnedModes = new Sequencer.SyncMode[slaveSyncModes.length];
System.arraycopy(slaveSyncModes, 0, returnedModes, 0, slaveSyncModes.length);
return returnedModes;
@@ -812,12 +586,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized void setTrackMute(int track, boolean mute) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setTrackMute(track, mute);
- return;
- }
- }
int trackCount = getTrackCount();
if (track < 0 || track >= getTrackCount()) return;
trackMuted = ensureBoolArraySize(trackMuted, trackCount);
@@ -829,11 +597,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized boolean getTrackMute(int track) {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTrackMute(track);
- }
- }
if (track < 0 || track >= getTrackCount()) return false;
if (trackMuted == null || trackMuted.length <= track) return false;
return trackMuted[track];
@@ -841,12 +604,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized void setTrackSolo(int track, boolean solo) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setTrackSolo(track, solo);
- return;
- }
- }
int trackCount = getTrackCount();
if (track < 0 || track >= getTrackCount()) return;
trackSolo = ensureBoolArraySize(trackSolo, trackCount);
@@ -858,11 +615,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public synchronized boolean getTrackSolo(int track) {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getTrackSolo(track);
- }
- }
if (track < 0 || track >= getTrackCount()) return false;
if (trackSolo == null || trackSolo.length <= track) return false;
return trackSolo[track];
@@ -870,12 +622,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public boolean addMetaEventListener(MetaEventListener listener) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.addMetaEventListener(listener);
- // do not return here!
- }
- }
synchronized(metaEventListeners) {
if (! metaEventListeners.contains(listener)) {
@@ -887,12 +633,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public void removeMetaEventListener(MetaEventListener listener) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.removeMetaEventListener(listener);
- // do not return here!
- }
- }
synchronized(metaEventListeners) {
int index = metaEventListeners.indexOf(listener);
if (index >= 0) {
@@ -903,13 +643,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public int[] addControllerEventListener(ControllerEventListener listener, int[] controllers) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.addControllerEventListener(listener, controllers);
- // do not return here!
- }
- }
-
synchronized(controllerEventListeners) {
// first find the listener. if we have one, add the controllers
@@ -938,12 +671,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
public int[] removeControllerEventListener(ControllerEventListener listener, int[] controllers) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.removeControllerEventListener(listener, controllers);
- // do not return here!
- }
- }
synchronized(controllerEventListeners) {
ControllerListElement cve = null;
boolean flag = false;
@@ -973,12 +700,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
////////////////// LOOPING (added in 1.5) ///////////////////////
public void setLoopStartPoint(long tick) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setLoopStartPoint(tick);
- return;
- }
- }
if ((tick > getTickLength())
|| ((loopEnd != -1) && (tick > loopEnd))
|| (tick < 0)) {
@@ -988,21 +709,10 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
}
public long getLoopStartPoint() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getLoopStartPoint();
- }
- }
return loopStart;
}
public void setLoopEndPoint(long tick) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setLoopEndPoint(tick);
- return;
- }
- }
if ((tick > getTickLength())
|| ((loopStart > tick) && (tick != -1))
|| (tick < -1)) {
@@ -1012,21 +722,10 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
}
public long getLoopEndPoint() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getLoopEndPoint();
- }
- }
return loopEnd;
}
public void setLoopCount(int count) {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.setLoopCount(count);
- return;
- }
- }
if (count != LOOP_CONTINUOUSLY
&& count < 0) {
throw new IllegalArgumentException("illegal value for loop count: "+count);
@@ -1038,11 +737,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
}
public int getLoopCount() {
- if (RMF) {
- if (seqBridge != null) {
- return seqBridge.getLoopCount();
- }
- }
return loopCount;
}
@@ -1053,13 +747,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
*/
protected void implOpen() throws MidiUnavailableException {
if (Printer.trace) Printer.trace(">> RealTimeSequencer: implOpen()");
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.open();
- if (Printer.trace) Printer.trace("<< RealTimeSequencer: -> called seqBridge.open");
- return;
- }
- }
//openInternalSynth();
@@ -1095,12 +782,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
synth.open();
if (synth instanceof ReferenceCountingDevice) {
rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();
- if (synth.getClass().toString().contains("com.sun.media.sound.MixerSynth")
- && (synth.getDefaultSoundbank() == null)) {
- // don't use this receiver if no soundbank available
- rec = null;
- synth.close();
- }
} else {
rec = synth.getReceiver();
}
@@ -1147,12 +828,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
protected synchronized void implClose() {
- if (RMF) {
- if (seqBridge != null) {
- seqBridge.close();
- // don't return here!
- }
- }
if (Printer.trace) Printer.trace(">> RealTimeSequencer: implClose() ");
if (playThread == null) {
@@ -1302,12 +977,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
// OVERRIDES OF ABSTRACT MIDI DEVICE METHODS
protected boolean hasReceivers() {
- if (RMF) {
- if (seqBridge != null) {
- //RMF does not allow recording
- return false;
- }
- }
return true;
}
@@ -1318,12 +987,6 @@ class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoCon
protected boolean hasTransmitters() {
- if (RMF) {
- if (seqBridge != null) {
- //RMF does never allow setting own receivers
- return false;
- }
- }
return true;
}
diff --git a/src/share/classes/com/sun/media/sound/SF2GlobalRegion.java b/src/share/classes/com/sun/media/sound/SF2GlobalRegion.java
new file mode 100644
index 000000000..3740ee96b
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2GlobalRegion.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Soundfont global region.
+ *
+ * @author Karl Helgason
+ */
+public class SF2GlobalRegion extends SF2Region {
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2Instrument.java b/src/share/classes/com/sun/media/sound/SF2Instrument.java
new file mode 100644
index 000000000..ae44a7fe0
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2Instrument.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sound.midi.Patch;
+
+/**
+ * Soundfont instrument.
+ *
+ * @author Karl Helgason
+ */
+public class SF2Instrument extends ModelInstrument {
+
+ protected String name = "";
+ protected int preset = 0;
+ protected int bank = 0;
+ protected long library = 0;
+ protected long genre = 0;
+ protected long morphology = 0;
+ protected SF2GlobalRegion globalregion = null;
+ protected List<SF2InstrumentRegion> regions
+ = new ArrayList<SF2InstrumentRegion>();
+
+ public SF2Instrument() {
+ super(null, null, null, null);
+ }
+
+ public SF2Instrument(SF2Soundbank soundbank) {
+ super(soundbank, null, null, null);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Patch getPatch() {
+ if (bank == 128)
+ return new ModelPatch(0, preset, true);
+ else
+ return new ModelPatch(bank << 7, preset, false);
+ }
+
+ public void setPatch(Patch patch) {
+ if (patch instanceof ModelPatch && ((ModelPatch) patch).isPercussion()) {
+ bank = 128;
+ preset = patch.getProgram();
+ } else {
+ bank = patch.getBank() >> 7;
+ preset = patch.getProgram();
+ }
+ }
+
+ public Object getData() {
+ return null;
+ }
+
+ public long getGenre() {
+ return genre;
+ }
+
+ public void setGenre(long genre) {
+ this.genre = genre;
+ }
+
+ public long getLibrary() {
+ return library;
+ }
+
+ public void setLibrary(long library) {
+ this.library = library;
+ }
+
+ public long getMorphology() {
+ return morphology;
+ }
+
+ public void setMorphology(long morphology) {
+ this.morphology = morphology;
+ }
+
+ public List<SF2InstrumentRegion> getRegions() {
+ return regions;
+ }
+
+ public SF2GlobalRegion getGlobalRegion() {
+ return globalregion;
+ }
+
+ public void setGlobalZone(SF2GlobalRegion zone) {
+ globalregion = zone;
+ }
+
+ public String toString() {
+ if (bank == 128)
+ return "Drumkit: " + name + " preset #" + preset;
+ else
+ return "Instrument: " + name + " bank #" + bank
+ + " preset #" + preset;
+ }
+
+ public ModelPerformer[] getPerformers() {
+ int performercount = 0;
+ for (SF2InstrumentRegion presetzone : regions)
+ performercount += presetzone.getLayer().getRegions().size();
+ ModelPerformer[] performers = new ModelPerformer[performercount];
+ int pi = 0;
+
+ SF2GlobalRegion presetglobal = globalregion;
+ for (SF2InstrumentRegion presetzone : regions) {
+ Map<Integer, Short> pgenerators = new HashMap<Integer, Short>();
+ pgenerators.putAll(presetzone.getGenerators());
+ if (presetglobal != null)
+ pgenerators.putAll(presetglobal.getGenerators());
+
+ SF2Layer layer = presetzone.getLayer();
+ SF2GlobalRegion layerglobal = layer.getGlobalRegion();
+ for (SF2LayerRegion layerzone : layer.getRegions()) {
+ ModelPerformer performer = new ModelPerformer();
+ if (layerzone.getSample() != null)
+ performer.setName(layerzone.getSample().getName());
+ else
+ performer.setName(layer.getName());
+
+ performers[pi++] = performer;
+
+ int keyfrom = 0;
+ int keyto = 127;
+ int velfrom = 0;
+ int velto = 127;
+
+ if (layerzone.contains(SF2Region.GENERATOR_EXCLUSIVECLASS)) {
+ performer.setExclusiveClass(layerzone.getInteger(
+ SF2Region.GENERATOR_EXCLUSIVECLASS));
+ }
+ if (layerzone.contains(SF2Region.GENERATOR_KEYRANGE)) {
+ byte[] bytes = layerzone.getBytes(
+ SF2Region.GENERATOR_KEYRANGE);
+ if (bytes[0] >= 0)
+ if (bytes[0] > keyfrom)
+ keyfrom = bytes[0];
+ if (bytes[1] >= 0)
+ if (bytes[1] < keyto)
+ keyto = bytes[1];
+ }
+ if (layerzone.contains(SF2Region.GENERATOR_VELRANGE)) {
+ byte[] bytes = layerzone.getBytes(
+ SF2Region.GENERATOR_VELRANGE);
+ if (bytes[0] >= 0)
+ if (bytes[0] > velfrom)
+ velfrom = bytes[0];
+ if (bytes[1] >= 0)
+ if (bytes[1] < velto)
+ velto = bytes[1];
+ }
+ if (presetzone.contains(SF2Region.GENERATOR_KEYRANGE)) {
+ byte[] bytes = presetzone.getBytes(
+ SF2Region.GENERATOR_KEYRANGE);
+ if (bytes[0] > keyfrom)
+ keyfrom = bytes[0];
+ if (bytes[1] < keyto)
+ keyto = bytes[1];
+ }
+ if (presetzone.contains(SF2Region.GENERATOR_VELRANGE)) {
+ byte[] bytes = presetzone.getBytes(
+ SF2Region.GENERATOR_VELRANGE);
+ if (bytes[0] > velfrom)
+ velfrom = bytes[0];
+ if (bytes[1] < velto)
+ velto = bytes[1];
+ }
+ performer.setKeyFrom(keyfrom);
+ performer.setKeyTo(keyto);
+ performer.setVelFrom(velfrom);
+ performer.setVelTo(velto);
+
+ int startAddrsOffset = layerzone.getShort(
+ SF2Region.GENERATOR_STARTADDRSOFFSET);
+ int endAddrsOffset = layerzone.getShort(
+ SF2Region.GENERATOR_ENDADDRSOFFSET);
+ int startloopAddrsOffset = layerzone.getShort(
+ SF2Region.GENERATOR_STARTLOOPADDRSOFFSET);
+ int endloopAddrsOffset = layerzone.getShort(
+ SF2Region.GENERATOR_ENDLOOPADDRSOFFSET);
+
+ startAddrsOffset += layerzone.getShort(
+ SF2Region.GENERATOR_STARTADDRSCOARSEOFFSET) * 32768;
+ endAddrsOffset += layerzone.getShort(
+ SF2Region.GENERATOR_ENDADDRSCOARSEOFFSET) * 32768;
+ startloopAddrsOffset += layerzone.getShort(
+ SF2Region.GENERATOR_STARTLOOPADDRSCOARSEOFFSET) * 32768;
+ endloopAddrsOffset += layerzone.getShort(
+ SF2Region.GENERATOR_ENDLOOPADDRSCOARSEOFFSET) * 32768;
+ startloopAddrsOffset -= startAddrsOffset;
+ endloopAddrsOffset -= startAddrsOffset;
+
+ SF2Sample sample = layerzone.getSample();
+ int rootkey = sample.originalPitch;
+ if (layerzone.getShort(SF2Region.GENERATOR_OVERRIDINGROOTKEY) != -1) {
+ rootkey = layerzone.getShort(
+ SF2Region.GENERATOR_OVERRIDINGROOTKEY);
+ }
+ float pitchcorrection = (-rootkey * 100) + sample.pitchCorrection;
+ ModelByteBuffer buff = sample.getDataBuffer();
+ ModelByteBuffer buff24 = sample.getData24Buffer();
+
+ if (startAddrsOffset != 0 || endAddrsOffset != 0) {
+ buff = buff.subbuffer(startAddrsOffset * 2,
+ buff.capacity() + endAddrsOffset * 2);
+ if (buff24 != null) {
+ buff24 = buff24.subbuffer(startAddrsOffset,
+ buff24.capacity() + endAddrsOffset);
+ }
+
+ /*
+ if (startAddrsOffset < 0)
+ startAddrsOffset = 0;
+ if (endAddrsOffset > (buff.capacity()/2-startAddrsOffset))
+ startAddrsOffset = (int)buff.capacity()/2-startAddrsOffset;
+ byte[] data = buff.array();
+ int off = (int)buff.arrayOffset() + startAddrsOffset*2;
+ int len = (int)buff.capacity() + endAddrsOffset*2;
+ if (off+len > data.length)
+ len = data.length - off;
+ buff = new ModelByteBuffer(data, off, len);
+ if(buff24 != null) {
+ data = buff.array();
+ off = (int)buff.arrayOffset() + startAddrsOffset;
+ len = (int)buff.capacity() + endAddrsOffset;
+ buff24 = new ModelByteBuffer(data, off, len);
+ }
+ */
+ }
+
+ ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
+ buff, sample.getFormat(), pitchcorrection);
+ if (buff24 != null)
+ osc.set8BitExtensionBuffer(buff24);
+
+ Map<Integer, Short> generators = new HashMap<Integer, Short>();
+ if (layerglobal != null)
+ generators.putAll(layerglobal.getGenerators());
+ generators.putAll(layerzone.getGenerators());
+ for (Map.Entry<Integer, Short> gen : pgenerators.entrySet()) {
+ short val;
+ if (!generators.containsKey(gen.getKey()))
+ val = layerzone.getShort(gen.getKey());
+ else
+ val = generators.get(gen.getKey());
+ val += gen.getValue();
+ generators.put(gen.getKey(), val);
+ }
+
+ // SampleMode:
+ // 0 indicates a sound reproduced with no loop
+ // 1 indicates a sound which loops continuously
+ // 2 is unused but should be interpreted as indicating no loop
+ // 3 indicates a sound which loops for the duration of key
+ // depression then proceeds to play the remainder of the sample.
+ int sampleMode = getGeneratorValue(generators,
+ SF2Region.GENERATOR_SAMPLEMODES);
+ if ((sampleMode == 1) || (sampleMode == 3)) {
+ if (sample.startLoop >= 0 && sample.endLoop > 0) {
+ osc.setLoopStart((int)(sample.startLoop
+ + startloopAddrsOffset));
+ osc.setLoopLength((int)(sample.endLoop - sample.startLoop
+ + endloopAddrsOffset - startloopAddrsOffset));
+ if (sampleMode == 1)
+ osc.setLoopType(ModelWavetable.LOOP_TYPE_FORWARD);
+ if (sampleMode == 3)
+ osc.setLoopType(ModelWavetable.LOOP_TYPE_RELEASE);
+ }
+ }
+ performer.getOscillators().add(osc);
+
+
+ short volDelay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_DELAYVOLENV);
+ short volAttack = getGeneratorValue(generators,
+ SF2Region.GENERATOR_ATTACKVOLENV);
+ short volHold = getGeneratorValue(generators,
+ SF2Region.GENERATOR_HOLDVOLENV);
+ short volDecay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_DECAYVOLENV);
+ short volSustain = getGeneratorValue(generators,
+ SF2Region.GENERATOR_SUSTAINVOLENV);
+ short volRelease = getGeneratorValue(generators,
+ SF2Region.GENERATOR_RELEASEVOLENV);
+
+ if (volHold != -12000) {
+ short volKeyNumToHold = getGeneratorValue(generators,
+ SF2Region.GENERATOR_KEYNUMTOVOLENVHOLD);
+ volHold += 60 * volKeyNumToHold;
+ float fvalue = -volKeyNumToHold * 128;
+ ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ ModelIdentifier dest = ModelDestination.DESTINATION_EG1_HOLD;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src), fvalue,
+ new ModelDestination(dest)));
+ }
+ if (volDecay != -12000) {
+ short volKeyNumToDecay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_KEYNUMTOVOLENVDECAY);
+ volDecay += 60 * volKeyNumToDecay;
+ float fvalue = -volKeyNumToDecay * 128;
+ ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ ModelIdentifier dest = ModelDestination.DESTINATION_EG1_DECAY;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src), fvalue,
+ new ModelDestination(dest)));
+ }
+
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG1_DELAY, volDelay);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG1_ATTACK, volAttack);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG1_HOLD, volHold);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG1_DECAY, volDecay);
+ //float fvolsustain = (960-volSustain)*(1000.0f/960.0f);
+
+ volSustain = (short)(1000 - volSustain);
+ if (volSustain < 0)
+ volSustain = 0;
+ if (volSustain > 1000)
+ volSustain = 1000;
+
+ addValue(performer,
+ ModelDestination.DESTINATION_EG1_SUSTAIN, volSustain);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG1_RELEASE, volRelease);
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODENVTOFILTERFC) != 0
+ || getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODENVTOPITCH) != 0) {
+ short modDelay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_DELAYMODENV);
+ short modAttack = getGeneratorValue(generators,
+ SF2Region.GENERATOR_ATTACKMODENV);
+ short modHold = getGeneratorValue(generators,
+ SF2Region.GENERATOR_HOLDMODENV);
+ short modDecay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_DECAYMODENV);
+ short modSustain = getGeneratorValue(generators,
+ SF2Region.GENERATOR_SUSTAINMODENV);
+ short modRelease = getGeneratorValue(generators,
+ SF2Region.GENERATOR_RELEASEMODENV);
+
+
+ if (modHold != -12000) {
+ short modKeyNumToHold = getGeneratorValue(generators,
+ SF2Region.GENERATOR_KEYNUMTOMODENVHOLD);
+ modHold += 60 * modKeyNumToHold;
+ float fvalue = -modKeyNumToHold * 128;
+ ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ ModelIdentifier dest = ModelDestination.DESTINATION_EG2_HOLD;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src),
+ fvalue, new ModelDestination(dest)));
+ }
+ if (modDecay != -12000) {
+ short modKeyNumToDecay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_KEYNUMTOMODENVDECAY);
+ modDecay += 60 * modKeyNumToDecay;
+ float fvalue = -modKeyNumToDecay * 128;
+ ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ ModelIdentifier dest = ModelDestination.DESTINATION_EG2_DECAY;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG2_DELAY, modDelay);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG2_ATTACK, modAttack);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG2_HOLD, modHold);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG2_DECAY, modDecay);
+ if (modSustain < 0)
+ modSustain = 0;
+ if (modSustain > 1000)
+ modSustain = 1000;
+ addValue(performer, ModelDestination.DESTINATION_EG2_SUSTAIN,
+ 1000 - modSustain);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_EG2_RELEASE, modRelease);
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODENVTOFILTERFC) != 0) {
+ double fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODENVTOFILTERFC);
+ ModelIdentifier src = ModelSource.SOURCE_EG2;
+ ModelIdentifier dest
+ = ModelDestination.DESTINATION_FILTER_FREQ;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODENVTOPITCH) != 0) {
+ double fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODENVTOPITCH);
+ ModelIdentifier src = ModelSource.SOURCE_EG2;
+ ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ }
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOFILTERFC) != 0
+ || getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOPITCH) != 0
+ || getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOVOLUME) != 0) {
+ short lfo_freq = getGeneratorValue(generators,
+ SF2Region.GENERATOR_FREQMODLFO);
+ short lfo_delay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_DELAYMODLFO);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_LFO1_DELAY, lfo_delay);
+ addValue(performer,
+ ModelDestination.DESTINATION_LFO1_FREQ, lfo_freq);
+ }
+
+ short vib_freq = getGeneratorValue(generators,
+ SF2Region.GENERATOR_FREQVIBLFO);
+ short vib_delay = getGeneratorValue(generators,
+ SF2Region.GENERATOR_DELAYVIBLFO);
+ addTimecentValue(performer,
+ ModelDestination.DESTINATION_LFO2_DELAY, vib_delay);
+ addValue(performer,
+ ModelDestination.DESTINATION_LFO2_FREQ, vib_freq);
+
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_VIBLFOTOPITCH) != 0) {
+ double fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_VIBLFOTOPITCH);
+ ModelIdentifier src = ModelSource.SOURCE_LFO2;
+ ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(
+ new ModelSource(src,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOFILTERFC) != 0) {
+ double fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOFILTERFC);
+ ModelIdentifier src = ModelSource.SOURCE_LFO1;
+ ModelIdentifier dest = ModelDestination.DESTINATION_FILTER_FREQ;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(
+ new ModelSource(src,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOPITCH) != 0) {
+ double fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOPITCH);
+ ModelIdentifier src = ModelSource.SOURCE_LFO1;
+ ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(
+ new ModelSource(src,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOVOLUME) != 0) {
+ double fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_MODLFOTOVOLUME);
+ ModelIdentifier src = ModelSource.SOURCE_LFO1;
+ ModelIdentifier dest = ModelDestination.DESTINATION_GAIN;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(
+ new ModelSource(src,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR),
+ fvalue, new ModelDestination(dest)));
+ }
+
+ if (layerzone.getShort(SF2Region.GENERATOR_KEYNUM) != -1) {
+ double val = layerzone.getShort(SF2Region.GENERATOR_KEYNUM)/128.0;
+ addValue(performer, ModelDestination.DESTINATION_KEYNUMBER, val);
+ }
+
+ if (layerzone.getShort(SF2Region.GENERATOR_VELOCITY) != -1) {
+ double val = layerzone.getShort(SF2Region.GENERATOR_VELOCITY)
+ / 128.0;
+ addValue(performer, ModelDestination.DESTINATION_VELOCITY, val);
+ }
+
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_INITIALFILTERFC) < 13500) {
+ short filter_freq = getGeneratorValue(generators,
+ SF2Region.GENERATOR_INITIALFILTERFC);
+ short filter_q = getGeneratorValue(generators,
+ SF2Region.GENERATOR_INITIALFILTERQ);
+ addValue(performer,
+ ModelDestination.DESTINATION_FILTER_FREQ, filter_freq);
+ addValue(performer,
+ ModelDestination.DESTINATION_FILTER_Q, filter_q);
+ }
+
+ int tune = 100 * getGeneratorValue(generators,
+ SF2Region.GENERATOR_COARSETUNE);
+ tune += getGeneratorValue(generators,
+ SF2Region.GENERATOR_FINETUNE);
+ if (tune != 0) {
+ addValue(performer,
+ ModelDestination.DESTINATION_PITCH, (short) tune);
+ }
+ if (getGeneratorValue(generators, SF2Region.GENERATOR_PAN) != 0) {
+ short val = getGeneratorValue(generators,
+ SF2Region.GENERATOR_PAN);
+ addValue(performer, ModelDestination.DESTINATION_PAN, val);
+ }
+ if (getGeneratorValue(generators, SF2Region.GENERATOR_INITIALATTENUATION) != 0) {
+ short val = getGeneratorValue(generators,
+ SF2Region.GENERATOR_INITIALATTENUATION);
+ addValue(performer,
+ ModelDestination.DESTINATION_GAIN, -0.376287f * val);
+ }
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_CHORUSEFFECTSSEND) != 0) {
+ short val = getGeneratorValue(generators,
+ SF2Region.GENERATOR_CHORUSEFFECTSSEND);
+ addValue(performer, ModelDestination.DESTINATION_CHORUS, val);
+ }
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_REVERBEFFECTSSEND) != 0) {
+ short val = getGeneratorValue(generators,
+ SF2Region.GENERATOR_REVERBEFFECTSSEND);
+ addValue(performer, ModelDestination.DESTINATION_REVERB, val);
+ }
+ if (getGeneratorValue(generators,
+ SF2Region.GENERATOR_SCALETUNING) != 100) {
+ short fvalue = getGeneratorValue(generators,
+ SF2Region.GENERATOR_SCALETUNING);
+ if (fvalue == 0) {
+ ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(null, rootkey * 100,
+ new ModelDestination(dest)));
+ } else {
+ ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(null, rootkey * (100 - fvalue),
+ new ModelDestination(dest)));
+ }
+
+ ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(new ModelSource(src),
+ 128 * fvalue, new ModelDestination(dest)));
+
+ }
+
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(
+ new ModelSource(ModelSource.SOURCE_NOTEON_VELOCITY,
+ new ModelTransform() {
+ public double transform(double value) {
+ if (value < 0.5)
+ return 1 - value * 2;
+ else
+ return 0;
+ }
+ }),
+ -2400,
+ new ModelDestination(
+ ModelDestination.DESTINATION_FILTER_FREQ)));
+
+
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(
+ new ModelSource(ModelSource.SOURCE_LFO2,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ new ModelSource(new ModelIdentifier("midi_cc", "1", 0),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 50, new ModelDestination(
+ ModelDestination.DESTINATION_PITCH)));
+
+ if (layer.getGlobalRegion() != null) {
+ for (SF2Modulator modulator
+ : layer.getGlobalRegion().getModulators()) {
+ convertModulator(performer, modulator);
+ }
+ }
+ for (SF2Modulator modulator : layerzone.getModulators())
+ convertModulator(performer, modulator);
+
+ if (presetglobal != null) {
+ for (SF2Modulator modulator : presetglobal.getModulators())
+ convertModulator(performer, modulator);
+ }
+ for (SF2Modulator modulator : presetzone.getModulators())
+ convertModulator(performer, modulator);
+
+ }
+ }
+ return performers;
+ }
+
+ private void convertModulator(ModelPerformer performer,
+ SF2Modulator modulator) {
+ ModelSource src1 = convertSource(modulator.getSourceOperator());
+ ModelSource src2 = convertSource(modulator.getAmountSourceOperator());
+ if (src1 == null && modulator.getSourceOperator() != 0)
+ return;
+ if (src2 == null && modulator.getAmountSourceOperator() != 0)
+ return;
+ double amount = modulator.getAmount();
+ double[] amountcorrection = new double[1];
+ ModelSource[] extrasrc = new ModelSource[1];
+ amountcorrection[0] = 1;
+ ModelDestination dst = convertDestination(
+ modulator.getDestinationOperator(), amountcorrection, extrasrc);
+ amount *= amountcorrection[0];
+ if (dst == null)
+ return;
+ if (modulator.getTransportOperator() == SF2Modulator.TRANSFORM_ABSOLUTE) {
+ ((ModelStandardTransform)dst.getTransform()).setTransform(
+ ModelStandardTransform.TRANSFORM_ABSOLUTE);
+ }
+ ModelConnectionBlock conn = new ModelConnectionBlock(src1, src2, amount, dst);
+ if (extrasrc[0] != null)
+ conn.addSource(extrasrc[0]);
+ performer.getConnectionBlocks().add(conn);
+
+ }
+
+ private static ModelSource convertSource(int src) {
+ if (src == 0)
+ return null;
+ ModelIdentifier id = null;
+ int idsrc = src & 0x7F;
+ if ((src & SF2Modulator.SOURCE_MIDI_CONTROL) != 0) {
+ id = new ModelIdentifier("midi_cc", Integer.toString(idsrc));
+ } else {
+ if (idsrc == SF2Modulator.SOURCE_NOTE_ON_VELOCITY)
+ id = ModelSource.SOURCE_NOTEON_VELOCITY;
+ if (idsrc == SF2Modulator.SOURCE_NOTE_ON_KEYNUMBER)
+ id = ModelSource.SOURCE_NOTEON_KEYNUMBER;
+ if (idsrc == SF2Modulator.SOURCE_POLY_PRESSURE)
+ id = ModelSource.SOURCE_MIDI_POLY_PRESSURE;
+ if (idsrc == SF2Modulator.SOURCE_CHANNEL_PRESSURE)
+ id = ModelSource.SOURCE_MIDI_CHANNEL_PRESSURE;
+ if (idsrc == SF2Modulator.SOURCE_PITCH_WHEEL)
+ id = ModelSource.SOURCE_MIDI_PITCH;
+ if (idsrc == SF2Modulator.SOURCE_PITCH_SENSITIVITY)
+ id = new ModelIdentifier("midi_rpn", "0");
+ }
+ if (id == null)
+ return null;
+
+ ModelSource msrc = new ModelSource(id);
+ ModelStandardTransform transform
+ = (ModelStandardTransform) msrc.getTransform();
+
+ if ((SF2Modulator.SOURCE_DIRECTION_MAX_MIN & src) != 0)
+ transform.setDirection(ModelStandardTransform.DIRECTION_MAX2MIN);
+ else
+ transform.setDirection(ModelStandardTransform.DIRECTION_MIN2MAX);
+
+ if ((SF2Modulator.SOURCE_POLARITY_BIPOLAR & src) != 0)
+ transform.setPolarity(ModelStandardTransform.POLARITY_BIPOLAR);
+ else
+ transform.setPolarity(ModelStandardTransform.POLARITY_UNIPOLAR);
+
+ if ((SF2Modulator.SOURCE_TYPE_CONCAVE & src) != 0)
+ transform.setTransform(ModelStandardTransform.TRANSFORM_CONCAVE);
+ if ((SF2Modulator.SOURCE_TYPE_CONVEX & src) != 0)
+ transform.setTransform(ModelStandardTransform.TRANSFORM_CONVEX);
+ if ((SF2Modulator.SOURCE_TYPE_SWITCH & src) != 0)
+ transform.setTransform(ModelStandardTransform.TRANSFORM_SWITCH);
+
+ return msrc;
+ }
+
+ protected static ModelDestination convertDestination(int dst,
+ double[] amountcorrection, ModelSource[] extrasrc) {
+ ModelIdentifier id = null;
+ switch (dst) {
+ case SF2Region.GENERATOR_INITIALFILTERFC:
+ id = ModelDestination.DESTINATION_FILTER_FREQ;
+ break;
+ case SF2Region.GENERATOR_INITIALFILTERQ:
+ id = ModelDestination.DESTINATION_FILTER_Q;
+ break;
+ case SF2Region.GENERATOR_CHORUSEFFECTSSEND:
+ id = ModelDestination.DESTINATION_CHORUS;
+ break;
+ case SF2Region.GENERATOR_REVERBEFFECTSSEND:
+ id = ModelDestination.DESTINATION_REVERB;
+ break;
+ case SF2Region.GENERATOR_PAN:
+ id = ModelDestination.DESTINATION_PAN;
+ break;
+ case SF2Region.GENERATOR_DELAYMODLFO:
+ id = ModelDestination.DESTINATION_LFO1_DELAY;
+ break;
+ case SF2Region.GENERATOR_FREQMODLFO:
+ id = ModelDestination.DESTINATION_LFO1_FREQ;
+ break;
+ case SF2Region.GENERATOR_DELAYVIBLFO:
+ id = ModelDestination.DESTINATION_LFO2_DELAY;
+ break;
+ case SF2Region.GENERATOR_FREQVIBLFO:
+ id = ModelDestination.DESTINATION_LFO2_FREQ;
+ break;
+
+ case SF2Region.GENERATOR_DELAYMODENV:
+ id = ModelDestination.DESTINATION_EG2_DELAY;
+ break;
+ case SF2Region.GENERATOR_ATTACKMODENV:
+ id = ModelDestination.DESTINATION_EG2_ATTACK;
+ break;
+ case SF2Region.GENERATOR_HOLDMODENV:
+ id = ModelDestination.DESTINATION_EG2_HOLD;
+ break;
+ case SF2Region.GENERATOR_DECAYMODENV:
+ id = ModelDestination.DESTINATION_EG2_DECAY;
+ break;
+ case SF2Region.GENERATOR_SUSTAINMODENV:
+ id = ModelDestination.DESTINATION_EG2_SUSTAIN;
+ amountcorrection[0] = -1;
+ break;
+ case SF2Region.GENERATOR_RELEASEMODENV:
+ id = ModelDestination.DESTINATION_EG2_RELEASE;
+ break;
+ case SF2Region.GENERATOR_DELAYVOLENV:
+ id = ModelDestination.DESTINATION_EG1_DELAY;
+ break;
+ case SF2Region.GENERATOR_ATTACKVOLENV:
+ id = ModelDestination.DESTINATION_EG1_ATTACK;
+ break;
+ case SF2Region.GENERATOR_HOLDVOLENV:
+ id = ModelDestination.DESTINATION_EG1_HOLD;
+ break;
+ case SF2Region.GENERATOR_DECAYVOLENV:
+ id = ModelDestination.DESTINATION_EG1_DECAY;
+ break;
+ case SF2Region.GENERATOR_SUSTAINVOLENV:
+ id = ModelDestination.DESTINATION_EG1_SUSTAIN;
+ amountcorrection[0] = -1;
+ break;
+ case SF2Region.GENERATOR_RELEASEVOLENV:
+ id = ModelDestination.DESTINATION_EG1_RELEASE;
+ break;
+ case SF2Region.GENERATOR_KEYNUM:
+ id = ModelDestination.DESTINATION_KEYNUMBER;
+ break;
+ case SF2Region.GENERATOR_VELOCITY:
+ id = ModelDestination.DESTINATION_VELOCITY;
+ break;
+
+ case SF2Region.GENERATOR_COARSETUNE:
+ amountcorrection[0] = 100;
+ id = ModelDestination.DESTINATION_PITCH;
+ break;
+
+ case SF2Region.GENERATOR_FINETUNE:
+ id = ModelDestination.DESTINATION_PITCH;
+ break;
+
+ case SF2Region.GENERATOR_INITIALATTENUATION:
+ id = ModelDestination.DESTINATION_GAIN;
+ amountcorrection[0] = -0.376287f;
+ break;
+
+ case SF2Region.GENERATOR_VIBLFOTOPITCH:
+ id = ModelDestination.DESTINATION_PITCH;
+ extrasrc[0] = new ModelSource(
+ ModelSource.SOURCE_LFO2,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ break;
+
+ case SF2Region.GENERATOR_MODLFOTOPITCH:
+ id = ModelDestination.DESTINATION_PITCH;
+ extrasrc[0] = new ModelSource(
+ ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ break;
+
+ case SF2Region.GENERATOR_MODLFOTOFILTERFC:
+ id = ModelDestination.DESTINATION_FILTER_FREQ;
+ extrasrc[0] = new ModelSource(
+ ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ break;
+
+ case SF2Region.GENERATOR_MODLFOTOVOLUME:
+ id = ModelDestination.DESTINATION_GAIN;
+ amountcorrection[0] = -0.376287f;
+ extrasrc[0] = new ModelSource(
+ ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ break;
+
+ case SF2Region.GENERATOR_MODENVTOPITCH:
+ id = ModelDestination.DESTINATION_PITCH;
+ extrasrc[0] = new ModelSource(
+ ModelSource.SOURCE_EG2,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ break;
+
+ case SF2Region.GENERATOR_MODENVTOFILTERFC:
+ id = ModelDestination.DESTINATION_FILTER_FREQ;
+ extrasrc[0] = new ModelSource(
+ ModelSource.SOURCE_EG2,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR);
+ break;
+
+ default:
+ break;
+ }
+ if (id != null)
+ return new ModelDestination(id);
+ return null;
+ }
+
+ private void addTimecentValue(ModelPerformer performer,
+ ModelIdentifier dest, short value) {
+ double fvalue;
+ if (value == -12000)
+ fvalue = Double.NEGATIVE_INFINITY;
+ else
+ fvalue = value;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(fvalue, new ModelDestination(dest)));
+ }
+
+ private void addValue(ModelPerformer performer,
+ ModelIdentifier dest, short value) {
+ double fvalue = value;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(fvalue, new ModelDestination(dest)));
+ }
+
+ private void addValue(ModelPerformer performer,
+ ModelIdentifier dest, double value) {
+ double fvalue = value;
+ performer.getConnectionBlocks().add(
+ new ModelConnectionBlock(fvalue, new ModelDestination(dest)));
+ }
+
+ private short getGeneratorValue(Map<Integer, Short> generators, int gen) {
+ if (generators.containsKey(gen))
+ return generators.get(gen);
+ return SF2Region.getDefaultValue(gen);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2InstrumentRegion.java b/src/share/classes/com/sun/media/sound/SF2InstrumentRegion.java
new file mode 100644
index 000000000..a1a09eda5
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2InstrumentRegion.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Soundfont instrument region.
+ *
+ * @author Karl Helgason
+ */
+public class SF2InstrumentRegion extends SF2Region {
+
+ protected SF2Layer layer;
+
+ public SF2Layer getLayer() {
+ return layer;
+ }
+
+ public void setLayer(SF2Layer layer) {
+ this.layer = layer;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2Layer.java b/src/share/classes/com/sun/media/sound/SF2Layer.java
new file mode 100644
index 000000000..7af78a35f
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2Layer.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sound.midi.SoundbankResource;
+
+/**
+ * Soundfont layer.
+ *
+ * @author Karl Helgason
+ */
+public class SF2Layer extends SoundbankResource {
+
+ protected String name = "";
+ protected SF2GlobalRegion globalregion = null;
+ protected List<SF2LayerRegion> regions = new ArrayList<SF2LayerRegion>();
+
+ public SF2Layer(SF2Soundbank soundBank) {
+ super(soundBank, null, null);
+ }
+
+ public SF2Layer() {
+ super(null, null, null);
+ }
+
+ public Object getData() {
+ return null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<SF2LayerRegion> getRegions() {
+ return regions;
+ }
+
+ public SF2GlobalRegion getGlobalRegion() {
+ return globalregion;
+ }
+
+ public void setGlobalZone(SF2GlobalRegion zone) {
+ globalregion = zone;
+ }
+
+ public String toString() {
+ return "Layer: " + name;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2LayerRegion.java b/src/share/classes/com/sun/media/sound/SF2LayerRegion.java
new file mode 100644
index 000000000..c2006497e
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2LayerRegion.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Soundfont layer region.
+ *
+ * @author Karl Helgason
+ */
+public class SF2LayerRegion extends SF2Region {
+
+ protected SF2Sample sample;
+
+ public SF2Sample getSample() {
+ return sample;
+ }
+
+ public void setSample(SF2Sample sample) {
+ this.sample = sample;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2Modulator.java b/src/share/classes/com/sun/media/sound/SF2Modulator.java
new file mode 100644
index 000000000..4851fb127
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2Modulator.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Soundfont modulator container.
+ *
+ * @author Karl Helgason
+ */
+public class SF2Modulator {
+
+ public final static int SOURCE_NONE = 0;
+ public final static int SOURCE_NOTE_ON_VELOCITY = 2;
+ public final static int SOURCE_NOTE_ON_KEYNUMBER = 3;
+ public final static int SOURCE_POLY_PRESSURE = 10;
+ public final static int SOURCE_CHANNEL_PRESSURE = 13;
+ public final static int SOURCE_PITCH_WHEEL = 14;
+ public final static int SOURCE_PITCH_SENSITIVITY = 16;
+ public final static int SOURCE_MIDI_CONTROL = 128 * 1;
+ public final static int SOURCE_DIRECTION_MIN_MAX = 256 * 0;
+ public final static int SOURCE_DIRECTION_MAX_MIN = 256 * 1;
+ public final static int SOURCE_POLARITY_UNIPOLAR = 512 * 0;
+ public final static int SOURCE_POLARITY_BIPOLAR = 512 * 1;
+ public final static int SOURCE_TYPE_LINEAR = 1024 * 0;
+ public final static int SOURCE_TYPE_CONCAVE = 1024 * 1;
+ public final static int SOURCE_TYPE_CONVEX = 1024 * 2;
+ public final static int SOURCE_TYPE_SWITCH = 1024 * 3;
+ public final static int TRANSFORM_LINEAR = 0;
+ public final static int TRANSFORM_ABSOLUTE = 2;
+ protected int sourceOperator;
+ protected int destinationOperator;
+ protected short amount;
+ protected int amountSourceOperator;
+ protected int transportOperator;
+
+ public short getAmount() {
+ return amount;
+ }
+
+ public void setAmount(short amount) {
+ this.amount = amount;
+ }
+
+ public int getAmountSourceOperator() {
+ return amountSourceOperator;
+ }
+
+ public void setAmountSourceOperator(int amountSourceOperator) {
+ this.amountSourceOperator = amountSourceOperator;
+ }
+
+ public int getTransportOperator() {
+ return transportOperator;
+ }
+
+ public void setTransportOperator(int transportOperator) {
+ this.transportOperator = transportOperator;
+ }
+
+ public int getDestinationOperator() {
+ return destinationOperator;
+ }
+
+ public void setDestinationOperator(int destinationOperator) {
+ this.destinationOperator = destinationOperator;
+ }
+
+ public int getSourceOperator() {
+ return sourceOperator;
+ }
+
+ public void setSourceOperator(int sourceOperator) {
+ this.sourceOperator = sourceOperator;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2Region.java b/src/share/classes/com/sun/media/sound/SF2Region.java
new file mode 100644
index 000000000..3acef4382
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2Region.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Soundfont general region.
+ *
+ * @author Karl Helgason
+ */
+public class SF2Region {
+
+ public final static int GENERATOR_STARTADDRSOFFSET = 0;
+ public final static int GENERATOR_ENDADDRSOFFSET = 1;
+ public final static int GENERATOR_STARTLOOPADDRSOFFSET = 2;
+ public final static int GENERATOR_ENDLOOPADDRSOFFSET = 3;
+ public final static int GENERATOR_STARTADDRSCOARSEOFFSET = 4;
+ public final static int GENERATOR_MODLFOTOPITCH = 5;
+ public final static int GENERATOR_VIBLFOTOPITCH = 6;
+ public final static int GENERATOR_MODENVTOPITCH = 7;
+ public final static int GENERATOR_INITIALFILTERFC = 8;
+ public final static int GENERATOR_INITIALFILTERQ = 9;
+ public final static int GENERATOR_MODLFOTOFILTERFC = 10;
+ public final static int GENERATOR_MODENVTOFILTERFC = 11;
+ public final static int GENERATOR_ENDADDRSCOARSEOFFSET = 12;
+ public final static int GENERATOR_MODLFOTOVOLUME = 13;
+ public final static int GENERATOR_UNUSED1 = 14;
+ public final static int GENERATOR_CHORUSEFFECTSSEND = 15;
+ public final static int GENERATOR_REVERBEFFECTSSEND = 16;
+ public final static int GENERATOR_PAN = 17;
+ public final static int GENERATOR_UNUSED2 = 18;
+ public final static int GENERATOR_UNUSED3 = 19;
+ public final static int GENERATOR_UNUSED4 = 20;
+ public final static int GENERATOR_DELAYMODLFO = 21;
+ public final static int GENERATOR_FREQMODLFO = 22;
+ public final static int GENERATOR_DELAYVIBLFO = 23;
+ public final static int GENERATOR_FREQVIBLFO = 24;
+ public final static int GENERATOR_DELAYMODENV = 25;
+ public final static int GENERATOR_ATTACKMODENV = 26;
+ public final static int GENERATOR_HOLDMODENV = 27;
+ public final static int GENERATOR_DECAYMODENV = 28;
+ public final static int GENERATOR_SUSTAINMODENV = 29;
+ public final static int GENERATOR_RELEASEMODENV = 30;
+ public final static int GENERATOR_KEYNUMTOMODENVHOLD = 31;
+ public final static int GENERATOR_KEYNUMTOMODENVDECAY = 32;
+ public final static int GENERATOR_DELAYVOLENV = 33;
+ public final static int GENERATOR_ATTACKVOLENV = 34;
+ public final static int GENERATOR_HOLDVOLENV = 35;
+ public final static int GENERATOR_DECAYVOLENV = 36;
+ public final static int GENERATOR_SUSTAINVOLENV = 37;
+ public final static int GENERATOR_RELEASEVOLENV = 38;
+ public final static int GENERATOR_KEYNUMTOVOLENVHOLD = 39;
+ public final static int GENERATOR_KEYNUMTOVOLENVDECAY = 40;
+ public final static int GENERATOR_INSTRUMENT = 41;
+ public final static int GENERATOR_RESERVED1 = 42;
+ public final static int GENERATOR_KEYRANGE = 43;
+ public final static int GENERATOR_VELRANGE = 44;
+ public final static int GENERATOR_STARTLOOPADDRSCOARSEOFFSET = 45;
+ public final static int GENERATOR_KEYNUM = 46;
+ public final static int GENERATOR_VELOCITY = 47;
+ public final static int GENERATOR_INITIALATTENUATION = 48;
+ public final static int GENERATOR_RESERVED2 = 49;
+ public final static int GENERATOR_ENDLOOPADDRSCOARSEOFFSET = 50;
+ public final static int GENERATOR_COARSETUNE = 51;
+ public final static int GENERATOR_FINETUNE = 52;
+ public final static int GENERATOR_SAMPLEID = 53;
+ public final static int GENERATOR_SAMPLEMODES = 54;
+ public final static int GENERATOR_RESERVED3 = 55;
+ public final static int GENERATOR_SCALETUNING = 56;
+ public final static int GENERATOR_EXCLUSIVECLASS = 57;
+ public final static int GENERATOR_OVERRIDINGROOTKEY = 58;
+ public final static int GENERATOR_UNUSED5 = 59;
+ public final static int GENERATOR_ENDOPR = 60;
+ protected Map<Integer, Short> generators = new HashMap<Integer, Short>();
+ protected List<SF2Modulator> modulators = new ArrayList<SF2Modulator>();
+
+ public Map<Integer, Short> getGenerators() {
+ return generators;
+ }
+
+ public boolean contains(int generator) {
+ return generators.containsKey(generator);
+ }
+
+ static public short getDefaultValue(int generator) {
+ if (generator == 8) return (short)13500;
+ if (generator == 21) return (short)-12000;
+ if (generator == 23) return (short)-12000;
+ if (generator == 25) return (short)-12000;
+ if (generator == 26) return (short)-12000;
+ if (generator == 27) return (short)-12000;
+ if (generator == 28) return (short)-12000;
+ if (generator == 30) return (short)-12000;
+ if (generator == 33) return (short)-12000;
+ if (generator == 34) return (short)-12000;
+ if (generator == 35) return (short)-12000;
+ if (generator == 36) return (short)-12000;
+ if (generator == 38) return (short)-12000;
+ if (generator == 43) return (short)0x7F00;
+ if (generator == 44) return (short)0x7F00;
+ if (generator == 46) return (short)-1;
+ if (generator == 47) return (short)-1;
+ if (generator == 56) return (short)100;
+ if (generator == 58) return (short)-1;
+ return 0;
+ }
+
+ public short getShort(int generator) {
+ if (!contains(generator))
+ return getDefaultValue(generator);
+ return generators.get(generator);
+ }
+
+ public void putShort(int generator, short value) {
+ generators.put(generator, value);
+ }
+
+ public byte[] getBytes(int generator) {
+ int val = getInteger(generator);
+ byte[] bytes = new byte[2];
+ bytes[0] = (byte) (0xFF & val);
+ bytes[1] = (byte) ((0xFF00 & val) >> 8);
+ return bytes;
+ }
+
+ public void putBytes(int generator, byte[] bytes) {
+ generators.put(generator, (short) (bytes[0] + (bytes[1] << 8)));
+ }
+
+ public int getInteger(int generator) {
+ return 0xFFFF & getShort(generator);
+ }
+
+ public void putInteger(int generator, int value) {
+ generators.put(generator, (short) value);
+ }
+
+ public List<SF2Modulator> getModulators() {
+ return modulators;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2Sample.java b/src/share/classes/com/sun/media/sound/SF2Sample.java
new file mode 100644
index 000000000..582b34615
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2Sample.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.InputStream;
+
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.SoundbankResource;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+
+/**
+ * Soundfont sample storage.
+ *
+ * @author Karl Helgason
+ */
+public class SF2Sample extends SoundbankResource {
+
+ protected String name = "";
+ protected long startLoop = 0;
+ protected long endLoop = 0;
+ protected long sampleRate = 44100;
+ protected int originalPitch = 60;
+ protected byte pitchCorrection = 0;
+ protected int sampleLink = 0;
+ protected int sampleType = 0;
+ protected ModelByteBuffer data;
+ protected ModelByteBuffer data24;
+
+ public SF2Sample(Soundbank soundBank) {
+ super(soundBank, null, AudioInputStream.class);
+ }
+
+ public SF2Sample() {
+ super(null, null, AudioInputStream.class);
+ }
+
+ public Object getData() {
+
+ AudioFormat format = getFormat();
+ /*
+ if (sampleFile != null) {
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(sampleFile);
+ RIFFReader riff = new RIFFReader(fis);
+ if (!riff.getFormat().equals("RIFF")) {
+ throw new RIFFInvalidDataException(
+ "Input stream is not a valid RIFF stream!");
+ }
+ if (!riff.getType().equals("sfbk")) {
+ throw new RIFFInvalidDataException(
+ "Input stream is not a valid SoundFont!");
+ }
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ if (chunk.getFormat().equals("LIST")) {
+ if (chunk.getType().equals("sdta")) {
+ while(chunk.hasNextChunk()) {
+ RIFFReader chunkchunk = chunk.nextChunk();
+ if(chunkchunk.getFormat().equals("smpl")) {
+ chunkchunk.skip(sampleOffset);
+ return new AudioInputStream(chunkchunk,
+ format, sampleLen);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ return new Throwable(e.toString());
+ }
+ }
+ */
+ InputStream is = data.getInputStream();
+ if (is == null)
+ return null;
+ return new AudioInputStream(is, format, data.capacity());
+ }
+
+ public ModelByteBuffer getDataBuffer() {
+ return data;
+ }
+
+ public ModelByteBuffer getData24Buffer() {
+ return data24;
+ }
+
+ public AudioFormat getFormat() {
+ return new AudioFormat(sampleRate, 16, 1, true, false);
+ }
+
+ public void setData(ModelByteBuffer data) {
+ this.data = data;
+ }
+
+ public void setData(byte[] data) {
+ this.data = new ModelByteBuffer(data);
+ }
+
+ public void setData(byte[] data, int offset, int length) {
+ this.data = new ModelByteBuffer(data, offset, length);
+ }
+
+ public void setData24(ModelByteBuffer data24) {
+ this.data24 = data24;
+ }
+
+ public void setData24(byte[] data24) {
+ this.data24 = new ModelByteBuffer(data24);
+ }
+
+ public void setData24(byte[] data24, int offset, int length) {
+ this.data24 = new ModelByteBuffer(data24, offset, length);
+ }
+
+ /*
+ public void setData(File file, int offset, int length) {
+ this.data = null;
+ this.sampleFile = file;
+ this.sampleOffset = offset;
+ this.sampleLen = length;
+ }
+ */
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getEndLoop() {
+ return endLoop;
+ }
+
+ public void setEndLoop(long endLoop) {
+ this.endLoop = endLoop;
+ }
+
+ public int getOriginalPitch() {
+ return originalPitch;
+ }
+
+ public void setOriginalPitch(int originalPitch) {
+ this.originalPitch = originalPitch;
+ }
+
+ public byte getPitchCorrection() {
+ return pitchCorrection;
+ }
+
+ public void setPitchCorrection(byte pitchCorrection) {
+ this.pitchCorrection = pitchCorrection;
+ }
+
+ public int getSampleLink() {
+ return sampleLink;
+ }
+
+ public void setSampleLink(int sampleLink) {
+ this.sampleLink = sampleLink;
+ }
+
+ public long getSampleRate() {
+ return sampleRate;
+ }
+
+ public void setSampleRate(long sampleRate) {
+ this.sampleRate = sampleRate;
+ }
+
+ public int getSampleType() {
+ return sampleType;
+ }
+
+ public void setSampleType(int sampleType) {
+ this.sampleType = sampleType;
+ }
+
+ public long getStartLoop() {
+ return startLoop;
+ }
+
+ public void setStartLoop(long startLoop) {
+ this.startLoop = startLoop;
+ }
+
+ public String toString() {
+ return "Sample: " + name;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2Soundbank.java b/src/share/classes/com/sun/media/sound/SF2Soundbank.java
new file mode 100644
index 000000000..7ae60d870
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2Soundbank.java
@@ -0,0 +1,973 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.sound.midi.Instrument;
+import javax.sound.midi.Patch;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.SoundbankResource;
+
+/**
+ * A SoundFont 2.04 soundbank reader.
+ *
+ * Based on SoundFont 2.04 specification from:
+ * <p> http://developer.creative.com <br>
+ * http://www.soundfont.com/ ;
+ *
+ * @author Karl Helgason
+ */
+public class SF2Soundbank implements Soundbank {
+
+ // version of the Sound Font RIFF file
+ protected int major = 2;
+ protected int minor = 1;
+ // target Sound Engine
+ protected String targetEngine = "EMU8000";
+ // Sound Font Bank Name
+ protected String name = "untitled";
+ // Sound ROM Name
+ protected String romName = null;
+ // Sound ROM Version
+ protected int romVersionMajor = -1;
+ protected int romVersionMinor = -1;
+ // Date of Creation of the Bank
+ protected String creationDate = null;
+ // Sound Designers and Engineers for the Bank
+ protected String engineers = null;
+ // Product for which the Bank was intended
+ protected String product = null;
+ // Copyright message
+ protected String copyright = null;
+ // Comments
+ protected String comments = null;
+ // The SoundFont tools used to create and alter the bank
+ protected String tools = null;
+ // The Sample Data loaded from the SoundFont
+ private ModelByteBuffer sampleData = null;
+ private ModelByteBuffer sampleData24 = null;
+ private File sampleFile = null;
+ private boolean largeFormat = false;
+ private List<SF2Instrument> instruments = new ArrayList<SF2Instrument>();
+ private List<SF2Layer> layers = new ArrayList<SF2Layer>();
+ private List<SF2Sample> samples = new ArrayList<SF2Sample>();
+
+ public SF2Soundbank() {
+ }
+
+ public SF2Soundbank(URL url) throws IOException {
+
+ InputStream is = url.openStream();
+ try {
+ readSoundbank(is);
+ } finally {
+ is.close();
+ }
+ }
+
+ public SF2Soundbank(File file) throws IOException {
+ largeFormat = true;
+ sampleFile = file;
+ InputStream is = new FileInputStream(file);
+ try {
+ readSoundbank(is);
+ } finally {
+ is.close();
+ }
+ }
+
+ public SF2Soundbank(InputStream inputstream) throws IOException {
+ readSoundbank(inputstream);
+ }
+
+ private void readSoundbank(InputStream inputstream) throws IOException {
+ RIFFReader riff = new RIFFReader(inputstream);
+ if (!riff.getFormat().equals("RIFF")) {
+ throw new RIFFInvalidFormatException(
+ "Input stream is not a valid RIFF stream!");
+ }
+ if (!riff.getType().equals("sfbk")) {
+ throw new RIFFInvalidFormatException(
+ "Input stream is not a valid SoundFont!");
+ }
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ if (chunk.getFormat().equals("LIST")) {
+ if (chunk.getType().equals("INFO"))
+ readInfoChunk(chunk);
+ if (chunk.getType().equals("sdta"))
+ readSdtaChunk(chunk);
+ if (chunk.getType().equals("pdta"))
+ readPdtaChunk(chunk);
+ }
+ }
+ }
+
+ private void readInfoChunk(RIFFReader riff) throws IOException {
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("ifil")) {
+ major = chunk.readUnsignedShort();
+ minor = chunk.readUnsignedShort();
+ } else if (format.equals("isng")) {
+ this.targetEngine = chunk.readString(chunk.available());
+ } else if (format.equals("INAM")) {
+ this.name = chunk.readString(chunk.available());
+ } else if (format.equals("irom")) {
+ this.romName = chunk.readString(chunk.available());
+ } else if (format.equals("iver")) {
+ romVersionMajor = chunk.readUnsignedShort();
+ romVersionMinor = chunk.readUnsignedShort();
+ } else if (format.equals("ICRD")) {
+ this.creationDate = chunk.readString(chunk.available());
+ } else if (format.equals("IENG")) {
+ this.engineers = chunk.readString(chunk.available());
+ } else if (format.equals("IPRD")) {
+ this.product = chunk.readString(chunk.available());
+ } else if (format.equals("ICOP")) {
+ this.copyright = chunk.readString(chunk.available());
+ } else if (format.equals("ICMT")) {
+ this.comments = chunk.readString(chunk.available());
+ } else if (format.equals("ISFT")) {
+ this.tools = chunk.readString(chunk.available());
+ }
+
+ }
+ }
+
+ private void readSdtaChunk(RIFFReader riff) throws IOException {
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ if (chunk.getFormat().equals("smpl")) {
+ if (!largeFormat) {
+ byte[] sampleData = new byte[chunk.available()];
+
+ int read = 0;
+ int avail = chunk.available();
+ while (read != avail) {
+ if (avail - read > 65536) {
+ chunk.readFully(sampleData, read, 65536);
+ read += 65536;
+ } else {
+ chunk.readFully(sampleData, read, avail - read);
+ read = avail;
+ }
+
+ }
+ this.sampleData = new ModelByteBuffer(sampleData);
+ //chunk.read(sampleData);
+ } else {
+ this.sampleData = new ModelByteBuffer(sampleFile,
+ chunk.getFilePointer(), chunk.available());
+ }
+ }
+ if (chunk.getFormat().equals("sm24")) {
+ if (!largeFormat) {
+ byte[] sampleData24 = new byte[chunk.available()];
+ //chunk.read(sampleData24);
+
+ int read = 0;
+ int avail = chunk.available();
+ while (read != avail) {
+ if (avail - read > 65536) {
+ chunk.readFully(sampleData24, read, 65536);
+ read += 65536;
+ } else {
+ chunk.readFully(sampleData24, read, avail - read);
+ read = avail;
+ }
+
+ }
+ this.sampleData24 = new ModelByteBuffer(sampleData24);
+ } else {
+ this.sampleData24 = new ModelByteBuffer(sampleFile,
+ chunk.getFilePointer(), chunk.available());
+ }
+
+ }
+ }
+ }
+
+ private void readPdtaChunk(RIFFReader riff) throws IOException {
+
+ List<SF2Instrument> presets = new ArrayList<SF2Instrument>();
+ List<Integer> presets_bagNdx = new ArrayList<Integer>();
+ List<SF2InstrumentRegion> presets_splits_gen
+ = new ArrayList<SF2InstrumentRegion>();
+ List<SF2InstrumentRegion> presets_splits_mod
+ = new ArrayList<SF2InstrumentRegion>();
+
+ List<SF2Layer> instruments = new ArrayList<SF2Layer>();
+ List<Integer> instruments_bagNdx = new ArrayList<Integer>();
+ List<SF2LayerRegion> instruments_splits_gen
+ = new ArrayList<SF2LayerRegion>();
+ List<SF2LayerRegion> instruments_splits_mod
+ = new ArrayList<SF2LayerRegion>();
+
+ while (riff.hasNextChunk()) {
+ RIFFReader chunk = riff.nextChunk();
+ String format = chunk.getFormat();
+ if (format.equals("phdr")) {
+ // Preset Header / Instrument
+ if (chunk.available() % 38 != 0)
+ throw new RIFFInvalidDataException();
+ int count = chunk.available() / 38;
+ for (int i = 0; i < count; i++) {
+ SF2Instrument preset = new SF2Instrument(this);
+ preset.name = chunk.readString(20);
+ preset.preset = chunk.readUnsignedShort();
+ preset.bank = chunk.readUnsignedShort();
+ presets_bagNdx.add(chunk.readUnsignedShort());
+ preset.library = chunk.readUnsignedInt();
+ preset.genre = chunk.readUnsignedInt();
+ preset.morphology = chunk.readUnsignedInt();
+ presets.add(preset);
+ if (i != count - 1)
+ this.instruments.add(preset);
+ }
+ } else if (format.equals("pbag")) {
+ // Preset Zones / Instruments splits
+ if (chunk.available() % 4 != 0)
+ throw new RIFFInvalidDataException();
+ int count = chunk.available() / 4;
+
+ // Skip first record
+ {
+ int gencount = chunk.readUnsignedShort();
+ int modcount = chunk.readUnsignedShort();
+ while (presets_splits_gen.size() < gencount)
+ presets_splits_gen.add(null);
+ while (presets_splits_mod.size() < modcount)
+ presets_splits_mod.add(null);
+ count--;
+ }
+
+ int offset = presets_bagNdx.get(0);
+ // Offset should be 0 (but just case)
+ for (int i = 0; i < offset; i++) {
+ if (count == 0)
+ throw new RIFFInvalidDataException();
+ int gencount = chunk.readUnsignedShort();
+ int modcount = chunk.readUnsignedShort();
+ while (presets_splits_gen.size() < gencount)
+ presets_splits_gen.add(null);
+ while (presets_splits_mod.size() < modcount)
+ presets_splits_mod.add(null);
+ count--;
+ }
+
+ for (int i = 0; i < presets_bagNdx.size() - 1; i++) {
+ int zone_count = presets_bagNdx.get(i + 1)
+ - presets_bagNdx.get(i);
+ SF2Instrument preset = presets.get(i);
+ for (int ii = 0; ii < zone_count; ii++) {
+ if (count == 0)
+ throw new RIFFInvalidDataException();
+ int gencount = chunk.readUnsignedShort();
+ int modcount = chunk.readUnsignedShort();
+ SF2InstrumentRegion split = new SF2InstrumentRegion();
+ preset.regions.add(split);
+ while (presets_splits_gen.size() < gencount)
+ presets_splits_gen.add(split);
+ while (presets_splits_mod.size() < modcount)
+ presets_splits_mod.add(split);
+ count--;
+ }
+ }
+ } else if (format.equals("pmod")) {
+ // Preset Modulators / Split Modulators
+ for (int i = 0; i < presets_splits_mod.size(); i++) {
+ SF2Modulator modulator = new SF2Modulator();
+ modulator.sourceOperator = chunk.readUnsignedShort();
+ modulator.destinationOperator = chunk.readUnsignedShort();
+ modulator.amount = chunk.readShort();
+ modulator.amountSourceOperator = chunk.readUnsignedShort();
+ modulator.transportOperator = chunk.readUnsignedShort();
+ SF2InstrumentRegion split = presets_splits_mod.get(i);
+ if (split != null)
+ split.modulators.add(modulator);
+ }
+ } else if (format.equals("pgen")) {
+ // Preset Generators / Split Generators
+ for (int i = 0; i < presets_splits_gen.size(); i++) {
+ int operator = chunk.readUnsignedShort();
+ short amount = chunk.readShort();
+ SF2InstrumentRegion split = presets_splits_gen.get(i);
+ if (split != null)
+ split.generators.put(operator, amount);
+ }
+ } else if (format.equals("inst")) {
+ // Instrument Header / Layers
+ if (chunk.available() % 22 != 0)
+ throw new RIFFInvalidDataException();
+ int count = chunk.available() / 22;
+ for (int i = 0; i < count; i++) {
+ SF2Layer layer = new SF2Layer(this);
+ layer.name = chunk.readString(20);
+ instruments_bagNdx.add(chunk.readUnsignedShort());
+ instruments.add(layer);
+ if (i != count - 1)
+ this.layers.add(layer);
+ }
+ } else if (format.equals("ibag")) {
+ // Instrument Zones / Layer splits
+ if (chunk.available() % 4 != 0)
+ throw new RIFFInvalidDataException();
+ int count = chunk.available() / 4;
+
+ // Skip first record
+ {
+ int gencount = chunk.readUnsignedShort();
+ int modcount = chunk.readUnsignedShort();
+ while (instruments_splits_gen.size() < gencount)
+ instruments_splits_gen.add(null);
+ while (instruments_splits_mod.size() < modcount)
+ instruments_splits_mod.add(null);
+ count--;
+ }
+
+ int offset = instruments_bagNdx.get(0);
+ // Offset should be 0 (but just case)
+ for (int i = 0; i < offset; i++) {
+ if (count == 0)
+ throw new RIFFInvalidDataException();
+ int gencount = chunk.readUnsignedShort();
+ int modcount = chunk.readUnsignedShort();
+ while (instruments_splits_gen.size() < gencount)
+ instruments_splits_gen.add(null);
+ while (instruments_splits_mod.size() < modcount)
+ instruments_splits_mod.add(null);
+ count--;
+ }
+
+ for (int i = 0; i < instruments_bagNdx.size() - 1; i++) {
+ int zone_count = instruments_bagNdx.get(i + 1) - instruments_bagNdx.get(i);
+ SF2Layer layer = layers.get(i);
+ for (int ii = 0; ii < zone_count; ii++) {
+ if (count == 0)
+ throw new RIFFInvalidDataException();
+ int gencount = chunk.readUnsignedShort();
+ int modcount = chunk.readUnsignedShort();
+ SF2LayerRegion split = new SF2LayerRegion();
+ layer.regions.add(split);
+ while (instruments_splits_gen.size() < gencount)
+ instruments_splits_gen.add(split);
+ while (instruments_splits_mod.size() < modcount)
+ instruments_splits_mod.add(split);
+ count--;
+ }
+ }
+
+ } else if (format.equals("imod")) {
+ // Instrument Modulators / Split Modulators
+ for (int i = 0; i < instruments_splits_mod.size(); i++) {
+ SF2Modulator modulator = new SF2Modulator();
+ modulator.sourceOperator = chunk.readUnsignedShort();
+ modulator.destinationOperator = chunk.readUnsignedShort();
+ modulator.amount = chunk.readShort();
+ modulator.amountSourceOperator = chunk.readUnsignedShort();
+ modulator.transportOperator = chunk.readUnsignedShort();
+ SF2LayerRegion split = instruments_splits_gen.get(i);
+ if (split != null)
+ split.modulators.add(modulator);
+ }
+ } else if (format.equals("igen")) {
+ // Instrument Generators / Split Generators
+ for (int i = 0; i < instruments_splits_gen.size(); i++) {
+ int operator = chunk.readUnsignedShort();
+ short amount = chunk.readShort();
+ SF2LayerRegion split = instruments_splits_gen.get(i);
+ if (split != null)
+ split.generators.put(operator, amount);
+ }
+ } else if (format.equals("shdr")) {
+ // Sample Headers
+ if (chunk.available() % 46 != 0)
+ throw new RIFFInvalidDataException();
+ int count = chunk.available() / 46;
+ for (int i = 0; i < count; i++) {
+ SF2Sample sample = new SF2Sample(this);
+ sample.name = chunk.readString(20);
+ long start = chunk.readUnsignedInt();
+ long end = chunk.readUnsignedInt();
+ sample.data = sampleData.subbuffer(start * 2, end * 2, true);
+ if (sampleData24 != null)
+ sample.data24 = sampleData24.subbuffer(start, end, true);
+ /*
+ sample.data = new ModelByteBuffer(sampleData, (int)(start*2),
+ (int)((end - start)*2));
+ if (sampleData24 != null)
+ sample.data24 = new ModelByteBuffer(sampleData24,
+ (int)start, (int)(end - start));
+ */
+ sample.startLoop = chunk.readUnsignedInt() - start;
+ sample.endLoop = chunk.readUnsignedInt() - start;
+ if (sample.startLoop < 0)
+ sample.startLoop = -1;
+ if (sample.endLoop < 0)
+ sample.endLoop = -1;
+ sample.sampleRate = chunk.readUnsignedInt();
+ sample.originalPitch = chunk.readUnsignedByte();
+ sample.pitchCorrection = chunk.readByte();
+ sample.sampleLink = chunk.readUnsignedShort();
+ sample.sampleType = chunk.readUnsignedShort();
+ if (i != count - 1)
+ this.samples.add(sample);
+ }
+ }
+ }
+
+ Iterator<SF2Layer> liter = this.layers.iterator();
+ while (liter.hasNext()) {
+ SF2Layer layer = liter.next();
+ Iterator<SF2LayerRegion> siter = layer.regions.iterator();
+ SF2Region globalsplit = null;
+ while (siter.hasNext()) {
+ SF2LayerRegion split = siter.next();
+ if (split.generators.get(SF2LayerRegion.GENERATOR_SAMPLEID) != null) {
+ int sampleid = split.generators.get(
+ SF2LayerRegion.GENERATOR_SAMPLEID);
+ split.generators.remove(SF2LayerRegion.GENERATOR_SAMPLEID);
+ split.sample = samples.get(sampleid);
+ } else {
+ globalsplit = split;
+ }
+ }
+ if (globalsplit != null) {
+ layer.getRegions().remove(globalsplit);
+ SF2GlobalRegion gsplit = new SF2GlobalRegion();
+ gsplit.generators = globalsplit.generators;
+ gsplit.modulators = globalsplit.modulators;
+ layer.setGlobalZone(gsplit);
+ }
+ }
+
+
+ Iterator<SF2Instrument> iiter = this.instruments.iterator();
+ while (iiter.hasNext()) {
+ SF2Instrument instrument = iiter.next();
+ Iterator<SF2InstrumentRegion> siter = instrument.regions.iterator();
+ SF2Region globalsplit = null;
+ while (siter.hasNext()) {
+ SF2InstrumentRegion split = siter.next();
+ if (split.generators.get(SF2LayerRegion.GENERATOR_INSTRUMENT) != null) {
+ int instrumentid = split.generators.get(
+ SF2InstrumentRegion.GENERATOR_INSTRUMENT);
+ split.generators.remove(SF2LayerRegion.GENERATOR_INSTRUMENT);
+ split.layer = layers.get(instrumentid);
+ } else {
+ globalsplit = split;
+ }
+ }
+
+ if (globalsplit != null) {
+ instrument.getRegions().remove(globalsplit);
+ SF2GlobalRegion gsplit = new SF2GlobalRegion();
+ gsplit.generators = globalsplit.generators;
+ gsplit.modulators = globalsplit.modulators;
+ instrument.setGlobalZone(gsplit);
+ }
+ }
+
+ }
+
+ public void save(String name) throws IOException {
+ writeSoundbank(new RIFFWriter(name, "sfbk"));
+ }
+
+ public void save(File file) throws IOException {
+ writeSoundbank(new RIFFWriter(file, "sfbk"));
+ }
+
+ public void save(OutputStream out) throws IOException {
+ writeSoundbank(new RIFFWriter(out, "sfbk"));
+ }
+
+ private void writeSoundbank(RIFFWriter writer) throws IOException {
+ writeInfo(writer.writeList("INFO"));
+ writeSdtaChunk(writer.writeList("sdta"));
+ writePdtaChunk(writer.writeList("pdta"));
+ writer.close();
+ }
+
+ private void writeInfoStringChunk(RIFFWriter writer, String name,
+ String value) throws IOException {
+ if (value == null)
+ return;
+ RIFFWriter chunk = writer.writeChunk(name);
+ chunk.writeString(value);
+ int len = value.getBytes("ascii").length;
+ chunk.write(0);
+ len++;
+ if (len % 2 != 0)
+ chunk.write(0);
+ }
+
+ private void writeInfo(RIFFWriter writer) throws IOException {
+ if (this.targetEngine == null)
+ this.targetEngine = "EMU8000";
+ if (this.name == null)
+ this.name = "";
+
+ RIFFWriter ifil_chunk = writer.writeChunk("ifil");
+ ifil_chunk.writeUnsignedShort(this.major);
+ ifil_chunk.writeUnsignedShort(this.minor);
+ writeInfoStringChunk(writer, "isng", this.targetEngine);
+ writeInfoStringChunk(writer, "INAM", this.name);
+ writeInfoStringChunk(writer, "irom", this.romName);
+ if (romVersionMajor != -1) {
+ RIFFWriter iver_chunk = writer.writeChunk("iver");
+ iver_chunk.writeUnsignedShort(this.romVersionMajor);
+ iver_chunk.writeUnsignedShort(this.romVersionMinor);
+ }
+ writeInfoStringChunk(writer, "ICRD", this.creationDate);
+ writeInfoStringChunk(writer, "IENG", this.engineers);
+ writeInfoStringChunk(writer, "IPRD", this.product);
+ writeInfoStringChunk(writer, "ICOP", this.copyright);
+ writeInfoStringChunk(writer, "ICMT", this.comments);
+ writeInfoStringChunk(writer, "ISFT", this.tools);
+
+ writer.close();
+ }
+
+ private void writeSdtaChunk(RIFFWriter writer) throws IOException {
+
+ byte[] pad = new byte[32];
+
+ RIFFWriter smpl_chunk = writer.writeChunk("smpl");
+ for (SF2Sample sample : samples) {
+ ModelByteBuffer data = sample.getDataBuffer();
+ data.writeTo(smpl_chunk);
+ /*
+ smpl_chunk.write(data.array(),
+ data.arrayOffset(),
+ data.capacity());
+ */
+ smpl_chunk.write(pad);
+ smpl_chunk.write(pad);
+ }
+ if (major < 2)
+ return;
+ if (major == 2 && minor < 4)
+ return;
+
+
+ for (SF2Sample sample : samples) {
+ ModelByteBuffer data24 = sample.getData24Buffer();
+ if (data24 == null)
+ return;
+ }
+
+ RIFFWriter sm24_chunk = writer.writeChunk("sm24");
+ for (SF2Sample sample : samples) {
+ ModelByteBuffer data = sample.getData24Buffer();
+ data.writeTo(sm24_chunk);
+ /*
+ sm24_chunk.write(data.array(),
+ data.arrayOffset(),
+ data.capacity());*/
+ smpl_chunk.write(pad);
+ }
+ }
+
+ private void writeModulators(RIFFWriter writer, List<SF2Modulator> modulators)
+ throws IOException {
+ for (SF2Modulator modulator : modulators) {
+ writer.writeUnsignedShort(modulator.sourceOperator);
+ writer.writeUnsignedShort(modulator.destinationOperator);
+ writer.writeShort(modulator.amount);
+ writer.writeUnsignedShort(modulator.amountSourceOperator);
+ writer.writeUnsignedShort(modulator.transportOperator);
+ }
+ }
+
+ private void writeGenerators(RIFFWriter writer, Map<Integer, Short> generators)
+ throws IOException {
+ Short keyrange = (Short) generators.get(SF2Region.GENERATOR_KEYRANGE);
+ Short velrange = (Short) generators.get(SF2Region.GENERATOR_VELRANGE);
+ if (keyrange != null) {
+ writer.writeUnsignedShort(SF2Region.GENERATOR_KEYRANGE);
+ writer.writeShort(keyrange);
+ }
+ if (velrange != null) {
+ writer.writeUnsignedShort(SF2Region.GENERATOR_VELRANGE);
+ writer.writeShort(velrange);
+ }
+ for (Map.Entry<Integer, Short> generator : generators.entrySet()) {
+ if (generator.getKey() == SF2Region.GENERATOR_KEYRANGE)
+ continue;
+ if (generator.getKey() == SF2Region.GENERATOR_VELRANGE)
+ continue;
+ writer.writeUnsignedShort(generator.getKey());
+ writer.writeShort(generator.getValue());
+ }
+ }
+
+ private void writePdtaChunk(RIFFWriter writer) throws IOException {
+
+ RIFFWriter phdr_chunk = writer.writeChunk("phdr");
+ int phdr_zone_count = 0;
+ for (SF2Instrument preset : this.instruments) {
+ phdr_chunk.writeString(preset.name, 20);
+ phdr_chunk.writeUnsignedShort(preset.preset);
+ phdr_chunk.writeUnsignedShort(preset.bank);
+ phdr_chunk.writeUnsignedShort(phdr_zone_count);
+ if (preset.getGlobalRegion() != null)
+ phdr_zone_count += 1;
+ phdr_zone_count += preset.getRegions().size();
+ phdr_chunk.writeUnsignedInt(preset.library);
+ phdr_chunk.writeUnsignedInt(preset.genre);
+ phdr_chunk.writeUnsignedInt(preset.morphology);
+ }
+ phdr_chunk.writeString("EOP", 20);
+ phdr_chunk.writeUnsignedShort(0);
+ phdr_chunk.writeUnsignedShort(0);
+ phdr_chunk.writeUnsignedShort(phdr_zone_count);
+ phdr_chunk.writeUnsignedInt(0);
+ phdr_chunk.writeUnsignedInt(0);
+ phdr_chunk.writeUnsignedInt(0);
+
+
+ RIFFWriter pbag_chunk = writer.writeChunk("pbag");
+ int pbag_gencount = 0;
+ int pbag_modcount = 0;
+ for (SF2Instrument preset : this.instruments) {
+ if (preset.getGlobalRegion() != null) {
+ pbag_chunk.writeUnsignedShort(pbag_gencount);
+ pbag_chunk.writeUnsignedShort(pbag_modcount);
+ pbag_gencount += preset.getGlobalRegion().getGenerators().size();
+ pbag_modcount += preset.getGlobalRegion().getModulators().size();
+ }
+ for (SF2InstrumentRegion region : preset.getRegions()) {
+ pbag_chunk.writeUnsignedShort(pbag_gencount);
+ pbag_chunk.writeUnsignedShort(pbag_modcount);
+ if (layers.indexOf(region.layer) != -1) {
+ // One generator is used to reference to instrument record
+ pbag_gencount += 1;
+ }
+ pbag_gencount += region.getGenerators().size();
+ pbag_modcount += region.getModulators().size();
+
+ }
+ }
+ pbag_chunk.writeUnsignedShort(pbag_gencount);
+ pbag_chunk.writeUnsignedShort(pbag_modcount);
+
+ RIFFWriter pmod_chunk = writer.writeChunk("pmod");
+ for (SF2Instrument preset : this.instruments) {
+ if (preset.getGlobalRegion() != null) {
+ writeModulators(pmod_chunk,
+ preset.getGlobalRegion().getModulators());
+ }
+ for (SF2InstrumentRegion region : preset.getRegions())
+ writeModulators(pmod_chunk, region.getModulators());
+ }
+ pmod_chunk.write(new byte[10]);
+
+ RIFFWriter pgen_chunk = writer.writeChunk("pgen");
+ for (SF2Instrument preset : this.instruments) {
+ if (preset.getGlobalRegion() != null) {
+ writeGenerators(pgen_chunk,
+ preset.getGlobalRegion().getGenerators());
+ }
+ for (SF2InstrumentRegion region : preset.getRegions()) {
+ writeGenerators(pgen_chunk, region.getGenerators());
+ int ix = (int) layers.indexOf(region.layer);
+ if (ix != -1) {
+ pgen_chunk.writeUnsignedShort(SF2Region.GENERATOR_INSTRUMENT);
+ pgen_chunk.writeShort((short) ix);
+ }
+ }
+ }
+ pgen_chunk.write(new byte[4]);
+
+ RIFFWriter inst_chunk = writer.writeChunk("inst");
+ int inst_zone_count = 0;
+ for (SF2Layer instrument : this.layers) {
+ inst_chunk.writeString(instrument.name, 20);
+ inst_chunk.writeUnsignedShort(inst_zone_count);
+ if (instrument.getGlobalRegion() != null)
+ inst_zone_count += 1;
+ inst_zone_count += instrument.getRegions().size();
+ }
+ inst_chunk.writeString("EOI", 20);
+ inst_chunk.writeUnsignedShort(inst_zone_count);
+
+
+ RIFFWriter ibag_chunk = writer.writeChunk("ibag");
+ int ibag_gencount = 0;
+ int ibag_modcount = 0;
+ for (SF2Layer instrument : this.layers) {
+ if (instrument.getGlobalRegion() != null) {
+ ibag_chunk.writeUnsignedShort(ibag_gencount);
+ ibag_chunk.writeUnsignedShort(ibag_modcount);
+ ibag_gencount
+ += instrument.getGlobalRegion().getGenerators().size();
+ ibag_modcount
+ += instrument.getGlobalRegion().getModulators().size();
+ }
+ for (SF2LayerRegion region : instrument.getRegions()) {
+ ibag_chunk.writeUnsignedShort(ibag_gencount);
+ ibag_chunk.writeUnsignedShort(ibag_modcount);
+ if (samples.indexOf(region.sample) != -1) {
+ // One generator is used to reference to instrument record
+ ibag_gencount += 1;
+ }
+ ibag_gencount += region.getGenerators().size();
+ ibag_modcount += region.getModulators().size();
+
+ }
+ }
+ ibag_chunk.writeUnsignedShort(ibag_gencount);
+ ibag_chunk.writeUnsignedShort(ibag_modcount);
+
+
+ RIFFWriter imod_chunk = writer.writeChunk("imod");
+ for (SF2Layer instrument : this.layers) {
+ if (instrument.getGlobalRegion() != null) {
+ writeModulators(imod_chunk,
+ instrument.getGlobalRegion().getModulators());
+ }
+ for (SF2LayerRegion region : instrument.getRegions())
+ writeModulators(imod_chunk, region.getModulators());
+ }
+ imod_chunk.write(new byte[10]);
+
+ RIFFWriter igen_chunk = writer.writeChunk("igen");
+ for (SF2Layer instrument : this.layers) {
+ if (instrument.getGlobalRegion() != null) {
+ writeGenerators(igen_chunk,
+ instrument.getGlobalRegion().getGenerators());
+ }
+ for (SF2LayerRegion region : instrument.getRegions()) {
+ writeGenerators(igen_chunk, region.getGenerators());
+ int ix = samples.indexOf(region.sample);
+ if (ix != -1) {
+ igen_chunk.writeUnsignedShort(SF2Region.GENERATOR_SAMPLEID);
+ igen_chunk.writeShort((short) ix);
+ }
+ }
+ }
+ igen_chunk.write(new byte[4]);
+
+
+ RIFFWriter shdr_chunk = writer.writeChunk("shdr");
+ long sample_pos = 0;
+ for (SF2Sample sample : samples) {
+ shdr_chunk.writeString(sample.name, 20);
+ long start = sample_pos;
+ sample_pos += sample.data.capacity() / 2;
+ long end = sample_pos;
+ long startLoop = sample.startLoop + start;
+ long endLoop = sample.endLoop + start;
+ if (startLoop < start)
+ startLoop = start;
+ if (endLoop > end)
+ endLoop = end;
+ shdr_chunk.writeUnsignedInt(start);
+ shdr_chunk.writeUnsignedInt(end);
+ shdr_chunk.writeUnsignedInt(startLoop);
+ shdr_chunk.writeUnsignedInt(endLoop);
+ shdr_chunk.writeUnsignedInt(sample.sampleRate);
+ shdr_chunk.writeUnsignedByte(sample.originalPitch);
+ shdr_chunk.writeByte(sample.pitchCorrection);
+ shdr_chunk.writeUnsignedShort(sample.sampleLink);
+ shdr_chunk.writeUnsignedShort(sample.sampleType);
+ sample_pos += 32;
+ }
+ shdr_chunk.writeString("EOS", 20);
+ shdr_chunk.write(new byte[26]);
+
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getVersion() {
+ return major + "." + minor;
+ }
+
+ public String getVendor() {
+ return engineers;
+ }
+
+ public String getDescription() {
+ return comments;
+ }
+
+ public void setName(String s) {
+ name = s;
+ }
+
+ public void setVendor(String s) {
+ engineers = s;
+ }
+
+ public void setDescription(String s) {
+ comments = s;
+ }
+
+ public SoundbankResource[] getResources() {
+ SoundbankResource[] resources
+ = new SoundbankResource[layers.size() + samples.size()];
+ int j = 0;
+ for (int i = 0; i < layers.size(); i++)
+ resources[j++] = layers.get(i);
+ for (int i = 0; i < samples.size(); i++)
+ resources[j++] = samples.get(i);
+ return resources;
+ }
+
+ public SF2Instrument[] getInstruments() {
+ SF2Instrument[] inslist_array
+ = instruments.toArray(new SF2Instrument[instruments.size()]);
+ Arrays.sort(inslist_array, new ModelInstrumentComparator());
+ return inslist_array;
+ }
+
+ public SF2Layer[] getLayers() {
+ return layers.toArray(new SF2Layer[layers.size()]);
+ }
+
+ public SF2Sample[] getSamples() {
+ return samples.toArray(new SF2Sample[samples.size()]);
+ }
+
+ public Instrument getInstrument(Patch patch) {
+ int program = patch.getProgram();
+ int bank = patch.getBank();
+ boolean percussion = false;
+ if (patch instanceof ModelPatch)
+ percussion = ((ModelPatch)patch).isPercussion();
+ for (Instrument instrument : instruments) {
+ Patch patch2 = instrument.getPatch();
+ int program2 = patch2.getProgram();
+ int bank2 = patch2.getBank();
+ if (program == program2 && bank == bank2) {
+ boolean percussion2 = false;
+ if (patch2 instanceof ModelPatch)
+ percussion2 = ((ModelPatch) patch2).isPercussion();
+ if (percussion == percussion2)
+ return instrument;
+ }
+ }
+ return null;
+ }
+
+ public String getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(String creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public String getProduct() {
+ return product;
+ }
+
+ public void setProduct(String product) {
+ this.product = product;
+ }
+
+ public String getRomName() {
+ return romName;
+ }
+
+ public void setRomName(String romName) {
+ this.romName = romName;
+ }
+
+ public int getRomVersionMajor() {
+ return romVersionMajor;
+ }
+
+ public void setRomVersionMajor(int romVersionMajor) {
+ this.romVersionMajor = romVersionMajor;
+ }
+
+ public int getRomVersionMinor() {
+ return romVersionMinor;
+ }
+
+ public void setRomVersionMinor(int romVersionMinor) {
+ this.romVersionMinor = romVersionMinor;
+ }
+
+ public String getTargetEngine() {
+ return targetEngine;
+ }
+
+ public void setTargetEngine(String targetEngine) {
+ this.targetEngine = targetEngine;
+ }
+
+ public String getTools() {
+ return tools;
+ }
+
+ public void setTools(String tools) {
+ this.tools = tools;
+ }
+
+ public void addResource(SoundbankResource resource) {
+ if (resource instanceof SF2Instrument)
+ instruments.add((SF2Instrument)resource);
+ if (resource instanceof SF2Layer)
+ layers.add((SF2Layer)resource);
+ if (resource instanceof SF2Sample)
+ samples.add((SF2Sample)resource);
+ }
+
+ public void removeResource(SoundbankResource resource) {
+ if (resource instanceof SF2Instrument)
+ instruments.remove((SF2Instrument)resource);
+ if (resource instanceof SF2Layer)
+ layers.remove((SF2Layer)resource);
+ if (resource instanceof SF2Sample)
+ samples.remove((SF2Sample)resource);
+ }
+
+ public void addInstrument(SF2Instrument resource) {
+ instruments.add(resource);
+ }
+
+ public void removeInstrument(SF2Instrument resource) {
+ instruments.remove(resource);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SF2SoundbankReader.java b/src/share/classes/com/sun/media/sound/SF2SoundbankReader.java
new file mode 100644
index 000000000..942752df2
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SF2SoundbankReader.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.spi.SoundbankReader;
+
+/**
+ * This class is used to connect the SF2SoundBank class
+ * to the SoundbankReader SPI interface.
+ *
+ * @author Karl Helgason
+ */
+public class SF2SoundbankReader extends SoundbankReader {
+
+ public Soundbank getSoundbank(URL url)
+ throws InvalidMidiDataException, IOException {
+ try {
+ return new SF2Soundbank(url);
+ } catch (RIFFInvalidFormatException e) {
+ return null;
+ } catch(IOException ioe) {
+ return null;
+ }
+ }
+
+ public Soundbank getSoundbank(InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ try {
+ stream.mark(512);
+ return new SF2Soundbank(stream);
+ } catch (RIFFInvalidFormatException e) {
+ stream.reset();
+ return null;
+ }
+ }
+
+ public Soundbank getSoundbank(File file)
+ throws InvalidMidiDataException, IOException {
+ try {
+ return new SF2Soundbank(file);
+ } catch (RIFFInvalidFormatException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SimpleInstrument.java b/src/share/classes/com/sun/media/sound/SimpleInstrument.java
new file mode 100644
index 000000000..92c608105
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SimpleInstrument.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.sound.midi.Patch;
+
+/**
+ * A simple instrument that is made of other ModelInstrument, ModelPerformer
+ * objects.
+ *
+ * @author Karl Helgason
+ */
+public class SimpleInstrument extends ModelInstrument {
+
+ private static class SimpleInstrumentPart {
+ ModelPerformer[] performers;
+ int keyFrom;
+ int keyTo;
+ int velFrom;
+ int velTo;
+ int exclusiveClass;
+ }
+ protected int preset = 0;
+ protected int bank = 0;
+ protected boolean percussion = false;
+ protected String name = "";
+ protected List<SimpleInstrumentPart> parts
+ = new ArrayList<SimpleInstrumentPart>();
+
+ public SimpleInstrument() {
+ super(null, null, null, null);
+ }
+
+ public void clear() {
+ parts.clear();
+ }
+
+ public void add(ModelPerformer[] performers, int keyFrom, int keyTo,
+ int velFrom, int velTo, int exclusiveClass) {
+ SimpleInstrumentPart part = new SimpleInstrumentPart();
+ part.performers = performers;
+ part.keyFrom = keyFrom;
+ part.keyTo = keyTo;
+ part.velFrom = velFrom;
+ part.velTo = velTo;
+ part.exclusiveClass = exclusiveClass;
+ parts.add(part);
+ }
+
+ public void add(ModelPerformer[] performers, int keyFrom, int keyTo,
+ int velFrom, int velTo) {
+ add(performers, keyFrom, keyTo, velFrom, velTo, -1);
+ }
+
+ public void add(ModelPerformer[] performers, int keyFrom, int keyTo) {
+ add(performers, keyFrom, keyTo, 0, 127, -1);
+ }
+
+ public void add(ModelPerformer[] performers) {
+ add(performers, 0, 127, 0, 127, -1);
+ }
+
+ public void add(ModelPerformer performer, int keyFrom, int keyTo,
+ int velFrom, int velTo, int exclusiveClass) {
+ add(new ModelPerformer[]{performer}, keyFrom, keyTo, velFrom, velTo,
+ exclusiveClass);
+ }
+
+ public void add(ModelPerformer performer, int keyFrom, int keyTo,
+ int velFrom, int velTo) {
+ add(new ModelPerformer[]{performer}, keyFrom, keyTo, velFrom, velTo);
+ }
+
+ public void add(ModelPerformer performer, int keyFrom, int keyTo) {
+ add(new ModelPerformer[]{performer}, keyFrom, keyTo);
+ }
+
+ public void add(ModelPerformer performer) {
+ add(new ModelPerformer[]{performer});
+ }
+
+ public void add(ModelInstrument ins, int keyFrom, int keyTo, int velFrom,
+ int velTo, int exclusiveClass) {
+ add(ins.getPerformers(), keyFrom, keyTo, velFrom, velTo, exclusiveClass);
+ }
+
+ public void add(ModelInstrument ins, int keyFrom, int keyTo, int velFrom,
+ int velTo) {
+ add(ins.getPerformers(), keyFrom, keyTo, velFrom, velTo);
+ }
+
+ public void add(ModelInstrument ins, int keyFrom, int keyTo) {
+ add(ins.getPerformers(), keyFrom, keyTo);
+ }
+
+ public void add(ModelInstrument ins) {
+ add(ins.getPerformers());
+ }
+
+ public ModelPerformer[] getPerformers() {
+
+ int percount = 0;
+ for (SimpleInstrumentPart part : parts)
+ if (part.performers != null)
+ percount += part.performers.length;
+
+ ModelPerformer[] performers = new ModelPerformer[percount];
+ int px = 0;
+ for (SimpleInstrumentPart part : parts) {
+ if (part.performers != null) {
+ for (ModelPerformer mperfm : part.performers) {
+ ModelPerformer performer = new ModelPerformer();
+ performer.setName(getName());
+ performers[px++] = performer;
+
+ performer.setDefaultConnectionsEnabled(
+ mperfm.isDefaultConnectionsEnabled());
+ performer.setKeyFrom(mperfm.getKeyFrom());
+ performer.setKeyTo(mperfm.getKeyTo());
+ performer.setVelFrom(mperfm.getVelFrom());
+ performer.setVelTo(mperfm.getVelTo());
+ performer.setExclusiveClass(mperfm.getExclusiveClass());
+ performer.setSelfNonExclusive(mperfm.isSelfNonExclusive());
+ performer.setReleaseTriggered(mperfm.isReleaseTriggered());
+ if (part.exclusiveClass != -1)
+ performer.setExclusiveClass(part.exclusiveClass);
+ if (part.keyFrom > performer.getKeyFrom())
+ performer.setKeyFrom(part.keyFrom);
+ if (part.keyTo < performer.getKeyTo())
+ performer.setKeyTo(part.keyTo);
+ if (part.velFrom > performer.getVelFrom())
+ performer.setVelFrom(part.velFrom);
+ if (part.velTo < performer.getVelTo())
+ performer.setVelTo(part.velTo);
+ performer.getOscillators().addAll(mperfm.getOscillators());
+ performer.getConnectionBlocks().addAll(
+ mperfm.getConnectionBlocks());
+ }
+ }
+ }
+
+ return performers;
+ }
+
+ public Object getData() {
+ return null;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ModelPatch getPatch() {
+ return new ModelPatch(bank, preset, percussion);
+ }
+
+ public void setPatch(Patch patch) {
+ if (patch instanceof ModelPatch && ((ModelPatch)patch).isPercussion()) {
+ percussion = true;
+ bank = patch.getBank();
+ preset = patch.getProgram();
+ } else {
+ percussion = false;
+ bank = patch.getBank();
+ preset = patch.getProgram();
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SimpleSoundbank.java b/src/share/classes/com/sun/media/sound/SimpleSoundbank.java
new file mode 100644
index 000000000..c5dea6e73
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SimpleSoundbank.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.sound.midi.Instrument;
+import javax.sound.midi.Patch;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.SoundbankResource;
+
+/**
+ * A simple soundbank that contains instruments and soundbankresources.
+ *
+ * @author Karl Helgason
+ */
+public class SimpleSoundbank implements Soundbank {
+
+ String name = "";
+ String version = "";
+ String vendor = "";
+ String description = "";
+ List<SoundbankResource> resources = new ArrayList<SoundbankResource>();
+ List<Instrument> instruments = new ArrayList<Instrument>();
+
+ public String getName() {
+ return name;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getVendor() {
+ return vendor;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setVendor(String vendor) {
+ this.vendor = vendor;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public SoundbankResource[] getResources() {
+ return resources.toArray(new SoundbankResource[resources.size()]);
+ }
+
+ public Instrument[] getInstruments() {
+ Instrument[] inslist_array
+ = instruments.toArray(new Instrument[resources.size()]);
+ Arrays.sort(inslist_array, new ModelInstrumentComparator());
+ return inslist_array;
+ }
+
+ public Instrument getInstrument(Patch patch) {
+ int program = patch.getProgram();
+ int bank = patch.getBank();
+ boolean percussion = false;
+ if (patch instanceof ModelPatch)
+ percussion = ((ModelPatch)patch).isPercussion();
+ for (Instrument instrument : instruments) {
+ Patch patch2 = instrument.getPatch();
+ int program2 = patch2.getProgram();
+ int bank2 = patch2.getBank();
+ if (program == program2 && bank == bank2) {
+ boolean percussion2 = false;
+ if (patch2 instanceof ModelPatch)
+ percussion2 = ((ModelPatch)patch2).isPercussion();
+ if (percussion == percussion2)
+ return instrument;
+ }
+ }
+ return null;
+ }
+
+ public void addResource(SoundbankResource resource) {
+ if (resource instanceof Instrument)
+ instruments.add((Instrument) resource);
+ else
+ resources.add(resource);
+ }
+
+ public void removeResource(SoundbankResource resource) {
+ if (resource instanceof Instrument)
+ instruments.remove((Instrument) resource);
+ else
+ resources.remove(resource);
+ }
+
+ public void addInstrument(Instrument resource) {
+ instruments.add(resource);
+ }
+
+ public void removeInstrument(Instrument resource) {
+ instruments.remove(resource);
+ }
+
+ public void addAllInstruments(Soundbank soundbank) {
+ for (Instrument ins : soundbank.getInstruments())
+ addInstrument(ins);
+ }
+
+ public void removeAllInstruments(Soundbank soundbank) {
+ for (Instrument ins : soundbank.getInstruments())
+ removeInstrument(ins);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftAbstractResampler.java b/src/share/classes/com/sun/media/sound/SoftAbstractResampler.java
new file mode 100644
index 000000000..4fcb26c45
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftAbstractResampler.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.VoiceStatus;
+
+/**
+ * Abstract resampler class.
+ *
+ * @author Karl Helgason
+ */
+public abstract class SoftAbstractResampler implements SoftResampler {
+
+ private class ModelAbstractResamplerStream implements SoftResamplerStreamer {
+
+ AudioFloatInputStream stream;
+ boolean stream_eof = false;
+ int loopmode;
+ boolean loopdirection = true; // true = forward
+ float loopstart;
+ float looplen;
+ float target_pitch;
+ float[] current_pitch = new float[1];
+ boolean started;
+ boolean eof;
+ int sector_pos = 0;
+ int sector_size = 400;
+ int sector_loopstart = -1;
+ boolean markset = false;
+ int marklimit = 0;
+ int streampos = 0;
+ int nrofchannels = 2;
+ boolean noteOff_flag = false;
+ float[][] ibuffer;
+ boolean ibuffer_order = true;
+ float[] sbuffer;
+ int pad;
+ int pad2;
+ float[] ix = new float[1];
+ int[] ox = new int[1];
+ float samplerateconv = 1;
+ float pitchcorrection = 0;
+
+ public ModelAbstractResamplerStream() {
+ pad = getPadding();
+ pad2 = getPadding() * 2;
+ ibuffer = new float[2][sector_size + pad2];
+ ibuffer_order = true;
+ }
+
+ public void noteOn(MidiChannel channel, VoiceStatus voice,
+ int noteNumber, int velocity) {
+ }
+
+ public void noteOff(int velocity) {
+ noteOff_flag = true;
+ }
+
+ public void open(ModelWavetable osc, float outputsamplerate)
+ throws IOException {
+
+ eof = false;
+ nrofchannels = osc.getChannels();
+ if (ibuffer.length < nrofchannels) {
+ ibuffer = new float[nrofchannels][sector_size + pad2];
+ }
+
+ stream = osc.openStream();
+ streampos = 0;
+ stream_eof = false;
+ pitchcorrection = osc.getPitchcorrection();
+ samplerateconv
+ = stream.getFormat().getSampleRate() / outputsamplerate;
+ looplen = osc.getLoopLength();
+ loopstart = osc.getLoopStart();
+ sector_loopstart = (int) (loopstart / sector_size);
+ sector_loopstart = sector_loopstart - 1;
+
+ sector_pos = 0;
+
+ if (sector_loopstart < 0)
+ sector_loopstart = 0;
+ started = false;
+ loopmode = osc.getLoopType();
+
+ if (loopmode != 0) {
+ markset = false;
+ marklimit = nrofchannels * (int) (looplen + pad2 + 1);
+ } else
+ markset = true;
+ // loopmode = 0;
+
+ target_pitch = samplerateconv;
+ current_pitch[0] = samplerateconv;
+
+ ibuffer_order = true;
+ loopdirection = true;
+ noteOff_flag = false;
+
+ for (int i = 0; i < nrofchannels; i++)
+ Arrays.fill(ibuffer[i], sector_size, sector_size + pad2, 0);
+ ix[0] = pad;
+ eof = false;
+
+ ix[0] = sector_size + pad;
+ sector_pos = -1;
+ streampos = -sector_size;
+
+ nextBuffer();
+ }
+
+ public void setPitch(float pitch) {
+ /*
+ this.pitch = (float) Math.pow(2f,
+ (pitchcorrection + pitch) / 1200.0f)
+ * samplerateconv;
+ */
+ this.target_pitch = (float)Math.exp(
+ (pitchcorrection + pitch) * (Math.log(2.0) / 1200.0))
+ * samplerateconv;
+
+ if (!started)
+ current_pitch[0] = this.target_pitch;
+ }
+
+ public void nextBuffer() throws IOException {
+ if (ix[0] < pad) {
+ if (markset) {
+ // reset to target sector
+ stream.reset();
+ ix[0] += streampos - (sector_loopstart * sector_size);
+ sector_pos = sector_loopstart;
+ streampos = sector_pos * sector_size;
+
+ // and go one sector backward
+ ix[0] += sector_size;
+ sector_pos -= 1;
+ streampos -= sector_size;
+ stream_eof = false;
+ }
+ }
+
+ if (ix[0] >= sector_size + pad) {
+ if (stream_eof) {
+ eof = true;
+ return;
+ }
+ }
+
+ if (ix[0] >= sector_size * 4 + pad) {
+ int skips = (int)((ix[0] - sector_size * 4 + pad) / sector_size);
+ ix[0] -= sector_size * skips;
+ sector_pos += skips;
+ streampos += sector_size * skips;
+ stream.skip(sector_size * skips);
+ }
+
+ while (ix[0] >= sector_size + pad) {
+ if (!markset) {
+ if (sector_pos + 1 == sector_loopstart) {
+ stream.mark(marklimit);
+ markset = true;
+ }
+ }
+ ix[0] -= sector_size;
+ sector_pos++;
+ streampos += sector_size;
+
+ for (int c = 0; c < nrofchannels; c++) {
+ float[] cbuffer = ibuffer[c];
+ for (int i = 0; i < pad2; i++)
+ cbuffer[i] = cbuffer[i + sector_size];
+ }
+
+ int ret;
+ if (nrofchannels == 1)
+ ret = stream.read(ibuffer[0], pad2, sector_size);
+ else {
+ int slen = sector_size * nrofchannels;
+ if (sbuffer == null || sbuffer.length < slen)
+ sbuffer = new float[slen];
+ int sret = stream.read(sbuffer, 0, slen);
+ if (sret == -1)
+ ret = -1;
+ else {
+ ret = sret / nrofchannels;
+ for (int i = 0; i < nrofchannels; i++) {
+ float[] buff = ibuffer[i];
+ int ix = i;
+ int ix_step = nrofchannels;
+ int ox = pad2;
+ for (int j = 0; j < ret; j++, ix += ix_step, ox++)
+ buff[ox] = sbuffer[ix];
+ }
+ }
+
+ }
+
+ if (ret == -1) {
+ ret = 0;
+ stream_eof = true;
+ for (int i = 0; i < nrofchannels; i++)
+ Arrays.fill(ibuffer[i], pad2, pad2 + sector_size, 0f);
+ return;
+ }
+ if (ret != sector_size) {
+ for (int i = 0; i < nrofchannels; i++)
+ Arrays.fill(ibuffer[i], pad2 + ret, pad2 + sector_size, 0f);
+ }
+
+ ibuffer_order = true;
+
+ }
+
+ }
+
+ public void reverseBuffers() {
+ ibuffer_order = !ibuffer_order;
+ for (int c = 0; c < nrofchannels; c++) {
+ float[] cbuff = ibuffer[c];
+ int len = cbuff.length - 1;
+ int len2 = cbuff.length / 2;
+ for (int i = 0; i < len2; i++) {
+ float x = cbuff[i];
+ cbuff[i] = cbuff[len - i];
+ cbuff[len - i] = x;
+ }
+ }
+ }
+
+ public int read(float[][] buffer, int offset, int len)
+ throws IOException {
+
+ if (eof)
+ return -1;
+
+ if (noteOff_flag)
+ if ((loopmode & 2) != 0)
+ if (loopdirection)
+ loopmode = 0;
+
+
+ float pitchstep = (target_pitch - current_pitch[0]) / len;
+ float[] current_pitch = this.current_pitch;
+ started = true;
+
+ int[] ox = this.ox;
+ ox[0] = offset;
+ int ox_end = len + offset;
+
+ float ixend = sector_size + pad;
+ if (!loopdirection)
+ ixend = pad;
+ while (ox[0] != ox_end) {
+ nextBuffer();
+ if (!loopdirection) {
+ // If we are in backward playing part of pingpong
+ // or reverse loop
+
+ if (streampos < (loopstart + pad)) {
+ ixend = loopstart - streampos + pad2;
+ if (ix[0] <= ixend) {
+ if ((loopmode & 4) != 0) {
+ // Ping pong loop, change loopdirection
+ loopdirection = true;
+ ixend = sector_size + pad;
+ continue;
+ }
+
+ ix[0] += looplen;
+ ixend = pad;
+ continue;
+ }
+ }
+
+ if (ibuffer_order != loopdirection)
+ reverseBuffers();
+
+ ix[0] = (sector_size + pad2) - ix[0];
+ ixend = (sector_size + pad2) - ixend;
+ ixend++;
+
+ float bak_ix = ix[0];
+ int bak_ox = ox[0];
+ float bak_pitch = current_pitch[0];
+ for (int i = 0; i < nrofchannels; i++) {
+ if (buffer[i] != null) {
+ ix[0] = bak_ix;
+ ox[0] = bak_ox;
+ current_pitch[0] = bak_pitch;
+ interpolate(ibuffer[i], ix, ixend, current_pitch,
+ pitchstep, buffer[i], ox, ox_end);
+ }
+ }
+
+ ix[0] = (sector_size + pad2) - ix[0];
+ ixend--;
+ ixend = (sector_size + pad2) - ixend;
+
+ if (eof) {
+ current_pitch[0] = this.target_pitch;
+ return ox[0] - offset;
+ }
+
+ continue;
+ }
+ if (loopmode != 0) {
+ if (streampos + sector_size > (looplen + loopstart + pad)) {
+ ixend = loopstart + looplen - streampos + pad2;
+ if (ix[0] >= ixend) {
+ if ((loopmode & 4) != 0 || (loopmode & 8) != 0) {
+ // Ping pong or revese loop, change loopdirection
+ loopdirection = false;
+ ixend = pad;
+ continue;
+ }
+ ixend = sector_size + pad;
+ ix[0] -= looplen;
+ continue;
+ }
+ }
+ }
+
+ if (ibuffer_order != loopdirection)
+ reverseBuffers();
+
+ float bak_ix = ix[0];
+ int bak_ox = ox[0];
+ float bak_pitch = current_pitch[0];
+ for (int i = 0; i < nrofchannels; i++) {
+ if (buffer[i] != null) {
+ ix[0] = bak_ix;
+ ox[0] = bak_ox;
+ current_pitch[0] = bak_pitch;
+ interpolate(ibuffer[i], ix, ixend, current_pitch,
+ pitchstep, buffer[i], ox, ox_end);
+ }
+ }
+
+ if (eof) {
+ current_pitch[0] = this.target_pitch;
+ return ox[0] - offset;
+ }
+ }
+
+ current_pitch[0] = this.target_pitch;
+ return len;
+ }
+
+ public void close() throws IOException {
+ stream.close();
+ }
+ }
+
+ public abstract int getPadding();
+
+ public abstract void interpolate(float[] in, float[] in_offset,
+ float in_end, float[] pitch, float pitchstep, float[] out,
+ int[] out_offset, int out_end);
+
+ public SoftResamplerStreamer openStreamer() {
+ return new ModelAbstractResamplerStream();
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftAudioBuffer.java b/src/share/classes/com/sun/media/sound/SoftAudioBuffer.java
new file mode 100644
index 000000000..ccc94899f
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftAudioBuffer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioFormat;
+
+/**
+ * This class is used to store audio buffer.
+ *
+ * @author Karl Helgason
+ */
+public class SoftAudioBuffer {
+
+ private int size;
+ private float[] buffer;
+ private boolean empty = true;
+ private AudioFormat format;
+ private AudioFloatConverter converter;
+ private byte[] converter_buffer;
+
+ public SoftAudioBuffer(int size, AudioFormat format) {
+ this.size = size;
+ this.format = format;
+ converter = AudioFloatConverter.getConverter(format);
+ }
+
+ public AudioFormat getFormat() {
+ return format;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void clear() {
+ if (!empty) {
+ Arrays.fill(buffer, 0);
+ empty = true;
+ }
+ }
+
+ public boolean isSilent() {
+ return empty;
+ }
+
+ public float[] array() {
+ empty = false;
+ if (buffer == null)
+ buffer = new float[size];
+ return buffer;
+ }
+
+ public void get(byte[] buffer, int channel) {
+
+ int framesize_pc = (format.getFrameSize() / format.getChannels());
+ int c_len = size * framesize_pc;
+ if (converter_buffer == null || converter_buffer.length < c_len)
+ converter_buffer = new byte[c_len];
+
+ if (format.getChannels() == 1) {
+ converter.toByteArray(array(), size, buffer);
+ } else {
+ converter.toByteArray(array(), size, converter_buffer);
+ if (channel >= format.getChannels())
+ return;
+ int z_stepover = format.getChannels() * framesize_pc;
+ int k_stepover = framesize_pc;
+ for (int j = 0; j < framesize_pc; j++) {
+ int k = j;
+ int z = channel * framesize_pc + j;
+ for (int i = 0; i < size; i++) {
+ buffer[z] = converter_buffer[k];
+ z += z_stepover;
+ k += k_stepover;
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftAudioProcessor.java b/src/share/classes/com/sun/media/sound/SoftAudioProcessor.java
new file mode 100644
index 000000000..1e8a53f72
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftAudioProcessor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Audio processor interface.
+ *
+ * @author Karl Helgason
+ */
+public interface SoftAudioProcessor {
+
+ public void globalParameterControlChange(int[] slothpath, long param,
+ long value);
+
+ public void init(float samplerate, float controlrate);
+
+ public void setInput(int pin, SoftAudioBuffer input);
+
+ public void setOutput(int pin, SoftAudioBuffer output);
+
+ public void setMixMode(boolean mix);
+
+ public void processAudio();
+
+ public void processControlLogic();
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftAudioPusher.java b/src/share/classes/com/sun/media/sound/SoftAudioPusher.java
new file mode 100644
index 000000000..d19ff412b
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftAudioPusher.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.SourceDataLine;
+
+/**
+ * This is a processor object that writes into SourceDataLine
+ *
+ * @author Karl Helgason
+ */
+public class SoftAudioPusher implements Runnable {
+
+ private volatile boolean active = false;
+ private SourceDataLine sourceDataLine = null;
+ private Thread audiothread;
+ private AudioInputStream ais;
+ private byte[] buffer;
+
+ public SoftAudioPusher(SourceDataLine sourceDataLine, AudioInputStream ais,
+ int workbuffersizer) {
+ this.ais = ais;
+ this.buffer = new byte[workbuffersizer];
+ this.sourceDataLine = sourceDataLine;
+ }
+
+ public synchronized void start() {
+ if (active)
+ return;
+ active = true;
+ audiothread = new Thread(this);
+ audiothread.setPriority(Thread.MAX_PRIORITY);
+ audiothread.start();
+ }
+
+ public synchronized void stop() {
+ if (!active)
+ return;
+ active = false;
+ try {
+ audiothread.join();
+ } catch (InterruptedException e) {
+ //e.printStackTrace();
+ }
+ }
+
+ public void run() {
+ byte[] buffer = SoftAudioPusher.this.buffer;
+ AudioInputStream ais = SoftAudioPusher.this.ais;
+ SourceDataLine sourceDataLine = SoftAudioPusher.this.sourceDataLine;
+
+ try {
+ while (active) {
+ // Read from audio source
+ int count = ais.read(buffer);
+ if(count < 0) break;
+ // Write byte buffer to source output
+ sourceDataLine.write(buffer, 0, count);
+ }
+ } catch (IOException e) {
+ active = false;
+ //e.printStackTrace();
+ }
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftChannel.java b/src/share/classes/com/sun/media/sound/SoftChannel.java
new file mode 100644
index 000000000..8bb5f2ef6
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftChannel.java
@@ -0,0 +1,1548 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.Patch;
+
+/**
+ * Software Synthesizer MIDI channel class.
+ *
+ * @author Karl Helgason
+ */
+public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
+
+ private static boolean[] dontResetControls = new boolean[128];
+ static {
+ for (int i = 0; i < dontResetControls.length; i++)
+ dontResetControls[i] = false;
+
+ dontResetControls[0] = true; // Bank Select (MSB)
+ dontResetControls[32] = true; // Bank Select (LSB)
+ dontResetControls[7] = true; // Channel Volume (MSB)
+ dontResetControls[8] = true; // Balance (MSB)
+ dontResetControls[10] = true; // Pan (MSB)
+ dontResetControls[11] = true; // Expression (MSB)
+ dontResetControls[91] = true; // Effects 1 Depth (default: Reverb Send)
+ dontResetControls[92] = true; // Effects 2 Depth (default: Tremolo Depth)
+ dontResetControls[93] = true; // Effects 3 Depth (default: Chorus Send)
+ dontResetControls[94] = true; // Effects 4 Depth (default: Celeste [Detune] Depth)
+ dontResetControls[95] = true; // Effects 5 Depth (default: Phaser Depth)
+ dontResetControls[70] = true; // Sound Controller 1 (default: Sound Variation)
+ dontResetControls[71] = true; // Sound Controller 2 (default: Timbre / Harmonic Quality)
+ dontResetControls[72] = true; // Sound Controller 3 (default: Release Time)
+ dontResetControls[73] = true; // Sound Controller 4 (default: Attack Time)
+ dontResetControls[74] = true; // Sound Controller 5 (default: Brightness)
+ dontResetControls[75] = true; // Sound Controller 6 (GM2 default: Decay Time)
+ dontResetControls[76] = true; // Sound Controller 7 (GM2 default: Vibrato Rate)
+ dontResetControls[77] = true; // Sound Controller 8 (GM2 default: Vibrato Depth)
+ dontResetControls[78] = true; // Sound Controller 9 (GM2 default: Vibrato Delay)
+ dontResetControls[79] = true; // Sound Controller 10 (GM2 default: Undefined)
+ dontResetControls[120] = true; // All Sound Off
+ dontResetControls[121] = true; // Reset All Controllers
+ dontResetControls[122] = true; // Local Control On/Off
+ dontResetControls[123] = true; // All Notes Off
+ dontResetControls[124] = true; // Omni Mode Off
+ dontResetControls[125] = true; // Omni Mode On
+ dontResetControls[126] = true; // Poly Mode Off
+ dontResetControls[127] = true; // Poly Mode On
+
+ dontResetControls[6] = true; // Data Entry (MSB)
+ dontResetControls[38] = true; // Data Entry (LSB)
+ dontResetControls[96] = true; // Data Increment
+ dontResetControls[97] = true; // Data Decrement
+ dontResetControls[98] = true; // Non-Registered Parameter Number (LSB)
+ dontResetControls[99] = true; // Non-Registered Parameter Number(MSB)
+ dontResetControls[100] = true; // RPN = Null
+ dontResetControls[101] = true; // RPN = Null
+
+ }
+
+ private static final int RPN_NULL_VALUE = (127 << 7) + 127;
+ private int rpn_control = RPN_NULL_VALUE;
+ private int nrpn_control = RPN_NULL_VALUE;
+ protected double portamento_time = 1; // keyschanges per control buffer time
+ protected int[] portamento_lastnote = new int[128];
+ protected int portamento_lastnote_ix = 0;
+ private int portamento_control_note = -1;
+ private boolean portamento = false;
+ private boolean mono = false;
+ private boolean mute = false;
+ private boolean solo = false;
+ private boolean solomute = false;
+ private Object control_mutex;
+ private int channel;
+ private SoftVoice[] voices;
+ private int bank;
+ private int program;
+ private SoftSynthesizer synthesizer;
+ private SoftMainMixer mainmixer;
+ private int[] polypressure = new int[128];
+ private int channelpressure = 0;
+ private int[] controller = new int[128];
+ private int pitchbend;
+ private double[] co_midi_pitch = new double[1];
+ private double[] co_midi_channel_pressure = new double[1];
+ protected SoftTuning tuning = new SoftTuning();
+ protected int tuning_bank = 0;
+ protected int tuning_program = 0;
+ protected SoftInstrument current_instrument = null;
+ protected ModelChannelMixer current_mixer = null;
+ private ModelDirector current_director = null;
+
+ // Controller Destination Settings
+ protected int cds_control_number = -1;
+ protected ModelConnectionBlock[] cds_control_connections = null;
+ protected ModelConnectionBlock[] cds_channelpressure_connections = null;
+ protected ModelConnectionBlock[] cds_polypressure_connections = null;
+ protected boolean sustain = false;
+ protected boolean[][] keybasedcontroller_active = null;
+ protected double[][] keybasedcontroller_value = null;
+
+ private class MidiControlObject implements SoftControl {
+ double[] pitch = co_midi_pitch;
+ double[] channel_pressure = co_midi_channel_pressure;
+ double[] poly_pressure = new double[1];
+
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ if (name.equals("pitch"))
+ return pitch;
+ if (name.equals("channel_pressure"))
+ return channel_pressure;
+ if (name.equals("poly_pressure"))
+ return poly_pressure;
+ return null;
+ }
+ }
+
+ private SoftControl[] co_midi = new SoftControl[128];
+ {
+ for (int i = 0; i < co_midi.length; i++) {
+ co_midi[i] = new MidiControlObject();
+ }
+ }
+
+ private double[][] co_midi_cc_cc = new double[128][1];
+ private SoftControl co_midi_cc = new SoftControl() {
+ double[][] cc = co_midi_cc_cc;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ return cc[Integer.parseInt(name)];
+ }
+ };
+ Map<Integer, int[]> co_midi_rpn_rpn_i = new HashMap<Integer, int[]>();
+ Map<Integer, double[]> co_midi_rpn_rpn = new HashMap<Integer, double[]>();
+ private SoftControl co_midi_rpn = new SoftControl() {
+ Map<Integer, double[]> rpn = co_midi_rpn_rpn;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ int iname = Integer.parseInt(name);
+ double[] v = rpn.get(iname);
+ if (v == null) {
+ v = new double[1];
+ rpn.put(iname, v);
+ }
+ return v;
+ }
+ };
+ Map<Integer, int[]> co_midi_nrpn_nrpn_i = new HashMap<Integer, int[]>();
+ Map<Integer, double[]> co_midi_nrpn_nrpn = new HashMap<Integer, double[]>();
+ private SoftControl co_midi_nrpn = new SoftControl() {
+ Map<Integer, double[]> nrpn = co_midi_nrpn_nrpn;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ int iname = Integer.parseInt(name);
+ double[] v = nrpn.get(iname);
+ if (v == null) {
+ v = new double[1];
+ nrpn.put(iname, v);
+ }
+ return v;
+ }
+ };
+
+ private static int restrict7Bit(int value)
+ {
+ if(value < 0) return 0;
+ if(value > 127) return 127;
+ return value;
+ }
+
+ private static int restrict14Bit(int value)
+ {
+ if(value < 0) return 0;
+ if(value > 16256) return 16256;
+ return value;
+ }
+
+ public SoftChannel(SoftSynthesizer synth, int channel) {
+ this.channel = channel;
+ this.voices = synth.getVoices();
+ this.synthesizer = synth;
+ this.mainmixer = synth.getMainMixer();
+ control_mutex = synth.control_mutex;
+ resetAllControllers(true);
+ }
+
+ private int findFreeVoice(int x) {
+ for (int i = x; i < voices.length; i++)
+ if (!voices[i].active)
+ return i;
+
+ // No free voice was found, we must steal one
+
+ int vmode = synthesizer.getVoiceAllocationMode();
+ if (vmode == 1) {
+ // DLS Static Voice Allocation
+
+ // * priority ( 10, 1-9, 11-16)
+ // Search for channel to steal from
+ int steal_channel = channel;
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].stealer_channel == null) {
+ if (steal_channel == 9) {
+ steal_channel = voices[j].channel;
+ } else {
+ if (voices[j].channel != 9) {
+ if (voices[j].channel > steal_channel)
+ steal_channel = voices[j].channel;
+ }
+ }
+ }
+ }
+
+ int voiceNo = -1;
+
+ SoftVoice v = null;
+ // Search for oldest voice in off state on steal_channel
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].channel == steal_channel) {
+ if (voices[j].stealer_channel == null && !voices[j].on) {
+ if (v == null) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ if (voices[j].voiceID < v.voiceID) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ }
+ }
+ }
+ // Search for oldest voice in on state on steal_channel
+ if (voiceNo == -1) {
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].channel == steal_channel) {
+ if (voices[j].stealer_channel == null) {
+ if (v == null) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ if (voices[j].voiceID < v.voiceID) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ }
+ }
+ }
+ }
+
+ return voiceNo;
+
+ } else {
+ // Default Voice Allocation
+ // * Find voice that is on
+ // and Find voice which has lowest voiceID ( oldest voice)
+ // * Or find voice that is off
+ // and Find voice which has lowest voiceID ( oldest voice)
+
+ int voiceNo = -1;
+
+ SoftVoice v = null;
+ // Search for oldest voice in off state
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].stealer_channel == null && !voices[j].on) {
+ if (v == null) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ if (voices[j].voiceID < v.voiceID) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ }
+ }
+ // Search for oldest voice in on state
+ if (voiceNo == -1) {
+
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].stealer_channel == null) {
+ if (v == null) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ if (voices[j].voiceID < v.voiceID) {
+ v = voices[j];
+ voiceNo = j;
+ }
+ }
+ }
+ }
+
+ return voiceNo;
+ }
+
+ }
+
+ protected void initVoice(SoftVoice voice, SoftPerformer p, int voiceID,
+ int noteNumber, int velocity, ModelConnectionBlock[] connectionBlocks,
+ ModelChannelMixer channelmixer, boolean releaseTriggered) {
+ if (voice.active) {
+ // Voice is active , we must steal the voice
+ voice.stealer_channel = this;
+ voice.stealer_performer = p;
+ voice.stealer_voiceID = voiceID;
+ voice.stealer_noteNumber = noteNumber;
+ voice.stealer_velocity = velocity;
+ voice.stealer_extendedConnectionBlocks = connectionBlocks;
+ voice.stealer_channelmixer = channelmixer;
+ voice.stealer_releaseTriggered = releaseTriggered;
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active && voices[i].voiceID == voice.voiceID)
+ voices[i].soundOff();
+ return;
+ }
+
+ voice.extendedConnectionBlocks = connectionBlocks;
+ voice.channelmixer = channelmixer;
+ voice.releaseTriggered = releaseTriggered;
+ voice.voiceID = voiceID;
+ voice.tuning = tuning;
+ voice.exclusiveClass = p.exclusiveClass;
+ voice.softchannel = this;
+ voice.channel = channel;
+ voice.bank = bank;
+ voice.program = program;
+ voice.instrument = current_instrument;
+ voice.performer = p;
+ voice.objects.clear();
+ voice.objects.put("midi", co_midi[noteNumber]);
+ voice.objects.put("midi_cc", co_midi_cc);
+ voice.objects.put("midi_rpn", co_midi_rpn);
+ voice.objects.put("midi_nrpn", co_midi_nrpn);
+ voice.noteOn(noteNumber, velocity);
+ voice.setMute(mute);
+ voice.setSoloMute(solomute);
+ if (releaseTriggered)
+ return;
+ if (portamento_control_note != -1) {
+ voice.co_noteon_keynumber[0]
+ = (tuning.getTuning(portamento_control_note) / 100.0)
+ * (1f / 128f);
+ voice.portamento = true;
+ portamento_control_note = -1;
+ } else if (portamento) {
+ if (mono) {
+ if (portamento_lastnote[0] != -1) {
+ voice.co_noteon_keynumber[0]
+ = (tuning.getTuning(portamento_lastnote[0]) / 100.0)
+ * (1f / 128f);
+ voice.portamento = true;
+ portamento_control_note = -1;
+ }
+ portamento_lastnote[0] = noteNumber;
+ } else {
+ if (portamento_lastnote_ix != 0) {
+ portamento_lastnote_ix--;
+ voice.co_noteon_keynumber[0]
+ = (tuning.getTuning(
+ portamento_lastnote[portamento_lastnote_ix])
+ / 100.0)
+ * (1f / 128f);
+ voice.portamento = true;
+ }
+ }
+ }
+ }
+
+ public void noteOn(int noteNumber, int velocity) {
+ noteNumber = restrict7Bit(noteNumber);
+ velocity = restrict7Bit(velocity);
+ noteOn_internal(noteNumber, velocity);
+ if (current_mixer != null)
+ current_mixer.noteOn(noteNumber, velocity);
+ }
+
+ private void noteOn_internal(int noteNumber, int velocity) {
+
+ if (velocity == 0) {
+ noteOff_internal(noteNumber, 64);
+ return;
+ }
+
+ synchronized (control_mutex) {
+ if (sustain) {
+ sustain = false;
+ for (int i = 0; i < voices.length; i++) {
+ if ((voices[i].sustain || voices[i].on)
+ && voices[i].channel == channel && voices[i].active
+ && voices[i].note == noteNumber) {
+ voices[i].sustain = false;
+ voices[i].on = true;
+ voices[i].noteOff(0);
+ }
+ }
+ sustain = true;
+ }
+
+ mainmixer.activity();
+
+ if (mono) {
+ if (portamento) {
+ boolean n_found = false;
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].on && voices[i].channel == channel
+ && voices[i].active
+ && voices[i].releaseTriggered == false) {
+ voices[i].portamento = true;
+ voices[i].setNote(noteNumber);
+ n_found = true;
+ }
+ }
+ if (n_found) {
+ portamento_lastnote[0] = noteNumber;
+ return;
+ }
+ }
+
+ if (portamento_control_note != -1) {
+ boolean n_found = false;
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].on && voices[i].channel == channel
+ && voices[i].active
+ && voices[i].note == portamento_control_note
+ && voices[i].releaseTriggered == false) {
+ voices[i].portamento = true;
+ voices[i].setNote(noteNumber);
+ n_found = true;
+ }
+ }
+ portamento_control_note = -1;
+ if (n_found)
+ return;
+ }
+ }
+
+ if (mono)
+ allNotesOff();
+
+ if (current_instrument == null) {
+ current_instrument
+ = synthesizer.findInstrument(program, bank, channel);
+ if (current_instrument == null)
+ return;
+ if (current_mixer != null)
+ mainmixer.stopMixer(current_mixer);
+ current_mixer = current_instrument.getSourceInstrument()
+ .getChannelMixer(this, synthesizer.getFormat());
+ if (current_mixer != null)
+ mainmixer.registerMixer(current_mixer);
+ current_director = current_instrument.getDirector(this, this);
+ applyInstrumentCustomization();
+ }
+ prevVoiceID = synthesizer.voiceIDCounter++;
+ firstVoice = true;
+ voiceNo = 0;
+
+ int tunedKey = (int)(Math.round(tuning.getTuning()[noteNumber]/100.0));
+ play_noteNumber = noteNumber;
+ play_velocity = velocity;
+ play_releasetriggered = false;
+ lastVelocity[noteNumber] = velocity;
+ current_director.noteOn(tunedKey, velocity);
+
+ /*
+ SoftPerformer[] performers = current_instrument.getPerformers();
+ for (int i = 0; i < performers.length; i++) {
+ SoftPerformer p = performers[i];
+ if (p.keyFrom <= tunedKey && p.keyTo >= tunedKey) {
+ if (p.velFrom <= velocity && p.velTo >= velocity) {
+ if (firstVoice) {
+ firstVoice = false;
+ if (p.exclusiveClass != 0) {
+ int x = p.exclusiveClass;
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].active
+ && voices[j].channel == channel
+ && voices[j].exclusiveClass == x) {
+ if (!(p.selfNonExclusive
+ && voices[j].note == noteNumber))
+ voices[j].shutdown();
+ }
+ }
+ }
+ }
+ voiceNo = findFreeVoice(voiceNo);
+ if (voiceNo == -1)
+ return;
+ initVoice(voices[voiceNo], p, prevVoiceID, noteNumber,
+ velocity);
+ }
+ }
+ }
+ */
+ }
+ }
+
+ public void noteOff(int noteNumber, int velocity) {
+ noteNumber = restrict7Bit(noteNumber);
+ velocity = restrict7Bit(velocity);
+ noteOff_internal(noteNumber, velocity);
+
+ if (current_mixer != null)
+ current_mixer.noteOff(noteNumber, velocity);
+ }
+
+ private void noteOff_internal(int noteNumber, int velocity) {
+ synchronized (control_mutex) {
+
+ if (!mono) {
+ if (portamento) {
+ if (portamento_lastnote_ix != 127) {
+ portamento_lastnote[portamento_lastnote_ix] = noteNumber;
+ portamento_lastnote_ix++;
+ }
+ }
+ }
+
+ mainmixer.activity();
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].on && voices[i].channel == channel
+ && voices[i].note == noteNumber
+ && voices[i].releaseTriggered == false) {
+ voices[i].noteOff(velocity);
+ }
+ }
+
+ // Try play back note-off triggered voices,
+
+ if (current_instrument == null) {
+ current_instrument
+ = synthesizer.findInstrument(program, bank, channel);
+ if (current_instrument == null)
+ return;
+ if (current_mixer != null)
+ mainmixer.stopMixer(current_mixer);
+ current_mixer = current_instrument.getSourceInstrument()
+ .getChannelMixer(this, synthesizer.getFormat());
+ if (current_mixer != null)
+ mainmixer.registerMixer(current_mixer);
+ current_director = current_instrument.getDirector(this, this);
+ applyInstrumentCustomization();
+
+ }
+ prevVoiceID = synthesizer.voiceIDCounter++;
+ firstVoice = true;
+ voiceNo = 0;
+
+ int tunedKey = (int)(Math.round(tuning.getTuning()[noteNumber]/100.0));
+ play_noteNumber = noteNumber;
+ play_velocity = lastVelocity[noteNumber];
+ play_releasetriggered = true;
+ current_director.noteOff(tunedKey, velocity);
+
+ }
+ }
+ private int[] lastVelocity = new int[128];
+ private int prevVoiceID;
+ private boolean firstVoice = true;
+ private int voiceNo = 0;
+ private int play_noteNumber = 0;
+ private int play_velocity = 0;
+ private boolean play_releasetriggered = false;
+
+ public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks) {
+
+ int noteNumber = play_noteNumber;
+ int velocity = play_velocity;
+ boolean releasetriggered = play_releasetriggered;
+
+ SoftPerformer p = current_instrument.getPerformers()[performerIndex];
+
+ if (firstVoice) {
+ firstVoice = false;
+ if (p.exclusiveClass != 0) {
+ int x = p.exclusiveClass;
+ for (int j = 0; j < voices.length; j++) {
+ if (voices[j].active && voices[j].channel == channel
+ && voices[j].exclusiveClass == x) {
+ if (!(p.selfNonExclusive && voices[j].note == noteNumber))
+ voices[j].shutdown();
+ }
+ }
+ }
+ }
+
+ voiceNo = findFreeVoice(voiceNo);
+
+ if (voiceNo == -1)
+ return;
+
+ initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity,
+ connectionBlocks, current_mixer, releasetriggered);
+ }
+
+ public void noteOff(int noteNumber) {
+ if(noteNumber < 0 || noteNumber > 127) return;
+ noteOff_internal(noteNumber, 64);
+ }
+
+ public void setPolyPressure(int noteNumber, int pressure) {
+ noteNumber = restrict7Bit(noteNumber);
+ pressure = restrict7Bit(pressure);
+
+ if (current_mixer != null)
+ current_mixer.setPolyPressure(noteNumber, pressure);
+
+ synchronized (control_mutex) {
+ mainmixer.activity();
+ co_midi[noteNumber].get(0, "poly_pressure")[0] = pressure*(1.0/128.0);
+ polypressure[noteNumber] = pressure;
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].active && voices[i].note == noteNumber)
+ voices[i].setPolyPressure(pressure);
+ }
+ }
+ }
+
+ public int getPolyPressure(int noteNumber) {
+ synchronized (control_mutex) {
+ return polypressure[noteNumber];
+ }
+ }
+
+ public void setChannelPressure(int pressure) {
+ pressure = restrict7Bit(pressure);
+ if (current_mixer != null)
+ current_mixer.setChannelPressure(pressure);
+ synchronized (control_mutex) {
+ mainmixer.activity();
+ co_midi_channel_pressure[0] = pressure * (1.0 / 128.0);
+ channelpressure = pressure;
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].active)
+ voices[i].setChannelPressure(pressure);
+ }
+ }
+ }
+
+ public int getChannelPressure() {
+ synchronized (control_mutex) {
+ return channelpressure;
+ }
+ }
+
+ protected void applyInstrumentCustomization() {
+ if (cds_control_connections == null
+ && cds_channelpressure_connections == null
+ && cds_polypressure_connections == null) {
+ return;
+ }
+
+ ModelInstrument src_instrument = current_instrument.getSourceInstrument();
+ ModelPerformer[] performers = src_instrument.getPerformers();
+ ModelPerformer[] new_performers = new ModelPerformer[performers.length];
+ for (int i = 0; i < new_performers.length; i++) {
+ ModelPerformer performer = performers[i];
+ ModelPerformer new_performer = new ModelPerformer();
+ new_performer.setName(performer.getName());
+ new_performer.setExclusiveClass(performer.getExclusiveClass());
+ new_performer.setKeyFrom(performer.getKeyFrom());
+ new_performer.setKeyTo(performer.getKeyTo());
+ new_performer.setVelFrom(performer.getVelFrom());
+ new_performer.setVelTo(performer.getVelTo());
+ new_performer.getOscillators().addAll(performer.getOscillators());
+ new_performer.getConnectionBlocks().addAll(
+ performer.getConnectionBlocks());
+ new_performers[i] = new_performer;
+
+ List<ModelConnectionBlock> connblocks =
+ new_performer.getConnectionBlocks();
+
+ if (cds_control_connections != null) {
+ String cc = Integer.toString(cds_control_number);
+ Iterator<ModelConnectionBlock> iter = connblocks.iterator();
+ while (iter.hasNext()) {
+ ModelConnectionBlock conn = iter.next();
+ ModelSource[] sources = conn.getSources();
+ boolean removeok = false;
+ if (sources != null) {
+ for (int j = 0; j < sources.length; j++) {
+ ModelSource src = sources[j];
+ if ("midi_cc".equals(src.getIdentifier().getObject())
+ && cc.equals(src.getIdentifier().getVariable())) {
+ removeok = true;
+ }
+ }
+ }
+ if (removeok)
+ iter.remove();
+ }
+ for (int j = 0; j < cds_control_connections.length; j++)
+ connblocks.add(cds_control_connections[j]);
+ }
+
+ if (cds_polypressure_connections != null) {
+ Iterator<ModelConnectionBlock> iter = connblocks.iterator();
+ while (iter.hasNext()) {
+ ModelConnectionBlock conn = iter.next();
+ ModelSource[] sources = conn.getSources();
+ boolean removeok = false;
+ if (sources != null) {
+ for (int j = 0; j < sources.length; j++) {
+ ModelSource src = sources[j];
+ if ("midi".equals(src.getIdentifier().getObject())
+ && "poly_pressure".equals(
+ src.getIdentifier().getVariable())) {
+ removeok = true;
+ }
+ }
+ }
+ if (removeok)
+ iter.remove();
+ }
+ for (int j = 0; j < cds_polypressure_connections.length; j++)
+ connblocks.add(cds_polypressure_connections[j]);
+ }
+
+
+ if (cds_channelpressure_connections != null) {
+ Iterator<ModelConnectionBlock> iter = connblocks.iterator();
+ while (iter.hasNext()) {
+ ModelConnectionBlock conn = iter.next();
+ ModelSource[] sources = conn.getSources();
+ boolean removeok = false;
+ if (sources != null) {
+ for (int j = 0; j < sources.length; j++) {
+ ModelIdentifier srcid = sources[j].getIdentifier();
+ if ("midi".equals(srcid.getObject()) &&
+ "channel_pressure".equals(srcid.getVariable())) {
+ removeok = true;
+ }
+ }
+ }
+ if (removeok)
+ iter.remove();
+ }
+ for (int j = 0; j < cds_channelpressure_connections.length; j++)
+ connblocks.add(cds_channelpressure_connections[j]);
+ }
+
+ }
+
+ current_instrument = new SoftInstrument(src_instrument, new_performers);
+
+ }
+
+ private ModelConnectionBlock[] createModelConnections(ModelIdentifier sid,
+ int[] destination, int[] range) {
+
+ /*
+ controlled parameter (pp)|range (rr)| Description |Default
+ -------------------------|----------|-------------------------|-------
+ 00 Pitch Control | 28H..58H | -24..+24 semitones | 40H
+ 01 Filter Cutoff Control | 00H..7FH | -9600..+9450 cents | 40H
+ 02 Amplitude Control | 00H..7FH | 0..(127/64)*100 percent | 40H
+ 03 LFO Pitch Depth | 00H..7FH | 0..600 cents | 0
+ 04 LFO Filter Depth | 00H..7FH | 0..2400 cents | 0
+ 05 LFO Amplitude Depth | 00H..7FH | 0..100 percent | 0
+ */
+
+ List<ModelConnectionBlock> conns = new ArrayList<ModelConnectionBlock>();
+
+ for (int i = 0; i < destination.length; i++) {
+ int d = destination[i];
+ int r = range[i];
+ if (d == 0) {
+ double scale = (r - 64) * 100;
+ ModelConnectionBlock conn = new ModelConnectionBlock(
+ new ModelSource(sid,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ scale,
+ new ModelDestination(
+ new ModelIdentifier("osc", "pitch")));
+ conns.add(conn);
+
+ }
+ if (d == 1) {
+ double scale = (r / 64.0 - 1.0) * 9600.0;
+ ModelConnectionBlock conn;
+ if (scale > 0) {
+ conn = new ModelConnectionBlock(
+ new ModelSource(sid,
+ ModelStandardTransform.DIRECTION_MAX2MIN,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ -scale,
+ new ModelDestination(
+ ModelDestination.DESTINATION_FILTER_FREQ));
+ } else {
+ conn = new ModelConnectionBlock(
+ new ModelSource(sid,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ scale,
+ new ModelDestination(
+ ModelDestination.DESTINATION_FILTER_FREQ));
+ }
+ conns.add(conn);
+ }
+ if (d == 2) {
+ final double scale = (r / 64.0);
+ ModelTransform mt = new ModelTransform() {
+ double s = scale;
+ public double transform(double value) {
+ if (s < 1)
+ value = s + (value * (1.0 - s));
+ else if (s > 1)
+ value = 1 + (value * (s - 1.0));
+ else
+ return 0;
+ return -((5.0 / 12.0) / Math.log(10)) * Math.log(value);
+ }
+ };
+
+ ModelConnectionBlock conn = new ModelConnectionBlock(
+ new ModelSource(sid, mt), -960,
+ new ModelDestination(ModelDestination.DESTINATION_GAIN));
+ conns.add(conn);
+
+ }
+ if (d == 3) {
+ double scale = (r / 64.0 - 1.0) * 9600.0;
+ ModelConnectionBlock conn = new ModelConnectionBlock(
+ new ModelSource(ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ new ModelSource(sid,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ scale,
+ new ModelDestination(
+ ModelDestination.DESTINATION_PITCH));
+ conns.add(conn);
+ }
+ if (d == 4) {
+ double scale = (r / 128.0) * 2400.0;
+ ModelConnectionBlock conn = new ModelConnectionBlock(
+ new ModelSource(ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ new ModelSource(sid,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ scale,
+ new ModelDestination(
+ ModelDestination.DESTINATION_FILTER_FREQ));
+ conns.add(conn);
+ }
+ if (d == 5) {
+ final double scale = (r / 127.0);
+
+ ModelTransform mt = new ModelTransform() {
+ double s = scale;
+ public double transform(double value) {
+ return -((5.0 / 12.0) / Math.log(10))
+ * Math.log(1 - value * s);
+ }
+ };
+
+ ModelConnectionBlock conn = new ModelConnectionBlock(
+ new ModelSource(ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ new ModelSource(sid, mt),
+ -960,
+ new ModelDestination(
+ ModelDestination.DESTINATION_GAIN));
+ conns.add(conn);
+ }
+ }
+
+ return conns.toArray(new ModelConnectionBlock[conns.size()]);
+ }
+
+ public void mapPolyPressureToDestination(int[] destination, int[] range) {
+ current_instrument = null;
+ if (destination.length == 0) {
+ cds_polypressure_connections = null;
+ return;
+ }
+ cds_polypressure_connections
+ = createModelConnections(
+ new ModelIdentifier("midi", "poly_pressure"),
+ destination, range);
+ }
+
+ public void mapChannelPressureToDestination(int[] destination, int[] range) {
+ current_instrument = null;
+ if (destination.length == 0) {
+ cds_channelpressure_connections = null;
+ return;
+ }
+ cds_channelpressure_connections
+ = createModelConnections(
+ new ModelIdentifier("midi", "channel_pressure"),
+ destination, range);
+ }
+
+ public void mapControlToDestination(int control, int[] destination, int[] range) {
+
+ if (!((control >= 0x01 && control <= 0x1F)
+ || (control >= 0x40 && control <= 0x5F))) {
+ cds_control_connections = null;
+ return;
+ }
+
+ current_instrument = null;
+ cds_control_number = control;
+ if (destination.length == 0) {
+ cds_control_connections = null;
+ return;
+ }
+ cds_control_connections
+ = createModelConnections(
+ new ModelIdentifier("midi_cc", Integer.toString(control)),
+ destination, range);
+ }
+
+ public void controlChangePerNote(int noteNumber, int controller, int value) {
+
+/*
+ CC# | nn | Name | vv | default | description
+-----|------|-------------------------|----------------|------------|-------------------------------
+7 |07H |Note Volume |00H-40H-7FH |40H |0-100-(127/64)*100(%)(Relative)
+10 |0AH |*Pan |00H-7FH absolute|Preset Value|Left-Center-Right (absolute)
+33-63|21-3FH|LSB for |01H-1FH | |
+71 |47H |Timbre/Harmonic Intensity|00H-40H-7FH |40H (???) |
+72 |48H |Release Time |00H-40H-7FH |40H (???) |
+73 |49H |Attack Time |00H-40H-7FH |40H (???) |
+74 |4AH |Brightness |00H-40H-7FH |40H (???) |
+75 |4BH |Decay Time |00H-40H-7FH |40H (???) |
+76 |4CH |Vibrato Rate |00H-40H-7FH |40H (???) |
+77 |4DH |Vibrato Depth |00H-40H-7FH |40H (???) |
+78 |4EH |Vibrato Delay |00H-40H-7FH |40H (???) |
+91 |5BH |*Reverb Send |00H-7FH absolute|Preset Value|Left-Center-Right (absolute)
+93 |5DH |*Chorus Send |00H-7FH absolute|Preset Value|Left-Center-Right (absolute)
+120 |78H |**Fine Tuning |00H-40H-7FH |40H (???) |
+121 |79H |**Coarse Tuning |00H-40H-7FH |40H (???) |
+*/
+
+ if (keybasedcontroller_active == null) {
+ keybasedcontroller_active = new boolean[128][];
+ keybasedcontroller_value = new double[128][];
+ }
+ if (keybasedcontroller_active[noteNumber] == null) {
+ keybasedcontroller_active[noteNumber] = new boolean[128];
+ Arrays.fill(keybasedcontroller_active[noteNumber], false);
+ keybasedcontroller_value[noteNumber] = new double[128];
+ Arrays.fill(keybasedcontroller_value[noteNumber], 0);
+ }
+
+ if (value == -1) {
+ keybasedcontroller_active[noteNumber][controller] = false;
+ } else {
+ keybasedcontroller_active[noteNumber][controller] = true;
+ keybasedcontroller_value[noteNumber][controller] = value / 128.0;
+ }
+
+ if (controller < 120) {
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].controlChange(controller, -1);
+ } else if (controller == 120) {
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].rpnChange(1, -1);
+ } else if (controller == 121) {
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].rpnChange(2, -1);
+ }
+
+ }
+
+ public int getControlPerNote(int noteNumber, int controller) {
+ if (keybasedcontroller_active == null)
+ return -1;
+ if (keybasedcontroller_active[noteNumber] == null)
+ return -1;
+ if (!keybasedcontroller_active[noteNumber][controller])
+ return -1;
+ return (int)(keybasedcontroller_value[noteNumber][controller] * 128);
+ }
+
+ public void controlChange(int controller, int value) {
+ controller = restrict7Bit(controller);
+ value = restrict7Bit(value);
+ if (current_mixer != null)
+ current_mixer.controlChange(controller, value);
+
+ synchronized (control_mutex) {
+ switch (controller) {
+ /*
+ Map<String, int[]>co_midi_rpn_rpn_i = new HashMap<String, int[]>();
+ Map<String, double[]>co_midi_rpn_rpn = new HashMap<String, double[]>();
+ Map<String, int[]>co_midi_nrpn_nrpn_i = new HashMap<String, int[]>();
+ Map<String, double[]>co_midi_nrpn_nrpn = new HashMap<String, double[]>();
+ */
+
+ case 5:
+ // This produce asin-like curve
+ // as described in General Midi Level 2 Specification, page 6
+ double x = -Math.asin((value / 128.0) * 2 - 1) / Math.PI + 0.5;
+ x = Math.pow(100000.0, x) / 100.0; // x is now cent/msec
+ // Convert x from cent/msec to key/controlbuffertime
+ x = x / 100.0; // x is now keys/msec
+ x = x * 1000.0; // x is now keys/sec
+ x = x / synthesizer.getControlRate(); // x is now keys/controlbuffertime
+ portamento_time = x;
+ break;
+ case 6:
+ case 38:
+ case 96:
+ case 97:
+ int val = 0;
+ if (nrpn_control != RPN_NULL_VALUE) {
+ int[] val_i = co_midi_nrpn_nrpn_i.get(nrpn_control);
+ if (val_i != null)
+ val = val_i[0];
+ }
+ if (rpn_control != RPN_NULL_VALUE) {
+ int[] val_i = co_midi_rpn_rpn_i.get(rpn_control);
+ if (val_i != null)
+ val = val_i[0];
+ }
+
+ if (controller == 6)
+ val = (val & 127) + (value << 7);
+ else if (controller == 38)
+ val = (val & (127 << 7)) + value;
+ else if (controller == 96 || controller == 97) {
+ int step = 1;
+ if (rpn_control == 2 || rpn_control == 3 || rpn_control == 4)
+ step = 128;
+ if (controller == 96)
+ val += step;
+ if (controller == 97)
+ val -= step;
+ }
+
+ if (nrpn_control != RPN_NULL_VALUE)
+ nrpnChange(nrpn_control, val);
+ if (rpn_control != RPN_NULL_VALUE)
+ rpnChange(rpn_control, val);
+
+ break;
+ case 64: // Hold1 (Damper) (cc#64)
+ boolean on = value >= 64;
+ if (sustain != on) {
+ sustain = on;
+ if (!on) {
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].active && voices[i].sustain &&
+ voices[i].channel == channel) {
+ voices[i].sustain = false;
+ if (!voices[i].on) {
+ voices[i].on = true;
+ voices[i].noteOff(0);
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active && voices[i].channel == channel)
+ voices[i].redamp();
+ }
+ }
+ break;
+ case 65:
+ //allNotesOff();
+ portamento = value >= 64;
+ portamento_lastnote[0] = -1;
+ /*
+ for (int i = 0; i < portamento_lastnote.length; i++)
+ portamento_lastnote[i] = -1;
+ */
+ portamento_lastnote_ix = 0;
+ break;
+ case 66: // Sostenuto (cc#66)
+ on = value >= 64;
+ if (on) {
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].active && voices[i].on &&
+ voices[i].channel == channel) {
+ voices[i].sostenuto = true;
+ }
+ }
+ }
+ if (!on) {
+ for (int i = 0; i < voices.length; i++) {
+ if (voices[i].active && voices[i].sostenuto &&
+ voices[i].channel == channel) {
+ voices[i].sostenuto = false;
+ if (!voices[i].on) {
+ voices[i].on = true;
+ voices[i].noteOff(0);
+ }
+ }
+ }
+ }
+ break;
+ case 84:
+ portamento_control_note = value;
+ break;
+ case 98:
+ nrpn_control = (nrpn_control & (127 << 7)) + value;
+ rpn_control = RPN_NULL_VALUE;
+ break;
+ case 99:
+ nrpn_control = (nrpn_control & 127) + (value << 7);
+ rpn_control = RPN_NULL_VALUE;
+ break;
+ case 100:
+ rpn_control = (rpn_control & (127 << 7)) + value;
+ nrpn_control = RPN_NULL_VALUE;
+ break;
+ case 101:
+ rpn_control = (rpn_control & 127) + (value << 7);
+ nrpn_control = RPN_NULL_VALUE;
+ break;
+ case 120:
+ allSoundOff();
+ break;
+ case 121:
+ resetAllControllers(value == 127);
+ break;
+ case 122:
+ localControl(value >= 64);
+ break;
+ case 123:
+ allNotesOff();
+ break;
+ case 124:
+ setOmni(false);
+ break;
+ case 125:
+ setOmni(true);
+ break;
+ case 126:
+ if (value == 1)
+ setMono(true);
+ break;
+ case 127:
+ setMono(false);
+ break;
+
+ default:
+ break;
+ }
+
+ co_midi_cc_cc[controller][0] = value * (1.0 / 128.0);
+
+ if (controller == 0x00) {
+ bank = /*(bank & 127) +*/ (value << 7);
+ return;
+ }
+
+ if (controller == 0x20) {
+ bank = (bank & (127 << 7)) + value;
+ return;
+ }
+
+ this.controller[controller] = value;
+ if(controller < 0x20)
+ this.controller[controller + 0x20] = 0;
+
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].controlChange(controller, value);
+
+ }
+ }
+
+ public int getController(int controller) {
+ synchronized (control_mutex) {
+ // Should only return lower 7 bits,
+ // even when controller is "boosted" higher.
+ return this.controller[controller] & 127;
+ }
+ }
+
+ public void tuningChange(int program) {
+ tuningChange(0, program);
+ }
+
+ public void tuningChange(int bank, int program) {
+ synchronized (control_mutex) {
+ tuning = synthesizer.getTuning(new Patch(bank, program));
+ }
+ }
+
+ public void programChange(int program) {
+ programChange(bank, program);
+ }
+
+ public void programChange(int bank, int program) {
+ bank = restrict7Bit(bank);
+ program = restrict7Bit(program);
+ synchronized (control_mutex) {
+ mainmixer.activity();
+ this.bank = bank;
+ this.program = program;
+ current_instrument = null;
+ }
+ }
+
+ public int getProgram() {
+ synchronized (control_mutex) {
+ return program;
+ }
+ }
+
+ public void setPitchBend(int bend) {
+ bend = restrict14Bit(bend);
+ if (current_mixer != null)
+ current_mixer.setPitchBend(bend);
+ synchronized (control_mutex) {
+ mainmixer.activity();
+ co_midi_pitch[0] = bend * (1.0 / 16384.0);
+ pitchbend = bend;
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].setPitchBend(bend);
+ }
+ }
+
+ public int getPitchBend() {
+ synchronized (control_mutex) {
+ return pitchbend;
+ }
+ }
+
+ public void nrpnChange(int controller, int value) {
+
+ /*
+ System.out.println("(" + channel + ").nrpnChange("
+ + Integer.toHexString(controller >> 7)
+ + " " + Integer.toHexString(controller & 127)
+ + ", " + Integer.toHexString(value >> 7)
+ + " " + Integer.toHexString(value & 127) + ")");
+ */
+
+ if (synthesizer.getGeneralMidiMode() == 0) {
+ if (controller == (0x01 << 7) + (0x08)) // Vibrato Rate
+ controlChange(76, value >> 7);
+ if (controller == (0x01 << 7) + (0x09)) // Vibrato Depth
+ controlChange(77, value >> 7);
+ if (controller == (0x01 << 7) + (0x0A)) // Vibrato Delay
+ controlChange(78, value >> 7);
+ if (controller == (0x01 << 7) + (0x20)) // Brightness
+ controlChange(74, value >> 7);
+ if (controller == (0x01 << 7) + (0x21)) // Filter Resonance
+ controlChange(71, value >> 7);
+ if (controller == (0x01 << 7) + (0x63)) // Attack Time
+ controlChange(73, value >> 7);
+ if (controller == (0x01 << 7) + (0x64)) // Decay Time
+ controlChange(75, value >> 7);
+ if (controller == (0x01 << 7) + (0x66)) // Release Time
+ controlChange(72, value >> 7);
+
+ if (controller >> 7 == 0x18) // Pitch coarse
+ controlChangePerNote(controller % 128, 120, value >> 7);
+ if (controller >> 7 == 0x1A) // Volume
+ controlChangePerNote(controller % 128, 7, value >> 7);
+ if (controller >> 7 == 0x1C) // Panpot
+ controlChangePerNote(controller % 128, 10, value >> 7);
+ if (controller >> 7 == 0x1D) // Reverb
+ controlChangePerNote(controller % 128, 91, value >> 7);
+ if (controller >> 7 == 0x1E) // Chorus
+ controlChangePerNote(controller % 128, 93, value >> 7);
+ }
+
+ int[] val_i = co_midi_nrpn_nrpn_i.get(controller);
+ double[] val_d = co_midi_nrpn_nrpn.get(controller);
+ if (val_i == null) {
+ val_i = new int[1];
+ co_midi_nrpn_nrpn_i.put(controller, val_i);
+ }
+ if (val_d == null) {
+ val_d = new double[1];
+ co_midi_nrpn_nrpn.put(controller, val_d);
+ }
+ val_i[0] = value;
+ val_d[0] = val_i[0] * (1.0 / 16384.0);
+
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].nrpnChange(controller, val_i[0]);
+
+ }
+
+ public void rpnChange(int controller, int value) {
+
+ /*
+ System.out.println("(" + channel + ").rpnChange("
+ + Integer.toHexString(controller >> 7)
+ + " " + Integer.toHexString(controller & 127)
+ + ", " + Integer.toHexString(value >> 7)
+ + " " + Integer.toHexString(value & 127) + ")");
+ */
+
+ if (controller == 3) {
+ tuning_program = (value >> 7) & 127;
+ tuningChange(tuning_bank, tuning_program);
+ }
+ if (controller == 4) {
+ tuning_bank = (value >> 7) & 127;
+ }
+
+ int[] val_i = co_midi_rpn_rpn_i.get(controller);
+ double[] val_d = co_midi_rpn_rpn.get(controller);
+ if (val_i == null) {
+ val_i = new int[1];
+ co_midi_rpn_rpn_i.put(controller, val_i);
+ }
+ if (val_d == null) {
+ val_d = new double[1];
+ co_midi_rpn_rpn.put(controller, val_d);
+ }
+ val_i[0] = value;
+ val_d[0] = val_i[0] * (1.0 / 16384.0);
+
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ voices[i].rpnChange(controller, val_i[0]);
+ }
+
+ public void resetAllControllers() {
+ resetAllControllers(false);
+ }
+
+ public void resetAllControllers(boolean allControls) {
+ synchronized (control_mutex) {
+ mainmixer.activity();
+
+ for (int i = 0; i < 128; i++) {
+ setPolyPressure(i, 0);
+ }
+ setChannelPressure(0);
+ setPitchBend(8192);
+ for (int i = 0; i < 128; i++) {
+ if (!dontResetControls[i])
+ controlChange(i, 0);
+ }
+
+ controlChange(71, 64); // Filter Resonance
+ controlChange(72, 64); // Release Time
+ controlChange(73, 64); // Attack Time
+ controlChange(74, 64); // Brightness
+ controlChange(75, 64); // Decay Time
+ controlChange(76, 64); // Vibrato Rate
+ controlChange(77, 64); // Vibrato Depth
+ controlChange(78, 64); // Vibrato Delay
+
+ controlChange(8, 64); // Balance
+ controlChange(11, 127); // Expression
+ controlChange(98, 127); // NRPN Null
+ controlChange(99, 127); // NRPN Null
+ controlChange(100, 127); // RPN = Null
+ controlChange(101, 127); // RPN = Null
+
+ // see DLS 2.1 (Power-on Default Values)
+ if (allControls) {
+
+ keybasedcontroller_active = null;
+ keybasedcontroller_value = null;
+
+ controlChange(7, 100); // Volume
+ controlChange(10, 64); // Pan
+ controlChange(91, 40); // Reverb
+
+ for (int controller : co_midi_rpn_rpn.keySet()) {
+ // don't reset tuning settings
+ if (controller != 3 && controller != 4)
+ rpnChange(controller, 0);
+ }
+ for (int controller : co_midi_nrpn_nrpn.keySet())
+ nrpnChange(controller, 0);
+ rpnChange(0, 2 << 7); // Bitch Bend sensitivity
+ rpnChange(1, 64 << 7); // Channel fine tunning
+ rpnChange(2, 64 << 7); // Channel Coarse Tuning
+ rpnChange(5, 64); // Modulation Depth, +/- 50 cent
+
+ tuning_bank = 0;
+ tuning_program = 0;
+ tuning = new SoftTuning();
+
+ }
+
+ }
+ }
+
+ public void allNotesOff() {
+ if (current_mixer != null)
+ current_mixer.allNotesOff();
+ synchronized (control_mutex) {
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].on && voices[i].channel == channel
+ && voices[i].releaseTriggered == false) {
+ voices[i].noteOff(0);
+ }
+ }
+ }
+
+ public void allSoundOff() {
+ if (current_mixer != null)
+ current_mixer.allSoundOff();
+ synchronized (control_mutex) {
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].on && voices[i].channel == channel)
+ voices[i].soundOff();
+ }
+ }
+
+ public boolean localControl(boolean on) {
+ return false;
+ }
+
+ public void setMono(boolean on) {
+ if (current_mixer != null)
+ current_mixer.setMono(on);
+ synchronized (control_mutex) {
+ allNotesOff();
+ mono = on;
+ }
+ }
+
+ public boolean getMono() {
+ synchronized (control_mutex) {
+ return mono;
+ }
+ }
+
+ public void setOmni(boolean on) {
+ if (current_mixer != null)
+ current_mixer.setOmni(on);
+ allNotesOff();
+ // Omni is not supported by GM2
+ }
+
+ public boolean getOmni() {
+ return false;
+ }
+
+ public void setMute(boolean mute) {
+ if (current_mixer != null)
+ current_mixer.setMute(mute);
+ synchronized (control_mutex) {
+ this.mute = mute;
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active && voices[i].channel == channel)
+ voices[i].setMute(mute);
+ }
+ }
+
+ public boolean getMute() {
+ synchronized (control_mutex) {
+ return mute;
+ }
+ }
+
+ public void setSolo(boolean soloState) {
+ if (current_mixer != null)
+ current_mixer.setSolo(soloState);
+
+ synchronized (control_mutex) {
+ this.solo = soloState;
+
+ boolean soloinuse = false;
+ for (SoftChannel c : synthesizer.channels) {
+ if (c.solo) {
+ soloinuse = true;
+ break;
+ }
+ }
+
+ if (!soloinuse) {
+ for (SoftChannel c : synthesizer.channels)
+ c.setSoloMute(false);
+ return;
+ }
+
+ for (SoftChannel c : synthesizer.channels)
+ c.setSoloMute(!c.solo);
+
+ }
+
+ }
+
+ private void setSoloMute(boolean mute) {
+ synchronized (control_mutex) {
+ if (solomute == mute)
+ return;
+ this.solomute = mute;
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active && voices[i].channel == channel)
+ voices[i].setSoloMute(solomute);
+ }
+ }
+
+ public boolean getSolo() {
+ synchronized (control_mutex) {
+ return solo;
+ }
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftChannelProxy.java b/src/share/classes/com/sun/media/sound/SoftChannelProxy.java
new file mode 100644
index 000000000..d4fcf8861
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftChannelProxy.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.MidiChannel;
+
+/**
+ * A MidiChannel proxy object used for external access to synthesizer internal
+ * channel objects.
+ *
+ * @author Karl Helgason
+ */
+public class SoftChannelProxy implements MidiChannel {
+
+ private MidiChannel channel = null;
+
+ public MidiChannel getChannel() {
+ return channel;
+ }
+
+ public void setChannel(MidiChannel channel) {
+ this.channel = channel;
+ }
+
+ public void allNotesOff() {
+ if (channel == null)
+ return;
+ channel.allNotesOff();
+ }
+
+ public void allSoundOff() {
+ if (channel == null)
+ return;
+ channel.allSoundOff();
+ }
+
+ public void controlChange(int controller, int value) {
+ if (channel == null)
+ return;
+ channel.controlChange(controller, value);
+ }
+
+ public int getChannelPressure() {
+ if (channel == null)
+ return 0;
+ return channel.getChannelPressure();
+ }
+
+ public int getController(int controller) {
+ if (channel == null)
+ return 0;
+ return channel.getController(controller);
+ }
+
+ public boolean getMono() {
+ if (channel == null)
+ return false;
+ return channel.getMono();
+ }
+
+ public boolean getMute() {
+ if (channel == null)
+ return false;
+ return channel.getMute();
+ }
+
+ public boolean getOmni() {
+ if (channel == null)
+ return false;
+ return channel.getOmni();
+ }
+
+ public int getPitchBend() {
+ if (channel == null)
+ return 8192;
+ return channel.getPitchBend();
+ }
+
+ public int getPolyPressure(int noteNumber) {
+ if (channel == null)
+ return 0;
+ return channel.getPolyPressure(noteNumber);
+ }
+
+ public int getProgram() {
+ if (channel == null)
+ return 0;
+ return channel.getProgram();
+ }
+
+ public boolean getSolo() {
+ if (channel == null)
+ return false;
+ return channel.getSolo();
+ }
+
+ public boolean localControl(boolean on) {
+ if (channel == null)
+ return false;
+ return channel.localControl(on);
+ }
+
+ public void noteOff(int noteNumber) {
+ if (channel == null)
+ return;
+ channel.noteOff(noteNumber);
+ }
+
+ public void noteOff(int noteNumber, int velocity) {
+ if (channel == null)
+ return;
+ channel.noteOff(noteNumber, velocity);
+ }
+
+ public void noteOn(int noteNumber, int velocity) {
+ if (channel == null)
+ return;
+ channel.noteOn(noteNumber, velocity);
+ }
+
+ public void programChange(int program) {
+ if (channel == null)
+ return;
+ channel.programChange(program);
+ }
+
+ public void programChange(int bank, int program) {
+ if (channel == null)
+ return;
+ channel.programChange(bank, program);
+ }
+
+ public void resetAllControllers() {
+ if (channel == null)
+ return;
+ channel.resetAllControllers();
+ }
+
+ public void setChannelPressure(int pressure) {
+ if (channel == null)
+ return;
+ channel.setChannelPressure(pressure);
+ }
+
+ public void setMono(boolean on) {
+ if (channel == null)
+ return;
+ channel.setMono(on);
+ }
+
+ public void setMute(boolean mute) {
+ if (channel == null)
+ return;
+ channel.setMute(mute);
+ }
+
+ public void setOmni(boolean on) {
+ if (channel == null)
+ return;
+ channel.setOmni(on);
+ }
+
+ public void setPitchBend(int bend) {
+ if (channel == null)
+ return;
+ channel.setPitchBend(bend);
+ }
+
+ public void setPolyPressure(int noteNumber, int pressure) {
+ if (channel == null)
+ return;
+ channel.setPolyPressure(noteNumber, pressure);
+ }
+
+ public void setSolo(boolean soloState) {
+ if (channel == null)
+ return;
+ channel.setSolo(soloState);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftChorus.java b/src/share/classes/com/sun/media/sound/SoftChorus.java
new file mode 100644
index 000000000..0a9f64439
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftChorus.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.Arrays;
+
+/**
+ * A chorus effect made using LFO and variable delay. One for each channel
+ * (left,right), with different starting phase for stereo effect.
+ *
+ * @author Karl Helgason
+ */
+public class SoftChorus implements SoftAudioProcessor {
+
+ private static class VariableDelay {
+
+ private float[] delaybuffer;
+ private int rovepos = 0;
+ private volatile float gain = 1;
+ private volatile float rgain = 0;
+ private volatile float delay = 0;
+ private float lastdelay = 0;
+ private volatile float feedback = 0;
+
+ public VariableDelay(int maxbuffersize) {
+ delaybuffer = new float[maxbuffersize];
+ }
+
+ public void setDelay(float delay) {
+ this.delay = delay;
+ }
+
+ public void setFeedBack(float feedback) {
+ this.feedback = feedback;
+ }
+
+ public void setGain(float gain) {
+ this.gain = gain;
+ }
+
+ public void setReverbSendGain(float rgain) {
+ this.rgain = rgain;
+ }
+
+ public void processMix(float[] in, float[] out, float[] rout) {
+ float gain = this.gain;
+ float delay = this.delay;
+ float feedback = this.feedback;
+
+ float[] delaybuffer = this.delaybuffer;
+ int len = in.length;
+ float delaydelta = (delay - lastdelay) / len;
+ int rnlen = delaybuffer.length;
+ int rovepos = this.rovepos;
+
+ if (rout == null)
+ for (int i = 0; i < len; i++) {
+ float r = rovepos - (lastdelay + 2) + rnlen;
+ int ri = (int) r;
+ float s = r - ri;
+ float a = delaybuffer[ri % rnlen];
+ float b = delaybuffer[(ri + 1) % rnlen];
+ float o = a * (1 - s) + b * (s);
+ out[i] += o * gain;
+ delaybuffer[rovepos] = in[i] + o * feedback;
+ rovepos = (rovepos + 1) % rnlen;
+ lastdelay += delaydelta;
+ }
+ else
+ for (int i = 0; i < len; i++) {
+ float r = rovepos - (lastdelay + 2) + rnlen;
+ int ri = (int) r;
+ float s = r - ri;
+ float a = delaybuffer[ri % rnlen];
+ float b = delaybuffer[(ri + 1) % rnlen];
+ float o = a * (1 - s) + b * (s);
+ out[i] += o * gain;
+ rout[i] += o * rgain;
+ delaybuffer[rovepos] = in[i] + o * feedback;
+ rovepos = (rovepos + 1) % rnlen;
+ lastdelay += delaydelta;
+ }
+ this.rovepos = rovepos;
+ lastdelay = delay;
+ }
+
+ public void processReplace(float[] in, float[] out, float[] rout) {
+ Arrays.fill(out, 0);
+ Arrays.fill(rout, 0);
+ processMix(in, out, rout);
+ }
+ }
+
+ private static class LFODelay {
+
+ private volatile double c_cos_delta;
+ private volatile double c_sin_delta;
+ private double c_cos = 1;
+ private double c_sin = 0;
+ private double depth = 0;
+ private VariableDelay vdelay;
+ private double samplerate;
+ private double controlrate;
+
+ public LFODelay(double samplerate, double controlrate) {
+ this.samplerate = samplerate;
+ this.controlrate = controlrate;
+ // vdelay = new VariableDelay((int)(samplerate*4));
+ vdelay = new VariableDelay((int) ((this.depth + 10) * 2));
+
+ }
+
+ public void setDepth(double depth) {
+ this.depth = depth * samplerate;
+ vdelay = new VariableDelay((int) ((this.depth + 10) * 2));
+ }
+
+ public void setRate(double rate) {
+ double g = (Math.PI * 2) * (rate / controlrate);
+ c_cos_delta = Math.cos(g);
+ c_sin_delta = Math.sin(g);
+ }
+
+ public void setPhase(double phase) {
+ c_cos = Math.cos(phase);
+ c_sin = Math.sin(phase);
+ }
+
+ public void setFeedBack(float feedback) {
+ vdelay.setFeedBack(feedback);
+ }
+
+ public void setGain(float gain) {
+ vdelay.setGain(gain);
+ }
+
+ public void setReverbSendGain(float rgain) {
+ vdelay.setReverbSendGain(rgain);
+ }
+
+ public void processMix(float[] in, float[] out, float[] rout) {
+ c_cos = c_cos * c_cos_delta - c_sin * c_sin_delta;
+ c_sin = c_cos * c_sin_delta + c_sin * c_cos_delta;
+ vdelay.setDelay((float) (depth * 0.5 * (c_cos + 2)));
+ vdelay.processMix(in, out, rout);
+ }
+
+ public void processReplace(float[] in, float[] out, float[] rout) {
+ c_cos = c_cos * c_cos_delta - c_sin * c_sin_delta;
+ c_sin = c_cos * c_sin_delta + c_sin * c_cos_delta;
+ vdelay.setDelay((float) (depth * 0.5 * (c_cos + 2)));
+ vdelay.processReplace(in, out, rout);
+
+ }
+ }
+ private boolean mix = true;
+ private SoftAudioBuffer inputA;
+ private SoftAudioBuffer left;
+ private SoftAudioBuffer right;
+ private SoftAudioBuffer reverb;
+ private LFODelay vdelay1L;
+ private LFODelay vdelay1R;
+ private float rgain = 0;
+ private boolean dirty = true;
+ private double dirty_vdelay1L_rate;
+ private double dirty_vdelay1R_rate;
+ private double dirty_vdelay1L_depth;
+ private double dirty_vdelay1R_depth;
+ private float dirty_vdelay1L_feedback;
+ private float dirty_vdelay1R_feedback;
+ private float dirty_vdelay1L_reverbsendgain;
+ private float dirty_vdelay1R_reverbsendgain;
+ private float controlrate;
+
+ public void init(float samplerate, float controlrate) {
+ this.controlrate = controlrate;
+ vdelay1L = new LFODelay(samplerate, controlrate);
+ vdelay1R = new LFODelay(samplerate, controlrate);
+ vdelay1L.setGain(1.0f); // %
+ vdelay1R.setGain(1.0f); // %
+ vdelay1L.setPhase(0.5 * Math.PI);
+ vdelay1R.setPhase(0);
+
+ globalParameterControlChange(new int[]{0x01 * 128 + 0x02}, 0, 2);
+ }
+
+ public void globalParameterControlChange(int[] slothpath, long param,
+ long value) {
+ if (slothpath.length == 1) {
+ if (slothpath[0] == 0x01 * 128 + 0x02) {
+ if (param == 0) { // Chorus Type
+ switch ((int)value) {
+ case 0: // Chorus 1 0 (0%) 3 (0.4Hz) 5 (1.9ms) 0 (0%)
+ globalParameterControlChange(slothpath, 3, 0);
+ globalParameterControlChange(slothpath, 1, 3);
+ globalParameterControlChange(slothpath, 2, 5);
+ globalParameterControlChange(slothpath, 4, 0);
+ break;
+ case 1: // Chorus 2 5 (4%) 9 (1.1Hz) 19 (6.3ms) 0 (0%)
+ globalParameterControlChange(slothpath, 3, 5);
+ globalParameterControlChange(slothpath, 1, 9);
+ globalParameterControlChange(slothpath, 2, 19);
+ globalParameterControlChange(slothpath, 4, 0);
+ break;
+ case 2: // Chorus 3 8 (6%) 3 (0.4Hz) 19 (6.3ms) 0 (0%)
+ globalParameterControlChange(slothpath, 3, 8);
+ globalParameterControlChange(slothpath, 1, 3);
+ globalParameterControlChange(slothpath, 2, 19);
+ globalParameterControlChange(slothpath, 4, 0);
+ break;
+ case 3: // Chorus 4 16 (12%) 9 (1.1Hz) 16 (5.3ms) 0 (0%)
+ globalParameterControlChange(slothpath, 3, 16);
+ globalParameterControlChange(slothpath, 1, 9);
+ globalParameterControlChange(slothpath, 2, 16);
+ globalParameterControlChange(slothpath, 4, 0);
+ break;
+ case 4: // FB Chorus 64 (49%) 2 (0.2Hz) 24 (7.8ms) 0 (0%)
+ globalParameterControlChange(slothpath, 3, 64);
+ globalParameterControlChange(slothpath, 1, 2);
+ globalParameterControlChange(slothpath, 2, 24);
+ globalParameterControlChange(slothpath, 4, 0);
+ break;
+ case 5: // Flanger 112 (86%) 1 (0.1Hz) 5 (1.9ms) 0 (0%)
+ globalParameterControlChange(slothpath, 3, 112);
+ globalParameterControlChange(slothpath, 1, 1);
+ globalParameterControlChange(slothpath, 2, 5);
+ globalParameterControlChange(slothpath, 4, 0);
+ break;
+ default:
+ break;
+ }
+ } else if (param == 1) { // Mod Rate
+ dirty_vdelay1L_rate = (value * 0.122);
+ dirty_vdelay1R_rate = (value * 0.122);
+ dirty = true;
+ } else if (param == 2) { // Mod Depth
+ dirty_vdelay1L_depth = ((value + 1) / 3200.0);
+ dirty_vdelay1R_depth = ((value + 1) / 3200.0);
+ dirty = true;
+ } else if (param == 3) { // Feedback
+ dirty_vdelay1L_feedback = (value * 0.00763f);
+ dirty_vdelay1R_feedback = (value * 0.00763f);
+ dirty = true;
+ }
+ if (param == 4) { // Send to Reverb
+ rgain = value * 0.00787f;
+ dirty_vdelay1L_reverbsendgain = (value * 0.00787f);
+ dirty_vdelay1R_reverbsendgain = (value * 0.00787f);
+ dirty = true;
+ }
+
+ }
+ }
+ }
+
+ public void processControlLogic() {
+ if (dirty) {
+ dirty = false;
+ vdelay1L.setRate(dirty_vdelay1L_rate);
+ vdelay1R.setRate(dirty_vdelay1R_rate);
+ vdelay1L.setDepth(dirty_vdelay1L_depth);
+ vdelay1R.setDepth(dirty_vdelay1R_depth);
+ vdelay1L.setFeedBack(dirty_vdelay1L_feedback);
+ vdelay1R.setFeedBack(dirty_vdelay1R_feedback);
+ vdelay1L.setReverbSendGain(dirty_vdelay1L_reverbsendgain);
+ vdelay1R.setReverbSendGain(dirty_vdelay1R_reverbsendgain);
+ }
+ }
+ double silentcounter = 1000;
+
+ public void processAudio() {
+
+ if (inputA.isSilent()) {
+ silentcounter += 1 / controlrate;
+
+ if (silentcounter > 1) {
+ if (!mix) {
+ left.clear();
+ right.clear();
+ }
+ return;
+ }
+ } else
+ silentcounter = 0;
+
+ float[] inputA = this.inputA.array();
+ float[] left = this.left.array();
+ float[] right = this.right == null ? null : this.right.array();
+ float[] reverb = rgain != 0 ? this.reverb.array() : null;
+
+ if (mix) {
+ vdelay1L.processMix(inputA, left, reverb);
+ if (right != null)
+ vdelay1R.processMix(inputA, right, reverb);
+ } else {
+ vdelay1L.processReplace(inputA, left, reverb);
+ if (right != null)
+ vdelay1R.processReplace(inputA, right, reverb);
+ }
+ }
+
+ public void setInput(int pin, SoftAudioBuffer input) {
+ if (pin == 0)
+ inputA = input;
+ }
+
+ public void setMixMode(boolean mix) {
+ this.mix = mix;
+ }
+
+ public void setOutput(int pin, SoftAudioBuffer output) {
+ if (pin == 0)
+ left = output;
+ if (pin == 1)
+ right = output;
+ if (pin == 2)
+ reverb = output;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftControl.java b/src/share/classes/com/sun/media/sound/SoftControl.java
new file mode 100644
index 000000000..7b521f391
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftControl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * <code>SoftControl</code> are the basic controls
+ * used for control-rate processing.
+ *
+ * @author Karl Helgason
+ */
+public interface SoftControl {
+
+ public double[] get(int instance, String name);
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftCubicResampler.java b/src/share/classes/com/sun/media/sound/SoftCubicResampler.java
new file mode 100644
index 000000000..cc9941110
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftCubicResampler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A resampler that uses third-order (cubic) interpolation.
+ *
+ * @author Karl Helgason
+ */
+public class SoftCubicResampler extends SoftAbstractResampler {
+
+ public int getPadding() {
+ return 3;
+ }
+
+ public void interpolate(float[] in, float[] in_offset, float in_end,
+ float[] startpitch, float pitchstep, float[] out, int[] out_offset,
+ int out_end) {
+ float pitch = startpitch[0];
+ float ix = in_offset[0];
+ int ox = out_offset[0];
+ float ix_end = in_end;
+ int ox_end = out_end;
+ if (pitchstep == 0) {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float fix = ix - iix;
+ float y0 = in[iix - 1];
+ float y1 = in[iix];
+ float y2 = in[iix + 1];
+ float y3 = in[iix + 2];
+ float a0 = y3 - y2 + y1 - y0;
+ float a1 = y0 - y1 - a0;
+ float a2 = y2 - y0;
+ float a3 = y1;
+ //float fix2 = fix * fix;
+ //out[ox++] = (a0 * fix + a1) * fix2 + (a2 * fix + a3);
+ out[ox++] = ((a0 * fix + a1) * fix + a2) * fix + a3;
+ ix += pitch;
+ }
+ } else {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float fix = ix - iix;
+ float y0 = in[iix - 1];
+ float y1 = in[iix];
+ float y2 = in[iix + 1];
+ float y3 = in[iix + 2];
+ float a0 = y3 - y2 + y1 - y0;
+ float a1 = y0 - y1 - a0;
+ float a2 = y2 - y0;
+ float a3 = y1;
+ //float fix2 = fix * fix;
+ //out[ox++] = (a0 * fix + a1) * fix2 + (a2 * fix + a3);
+ out[ox++] = ((a0 * fix + a1) * fix + a2) * fix + a3;
+ ix += pitch;
+ pitch += pitchstep;
+ }
+ }
+ in_offset[0] = ix;
+ out_offset[0] = ox;
+ startpitch[0] = pitch;
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftEnvelopeGenerator.java b/src/share/classes/com/sun/media/sound/SoftEnvelopeGenerator.java
new file mode 100644
index 000000000..c5462ab91
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftEnvelopeGenerator.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * AHDSR control signal envelope generator.
+ *
+ * @author Karl Helgason
+ */
+public class SoftEnvelopeGenerator implements SoftProcess {
+
+ public final static int EG_OFF = 0;
+ public final static int EG_DELAY = 1;
+ public final static int EG_ATTACK = 2;
+ public final static int EG_HOLD = 3;
+ public final static int EG_DECAY = 4;
+ public final static int EG_SUSTAIN = 5;
+ public final static int EG_RELEASE = 6;
+ public final static int EG_SHUTDOWN = 7;
+ public final static int EG_END = 8;
+ int max_count = 10;
+ int used_count = 0;
+ private int[] stage = new int[max_count];
+ private int[] stage_ix = new int[max_count];
+ private double[] stage_v = new double[max_count];
+ private int[] stage_count = new int[max_count];
+ private double[][] on = new double[max_count][1];
+ private double[][] active = new double[max_count][1];
+ private double[][] out = new double[max_count][1];
+ private double[][] delay = new double[max_count][1];
+ private double[][] attack = new double[max_count][1];
+ private double[][] hold = new double[max_count][1];
+ private double[][] decay = new double[max_count][1];
+ private double[][] sustain = new double[max_count][1];
+ private double[][] release = new double[max_count][1];
+ private double[][] shutdown = new double[max_count][1];
+ private double[][] release2 = new double[max_count][1];
+ private double[][] attack2 = new double[max_count][1];
+ private double[][] decay2 = new double[max_count][1];
+ private double control_time = 0;
+
+ public void reset() {
+ for (int i = 0; i < used_count; i++) {
+ stage[i] = 0;
+ on[i][0] = 0;
+ out[i][0] = 0;
+ delay[i][0] = 0;
+ attack[i][0] = 0;
+ hold[i][0] = 0;
+ decay[i][0] = 0;
+ sustain[i][0] = 0;
+ release[i][0] = 0;
+ shutdown[i][0] = 0;
+ attack2[i][0] = 0;
+ decay2[i][0] = 0;
+ release2[i][0] = 0;
+ }
+ used_count = 0;
+ }
+
+ public void init(SoftSynthesizer synth) {
+ control_time = 1.0 / synth.getControlRate();
+ processControlLogic();
+ }
+
+ public double[] get(int instance, String name) {
+ if (instance >= used_count)
+ used_count = instance + 1;
+ if (name == null)
+ return out[instance];
+ if (name.equals("on"))
+ return on[instance];
+ if (name.equals("active"))
+ return active[instance];
+ if (name.equals("delay"))
+ return delay[instance];
+ if (name.equals("attack"))
+ return attack[instance];
+ if (name.equals("hold"))
+ return hold[instance];
+ if (name.equals("decay"))
+ return decay[instance];
+ if (name.equals("sustain"))
+ return sustain[instance];
+ if (name.equals("release"))
+ return release[instance];
+ if (name.equals("shutdown"))
+ return shutdown[instance];
+ if (name.equals("attack2"))
+ return attack2[instance];
+ if (name.equals("decay2"))
+ return decay2[instance];
+ if (name.equals("release2"))
+ return release2[instance];
+
+ return null;
+ }
+
+ public void processControlLogic() {
+ for (int i = 0; i < used_count; i++) {
+
+ if (stage[i] == EG_END)
+ continue;
+
+ if ((stage[i] > EG_OFF) && (stage[i] < EG_RELEASE)) {
+ if (on[i][0] < 0.5) {
+ if (on[i][0] < -0.5) {
+ stage_count[i] = (int)(Math.pow(2,
+ this.shutdown[i][0] / 1200.0) / control_time);
+ if (stage_count[i] < 0)
+ stage_count[i] = 0;
+ stage_v[i] = out[i][0];
+ stage_ix[i] = 0;
+ stage[i] = EG_SHUTDOWN;
+ } else {
+ if ((release2[i][0] < 0.000001) && release[i][0] < 0
+ && Double.isInfinite(release[i][0])) {
+ out[i][0] = 0;
+ active[i][0] = 0;
+ stage[i] = EG_END;
+ continue;
+ }
+
+ stage_count[i] = (int)(Math.pow(2,
+ this.release[i][0] / 1200.0) / control_time);
+ stage_count[i]
+ += (int)(this.release2[i][0]/(control_time * 1000));
+ if (stage_count[i] < 0)
+ stage_count[i] = 0;
+ // stage_v[i] = out[i][0];
+ stage_ix[i] = 0;
+
+ double m = 1 - out[i][0];
+ stage_ix[i] = (int)(stage_count[i] * m);
+
+ stage[i] = EG_RELEASE;
+ }
+ }
+ }
+
+ switch (stage[i]) {
+ case EG_OFF:
+ active[i][0] = 1;
+ if (on[i][0] < 0.5)
+ break;
+ stage[i] = EG_DELAY;
+ stage_ix[i] = (int)(Math.pow(2,
+ this.delay[i][0] / 1200.0) / control_time);
+ if (stage_ix[i] < 0)
+ stage_ix[i] = 0;
+ case EG_DELAY:
+ if (stage_ix[i] == 0) {
+ double attack = this.attack[i][0];
+ double attack2 = this.attack2[i][0];
+
+ if (attack2 < 0.000001
+ && (attack < 0 && Double.isInfinite(attack))) {
+ out[i][0] = 1;
+ stage[i] = EG_HOLD;
+ stage_count[i] = (int)(Math.pow(2,
+ this.hold[i][0] / 1200.0) / control_time);
+ stage_ix[i] = 0;
+ } else {
+ stage[i] = EG_ATTACK;
+ stage_count[i] = (int)(Math.pow(2,
+ attack / 1200.0) / control_time);
+ stage_count[i] += (int)(attack2 / (control_time * 1000));
+ if (stage_count[i] < 0)
+ stage_count[i] = 0;
+ stage_ix[i] = 0;
+ }
+ } else
+ stage_ix[i]--;
+ break;
+ case EG_ATTACK:
+ stage_ix[i]++;
+ if (stage_ix[i] >= stage_count[i]) {
+ out[i][0] = 1;
+ stage[i] = EG_HOLD;
+ } else {
+ // CONVEX attack
+ double a = ((double)stage_ix[i]) / ((double)stage_count[i]);
+ a = 1 + ((40.0 / 96.0) / Math.log(10)) * Math.log(a);
+ if (a < 0)
+ a = 0;
+ else if (a > 1)
+ a = 1;
+ out[i][0] = a;
+ }
+ break;
+ case EG_HOLD:
+ stage_ix[i]++;
+ if (stage_ix[i] >= stage_count[i]) {
+ stage[i] = EG_DECAY;
+ stage_count[i] = (int)(Math.pow(2,
+ this.decay[i][0] / 1200.0) / control_time);
+ stage_count[i] += (int)(this.decay2[i][0]/(control_time*1000));
+ if (stage_count[i] < 0)
+ stage_count[i] = 0;
+ stage_ix[i] = 0;
+ }
+ break;
+ case EG_DECAY:
+ stage_ix[i]++;
+ double sustain = this.sustain[i][0] * (1.0 / 1000.0);
+ if (stage_ix[i] >= stage_count[i]) {
+ out[i][0] = sustain;
+ stage[i] = EG_SUSTAIN;
+ if (sustain < 0.001) {
+ out[i][0] = 0;
+ active[i][0] = 0;
+ stage[i] = EG_END;
+ }
+ } else {
+ double m = ((double)stage_ix[i]) / ((double)stage_count[i]);
+ out[i][0] = (1 - m) + sustain * m;
+ }
+ break;
+ case EG_SUSTAIN:
+ break;
+ case EG_RELEASE:
+ stage_ix[i]++;
+ if (stage_ix[i] >= stage_count[i]) {
+ out[i][0] = 0;
+ active[i][0] = 0;
+ stage[i] = EG_END;
+ } else {
+ double m = ((double)stage_ix[i]) / ((double)stage_count[i]);
+ out[i][0] = (1 - m); // *stage_v[i];
+
+ if (on[i][0] < -0.5) {
+ stage_count[i] = (int)(Math.pow(2,
+ this.shutdown[i][0] / 1200.0) / control_time);
+ if (stage_count[i] < 0)
+ stage_count[i] = 0;
+ stage_v[i] = out[i][0];
+ stage_ix[i] = 0;
+ stage[i] = EG_SHUTDOWN;
+ }
+
+ // re-damping
+ if (on[i][0] > 0.5) {
+ sustain = this.sustain[i][0] * (1.0 / 1000.0);
+ if (out[i][0] > sustain) {
+ stage[i] = EG_DECAY;
+ stage_count[i] = (int)(Math.pow(2,
+ this.decay[i][0] / 1200.0) / control_time);
+ stage_count[i] +=
+ (int)(this.decay2[i][0]/(control_time*1000));
+ if (stage_count[i] < 0)
+ stage_count[i] = 0;
+ m = (out[i][0] - 1) / (sustain - 1);
+ stage_ix[i] = (int) (stage_count[i] * m);
+ }
+ }
+
+ }
+ break;
+ case EG_SHUTDOWN:
+ stage_ix[i]++;
+ if (stage_ix[i] >= stage_count[i]) {
+ out[i][0] = 0;
+ active[i][0] = 0;
+ stage[i] = EG_END;
+ } else {
+ double m = ((double)stage_ix[i]) / ((double)stage_count[i]);
+ out[i][0] = (1 - m) * stage_v[i];
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftFilter.java b/src/share/classes/com/sun/media/sound/SoftFilter.java
new file mode 100644
index 000000000..0468f15be
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftFilter.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Infinite impulse response (IIR) filter class.
+ *
+ * The filters where implemented and adapted using algorithms from musicdsp.org
+ * archive: 1-RC and C filter, Simple 2-pole LP LP and HP filter, biquad,
+ * tweaked butterworth RBJ Audio-EQ-Cookbook, EQ filter kookbook
+ *
+ * @author Karl Helgason
+ */
+public class SoftFilter {
+
+ public final static int FILTERTYPE_LP6 = 0x00;
+ public final static int FILTERTYPE_LP12 = 0x01;
+ public final static int FILTERTYPE_HP12 = 0x11;
+ public final static int FILTERTYPE_BP12 = 0x21;
+ public final static int FILTERTYPE_NP12 = 0x31;
+ public final static int FILTERTYPE_LP24 = 0x03;
+ public final static int FILTERTYPE_HP24 = 0x13;
+
+ //
+ // 0x0 = 1st-order, 6 dB/oct
+ // 0x1 = 2nd-order, 12 dB/oct
+ // 0x2 = 3rd-order, 18 dB/oct
+ // 0x3 = 4th-order, 24 db/oct
+ //
+ // 0x00 = LP, Low Pass Filter
+ // 0x10 = HP, High Pass Filter
+ // 0x20 = BP, Band Pass Filter
+ // 0x30 = NP, Notch or Band Elimination Filter
+ //
+ private int filtertype = FILTERTYPE_LP6;
+ private float samplerate;
+ private float x1;
+ private float x2;
+ private float y1;
+ private float y2;
+ private float xx1;
+ private float xx2;
+ private float yy1;
+ private float yy2;
+ private float a0;
+ private float a1;
+ private float a2;
+ private float b1;
+ private float b2;
+ private float q;
+ private float gain = 1;
+ private float wet = 0;
+ private float last_wet = 0;
+ private float last_a0;
+ private float last_a1;
+ private float last_a2;
+ private float last_b1;
+ private float last_b2;
+ private float last_q;
+ private float last_gain;
+ private boolean last_set = false;
+ private double cutoff = 44100;
+ private double resonancedB = 0;
+ private boolean dirty = true;
+
+ public SoftFilter(float samplerate) {
+ this.samplerate = samplerate;
+ dirty = true;
+ }
+
+ public void setFrequency(double cent) {
+ if (cutoff == cent)
+ return;
+ cutoff = cent;
+ dirty = true;
+ }
+
+ public void setResonance(double db) {
+ if (resonancedB == db)
+ return;
+ resonancedB = db;
+ dirty = true;
+ }
+
+ public void reset() {
+ dirty = true;
+ last_set = false;
+ x1 = 0;
+ x2 = 0;
+ y1 = 0;
+ y2 = 0;
+ xx1 = 0;
+ xx2 = 0;
+ yy1 = 0;
+ yy2 = 0;
+ wet = 0.0f;
+ gain = 1.0f;
+ a0 = 0;
+ a1 = 0;
+ a2 = 0;
+ b1 = 0;
+ b2 = 0;
+ }
+
+ public void setFilterType(int filtertype) {
+ this.filtertype = filtertype;
+ }
+
+ public void processAudio(SoftAudioBuffer sbuffer) {
+ if (filtertype == FILTERTYPE_LP6)
+ filter1(sbuffer);
+ if (filtertype == FILTERTYPE_LP12)
+ filter2(sbuffer);
+ if (filtertype == FILTERTYPE_HP12)
+ filter2(sbuffer);
+ if (filtertype == FILTERTYPE_BP12)
+ filter2(sbuffer);
+ if (filtertype == FILTERTYPE_NP12)
+ filter2(sbuffer);
+ if (filtertype == FILTERTYPE_LP24)
+ filter4(sbuffer);
+ if (filtertype == FILTERTYPE_HP24)
+ filter4(sbuffer);
+ }
+
+ public void filter4(SoftAudioBuffer sbuffer) {
+
+ float[] buffer = sbuffer.array();
+
+ if (dirty) {
+ filter2calc();
+ dirty = false;
+ }
+ if (!last_set) {
+ last_a0 = a0;
+ last_a1 = a1;
+ last_a2 = a2;
+ last_b1 = b1;
+ last_b2 = b2;
+ last_gain = gain;
+ last_wet = wet;
+ last_set = true;
+ }
+
+ if (wet > 0 || last_wet > 0) {
+
+ int len = buffer.length;
+ float a0 = this.last_a0;
+ float a1 = this.last_a1;
+ float a2 = this.last_a2;
+ float b1 = this.last_b1;
+ float b2 = this.last_b2;
+ float gain = this.last_gain;
+ float wet = this.last_wet;
+ float a0_delta = (this.a0 - this.last_a0) / len;
+ float a1_delta = (this.a1 - this.last_a1) / len;
+ float a2_delta = (this.a2 - this.last_a2) / len;
+ float b1_delta = (this.b1 - this.last_b1) / len;
+ float b2_delta = (this.b2 - this.last_b2) / len;
+ float gain_delta = (this.gain - this.last_gain) / len;
+ float wet_delta = (this.wet - this.last_wet) / len;
+ float x1 = this.x1;
+ float x2 = this.x2;
+ float y1 = this.y1;
+ float y2 = this.y2;
+ float xx1 = this.xx1;
+ float xx2 = this.xx2;
+ float yy1 = this.yy1;
+ float yy2 = this.yy2;
+
+ if (wet_delta != 0) {
+ for (int i = 0; i < len; i++) {
+ a0 += a0_delta;
+ a1 += a1_delta;
+ a2 += a2_delta;
+ b1 += b1_delta;
+ b2 += b2_delta;
+ gain += gain_delta;
+ wet += wet_delta;
+ float x = buffer[i];
+ float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
+ float xx = (y * gain) * wet + (x) * (1 - wet);
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
+ buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
+ xx2 = xx1;
+ xx1 = xx;
+ yy2 = yy1;
+ yy1 = yy;
+ }
+ } else if (a0_delta == 0 && a1_delta == 0 && a2_delta == 0
+ && b1_delta == 0 && b2_delta == 0) {
+ for (int i = 0; i < len; i++) {
+ float x = buffer[i];
+ float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
+ float xx = (y * gain) * wet + (x) * (1 - wet);
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
+ buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
+ xx2 = xx1;
+ xx1 = xx;
+ yy2 = yy1;
+ yy1 = yy;
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ a0 += a0_delta;
+ a1 += a1_delta;
+ a2 += a2_delta;
+ b1 += b1_delta;
+ b2 += b2_delta;
+ gain += gain_delta;
+ float x = buffer[i];
+ float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
+ float xx = (y * gain) * wet + (x) * (1 - wet);
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
+ buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
+ xx2 = xx1;
+ xx1 = xx;
+ yy2 = yy1;
+ yy1 = yy;
+ }
+ }
+
+ if (Math.abs(x1) < 1.0E-8)
+ x1 = 0;
+ if (Math.abs(x2) < 1.0E-8)
+ x2 = 0;
+ if (Math.abs(y1) < 1.0E-8)
+ y1 = 0;
+ if (Math.abs(y2) < 1.0E-8)
+ y2 = 0;
+ this.x1 = x1;
+ this.x2 = x2;
+ this.y1 = y1;
+ this.y2 = y2;
+ this.xx1 = xx1;
+ this.xx2 = xx2;
+ this.yy1 = yy1;
+ this.yy2 = yy2;
+ }
+
+ this.last_a0 = this.a0;
+ this.last_a1 = this.a1;
+ this.last_a2 = this.a2;
+ this.last_b1 = this.b1;
+ this.last_b2 = this.b2;
+ this.last_gain = this.gain;
+ this.last_wet = this.wet;
+
+ }
+
+ private double sinh(double x) {
+ return (Math.exp(x) - Math.exp(-x)) * 0.5;
+ }
+
+ public void filter2calc() {
+
+ double resonancedB = this.resonancedB;
+ if (resonancedB < 0)
+ resonancedB = 0; // Negative dB are illegal.
+ if (resonancedB > 30)
+ resonancedB = 30; // At least 22.5 dB is needed.
+ if (filtertype == FILTERTYPE_LP24 || filtertype == FILTERTYPE_HP24)
+ resonancedB *= 0.6;
+
+ if (filtertype == FILTERTYPE_BP12) {
+ wet = 1;
+ double r = (cutoff / samplerate);
+ if (r > 0.45)
+ r = 0.45;
+
+ double bandwidth = Math.PI * Math.pow(10.0, -(resonancedB / 20));
+
+ double omega = 2 * Math.PI * r;
+ double cs = Math.cos(omega);
+ double sn = Math.sin(omega);
+ double alpha = sn * sinh((Math.log(2)*bandwidth*omega) / (sn * 2));
+
+ double b0 = alpha;
+ double b1 = 0;
+ double b2 = -alpha;
+ double a0 = 1 + alpha;
+ double a1 = -2 * cs;
+ double a2 = 1 - alpha;
+
+ double cf = 1.0 / a0;
+ this.b1 = (float) (a1 * cf);
+ this.b2 = (float) (a2 * cf);
+ this.a0 = (float) (b0 * cf);
+ this.a1 = (float) (b1 * cf);
+ this.a2 = (float) (b2 * cf);
+ }
+
+ if (filtertype == FILTERTYPE_NP12) {
+ wet = 1;
+ double r = (cutoff / samplerate);
+ if (r > 0.45)
+ r = 0.45;
+
+ double bandwidth = Math.PI * Math.pow(10.0, -(resonancedB / 20));
+
+ double omega = 2 * Math.PI * r;
+ double cs = Math.cos(omega);
+ double sn = Math.sin(omega);
+ double alpha = sn * sinh((Math.log(2)*bandwidth*omega) / (sn*2));
+
+ double b0 = 1;
+ double b1 = -2 * cs;
+ double b2 = 1;
+ double a0 = 1 + alpha;
+ double a1 = -2 * cs;
+ double a2 = 1 - alpha;
+
+ double cf = 1.0 / a0;
+ this.b1 = (float)(a1 * cf);
+ this.b2 = (float)(a2 * cf);
+ this.a0 = (float)(b0 * cf);
+ this.a1 = (float)(b1 * cf);
+ this.a2 = (float)(b2 * cf);
+ }
+
+ if (filtertype == FILTERTYPE_LP12 || filtertype == FILTERTYPE_LP24) {
+ double r = (cutoff / samplerate);
+ if (r > 0.45) {
+ if (wet == 0) {
+ if (resonancedB < 0.00001)
+ wet = 0.0f;
+ else
+ wet = 1.0f;
+ }
+ r = 0.45;
+ } else
+ wet = 1.0f;
+
+ double c = 1.0 / (Math.tan(Math.PI * r));
+ double csq = c * c;
+ double resonance = Math.pow(10.0, -(resonancedB / 20));
+ double q = Math.sqrt(2.0f) * resonance;
+ double a0 = 1.0 / (1.0 + (q * c) + (csq));
+ double a1 = 2.0 * a0;
+ double a2 = a0;
+ double b1 = (2.0 * a0) * (1.0 - csq);
+ double b2 = a0 * (1.0 - (q * c) + csq);
+
+ this.a0 = (float)a0;
+ this.a1 = (float)a1;
+ this.a2 = (float)a2;
+ this.b1 = (float)b1;
+ this.b2 = (float)b2;
+
+ }
+
+ if (filtertype == FILTERTYPE_HP12 || filtertype == FILTERTYPE_HP24) {
+ double r = (cutoff / samplerate);
+ if (r > 0.45)
+ r = 0.45;
+ if (r < 0.0001)
+ r = 0.0001;
+ wet = 1.0f;
+ double c = (Math.tan(Math.PI * (r)));
+ double csq = c * c;
+ double resonance = Math.pow(10.0, -(resonancedB / 20));
+ double q = Math.sqrt(2.0f) * resonance;
+ double a0 = 1.0 / (1.0 + (q * c) + (csq));
+ double a1 = -2.0 * a0;
+ double a2 = a0;
+ double b1 = (2.0 * a0) * (csq - 1.0);
+ double b2 = a0 * (1.0 - (q * c) + csq);
+
+ this.a0 = (float)a0;
+ this.a1 = (float)a1;
+ this.a2 = (float)a2;
+ this.b1 = (float)b1;
+ this.b2 = (float)b2;
+
+ }
+
+ }
+
+ public void filter2(SoftAudioBuffer sbuffer) {
+
+ float[] buffer = sbuffer.array();
+
+ if (dirty) {
+ filter2calc();
+ dirty = false;
+ }
+ if (!last_set) {
+ last_a0 = a0;
+ last_a1 = a1;
+ last_a2 = a2;
+ last_b1 = b1;
+ last_b2 = b2;
+ last_q = q;
+ last_gain = gain;
+ last_wet = wet;
+ last_set = true;
+ }
+
+ if (wet > 0 || last_wet > 0) {
+
+ int len = buffer.length;
+ float a0 = this.last_a0;
+ float a1 = this.last_a1;
+ float a2 = this.last_a2;
+ float b1 = this.last_b1;
+ float b2 = this.last_b2;
+ float gain = this.last_gain;
+ float wet = this.last_wet;
+ float a0_delta = (this.a0 - this.last_a0) / len;
+ float a1_delta = (this.a1 - this.last_a1) / len;
+ float a2_delta = (this.a2 - this.last_a2) / len;
+ float b1_delta = (this.b1 - this.last_b1) / len;
+ float b2_delta = (this.b2 - this.last_b2) / len;
+ float gain_delta = (this.gain - this.last_gain) / len;
+ float wet_delta = (this.wet - this.last_wet) / len;
+ float x1 = this.x1;
+ float x2 = this.x2;
+ float y1 = this.y1;
+ float y2 = this.y2;
+
+ if (wet_delta != 0) {
+ for (int i = 0; i < len; i++) {
+ a0 += a0_delta;
+ a1 += a1_delta;
+ a2 += a2_delta;
+ b1 += b1_delta;
+ b2 += b2_delta;
+ gain += gain_delta;
+ wet += wet_delta;
+ float x = buffer[i];
+ float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
+ buffer[i] = (y * gain) * wet + (x) * (1 - wet);
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ }
+ } else if (a0_delta == 0 && a1_delta == 0 && a2_delta == 0
+ && b1_delta == 0 && b2_delta == 0) {
+ for (int i = 0; i < len; i++) {
+ float x = buffer[i];
+ float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
+ buffer[i] = y * gain;
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ a0 += a0_delta;
+ a1 += a1_delta;
+ a2 += a2_delta;
+ b1 += b1_delta;
+ b2 += b2_delta;
+ gain += gain_delta;
+ float x = buffer[i];
+ float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
+ buffer[i] = y * gain;
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ }
+ }
+
+ if (Math.abs(x1) < 1.0E-8)
+ x1 = 0;
+ if (Math.abs(x2) < 1.0E-8)
+ x2 = 0;
+ if (Math.abs(y1) < 1.0E-8)
+ y1 = 0;
+ if (Math.abs(y2) < 1.0E-8)
+ y2 = 0;
+ this.x1 = x1;
+ this.x2 = x2;
+ this.y1 = y1;
+ this.y2 = y2;
+ }
+
+ this.last_a0 = this.a0;
+ this.last_a1 = this.a1;
+ this.last_a2 = this.a2;
+ this.last_b1 = this.b1;
+ this.last_b2 = this.b2;
+ this.last_q = this.q;
+ this.last_gain = this.gain;
+ this.last_wet = this.wet;
+
+ }
+
+ public void filter1calc() {
+ if (cutoff < 120)
+ cutoff = 120;
+ double c = (7.0 / 6.0) * Math.PI * 2 * cutoff / samplerate;
+ if (c > 1)
+ c = 1;
+ a0 = (float)(Math.sqrt(1 - Math.cos(c)) * Math.sqrt(0.5 * Math.PI));
+ if (resonancedB < 0)
+ resonancedB = 0;
+ if (resonancedB > 20)
+ resonancedB = 20;
+ q = (float)(Math.sqrt(0.5) * Math.pow(10.0, -(resonancedB / 20)));
+ gain = (float)Math.pow(10, -((resonancedB)) / 40.0);
+ if (wet == 0.0f)
+ if (resonancedB > 0.00001 || c < 0.9999999)
+ wet = 1.0f;
+ }
+
+ public void filter1(SoftAudioBuffer sbuffer) {
+
+ float[] buffer = sbuffer.array();
+
+ if (dirty) {
+ filter1calc();
+ dirty = false;
+ }
+ if (!last_set) {
+ last_a0 = a0;
+ last_q = q;
+ last_gain = gain;
+ last_wet = wet;
+ last_set = true;
+ }
+
+ if (wet > 0 || last_wet > 0) {
+
+ int len = buffer.length;
+ float a0 = this.last_a0;
+ float q = this.last_q;
+ float gain = this.last_gain;
+ float wet = this.last_wet;
+ float a0_delta = (this.a0 - this.last_a0) / len;
+ float q_delta = (this.q - this.last_q) / len;
+ float gain_delta = (this.gain - this.last_gain) / len;
+ float wet_delta = (this.wet - this.last_wet) / len;
+ float y2 = this.y2;
+ float y1 = this.y1;
+
+ if (wet_delta != 0) {
+ for (int i = 0; i < len; i++) {
+ a0 += a0_delta;
+ q += q_delta;
+ gain += gain_delta;
+ wet += wet_delta;
+ y1 = (1 - q * a0) * y1 - (a0) * y2 + (a0) * buffer[i];
+ y2 = (1 - q * a0) * y2 + (a0) * y1;
+ buffer[i] = y2 * gain * wet + buffer[i] * (1 - wet);
+ }
+ } else if (a0_delta == 0 && q_delta == 0) {
+ for (int i = 0; i < len; i++) {
+ y1 = (1 - q * a0) * y1 - (a0) * y2 + (a0) * buffer[i];
+ y2 = (1 - q * a0) * y2 + (a0) * y1;
+ buffer[i] = y2 * gain;
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ a0 += a0_delta;
+ q += q_delta;
+ gain += gain_delta;
+ y1 = (1 - q * a0) * y1 - (a0) * y2 + (a0) * buffer[i];
+ y2 = (1 - q * a0) * y2 + (a0) * y1;
+ buffer[i] = y2 * gain;
+ }
+ }
+
+ if (Math.abs(y2) < 1.0E-8)
+ y2 = 0;
+ if (Math.abs(y1) < 1.0E-8)
+ y1 = 0;
+ this.y2 = y2;
+ this.y1 = y1;
+ }
+
+ this.last_a0 = this.a0;
+ this.last_q = this.q;
+ this.last_gain = this.gain;
+ this.last_wet = this.wet;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftInstrument.java b/src/share/classes/com/sun/media/sound/SoftInstrument.java
new file mode 100644
index 000000000..fbeaf6b8d
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftInstrument.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.Instrument;
+import javax.sound.midi.MidiChannel;
+
+/**
+ * Software synthesizer internal instrument.
+ *
+ * @author Karl Helgason
+ */
+public class SoftInstrument extends Instrument {
+
+ private SoftPerformer[] performers;
+ private ModelPerformer[] modelperformers;
+ private Object data;
+ private ModelInstrument ins;
+
+ public SoftInstrument(ModelInstrument ins) {
+ super(ins.getSoundbank(), ins.getPatch(), ins.getName(),
+ ins.getDataClass());
+ data = ins.getData();
+ this.ins = ins;
+ initPerformers(((ModelInstrument)ins).getPerformers());
+ }
+
+ public SoftInstrument(ModelInstrument ins,
+ ModelPerformer[] overrideperformers) {
+ super(ins.getSoundbank(), ins.getPatch(), ins.getName(),
+ ins.getDataClass());
+ data = ins.getData();
+ this.ins = ins;
+ initPerformers(overrideperformers);
+ }
+
+ private void initPerformers(ModelPerformer[] modelperformers) {
+ this.modelperformers = modelperformers;
+ performers = new SoftPerformer[modelperformers.length];
+ for (int i = 0; i < modelperformers.length; i++)
+ performers[i] = new SoftPerformer(modelperformers[i]);
+ }
+
+ public ModelDirector getDirector(MidiChannel channel,
+ ModelDirectedPlayer player) {
+ return ins.getDirector(modelperformers, channel, player);
+ }
+
+ public ModelInstrument getSourceInstrument() {
+ return ins;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public SoftPerformer[] getPerformers() {
+ return performers;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftJitterCorrector.java b/src/share/classes/com/sun/media/sound/SoftJitterCorrector.java
new file mode 100644
index 000000000..98d205b6d
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftJitterCorrector.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+
+/**
+ * A jitter corrector to be used with SoftAudioPusher.
+ *
+ * @author Karl Helgason
+ */
+public class SoftJitterCorrector extends AudioInputStream {
+
+ private static class JitterStream extends InputStream {
+
+ static int MAX_BUFFER_SIZE = 1048576;
+ boolean active = true;
+ Thread thread;
+ AudioInputStream stream;
+ // Cyclic buffer
+ int writepos = 0;
+ int readpos = 0;
+ byte[][] buffers;
+ Object buffers_mutex = new Object();
+
+ // Adapative Drift Statistics
+ int w_count = 1000;
+ int w_min_tol = 2;
+ int w_max_tol = 10;
+ int w = 0;
+ int w_min = -1;
+ // Current read buffer
+ int bbuffer_pos = 0;
+ int bbuffer_max = 0;
+ byte[] bbuffer = null;
+
+ public byte[] nextReadBuffer() {
+ synchronized (buffers_mutex) {
+ if (writepos > readpos) {
+ int w_m = writepos - readpos;
+ if (w_m < w_min)
+ w_min = w_m;
+
+ int buffpos = readpos;
+ readpos++;
+ return buffers[buffpos % buffers.length];
+ }
+ w_min = -1;
+ w = w_count - 1;
+ }
+ while (true) {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ //e.printStackTrace();
+ return null;
+ }
+ synchronized (buffers_mutex) {
+ if (writepos > readpos) {
+ w = 0;
+ w_min = -1;
+ w = w_count - 1;
+ int buffpos = readpos;
+ readpos++;
+ return buffers[buffpos % buffers.length];
+ }
+ }
+ }
+ }
+
+ public byte[] nextWriteBuffer() {
+ synchronized (buffers_mutex) {
+ return buffers[writepos % buffers.length];
+ }
+ }
+
+ public void commit() {
+ synchronized (buffers_mutex) {
+ writepos++;
+ if ((writepos - readpos) > buffers.length) {
+ int newsize = (writepos - readpos) + 10;
+ newsize = Math.max(buffers.length * 2, newsize);
+ buffers = new byte[newsize][buffers[0].length];
+ }
+ }
+ }
+
+ public JitterStream(AudioInputStream s, int buffersize,
+ int smallbuffersize) {
+ this.w_count = 10 * (buffersize / smallbuffersize);
+ if (w_count < 100)
+ w_count = 100;
+ this.buffers
+ = new byte[(buffersize/smallbuffersize)+10][smallbuffersize];
+ this.bbuffer_max = MAX_BUFFER_SIZE / smallbuffersize;
+ this.stream = s;
+
+
+ Runnable runnable = new Runnable() {
+
+ public void run() {
+ AudioFormat format = stream.getFormat();
+ int bufflen = buffers[0].length;
+ int frames = bufflen / format.getFrameSize();
+ long nanos = (long) (frames * 1000000000.0
+ / format.getSampleRate());
+ long now = System.nanoTime();
+ long next = now + nanos;
+ int correction = 0;
+ while (true) {
+ synchronized (JitterStream.this) {
+ if (!active)
+ break;
+ }
+ int curbuffsize;
+ synchronized (buffers) {
+ curbuffsize = writepos - readpos;
+ if (correction == 0) {
+ w++;
+ if (w_min != Integer.MAX_VALUE) {
+ if (w == w_count) {
+ correction = 0;
+ if (w_min < w_min_tol) {
+ correction = (w_min_tol + w_max_tol)
+ / 2 - w_min;
+ }
+ if (w_min > w_max_tol) {
+ correction = (w_min_tol + w_max_tol)
+ / 2 - w_min;
+ }
+ w = 0;
+ w_min = Integer.MAX_VALUE;
+ }
+ }
+ }
+ }
+ while (curbuffsize > bbuffer_max) {
+ synchronized (buffers) {
+ curbuffsize = writepos - readpos;
+ }
+ synchronized (JitterStream.this) {
+ if (!active)
+ break;
+ }
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ //e.printStackTrace();
+ }
+ }
+
+ if (correction < 0)
+ correction++;
+ else {
+ byte[] buff = nextWriteBuffer();
+ try {
+ int n = 0;
+ while (n != buff.length) {
+ int s = stream.read(buff, n, buff.length
+ - n);
+ if (s < 0)
+ throw new EOFException();
+ if (s == 0)
+ Thread.yield();
+ n += s;
+ }
+ } catch (IOException e1) {
+ //e1.printStackTrace();
+ }
+ commit();
+ }
+
+ if (correction > 0) {
+ correction--;
+ next = System.nanoTime() + nanos;
+ continue;
+ }
+ long wait = next - System.nanoTime();
+ if (wait > 0) {
+ try {
+ Thread.sleep(wait / 1000000L);
+ } catch (InterruptedException e) {
+ //e.printStackTrace();
+ }
+ }
+ next += nanos;
+ }
+ }
+ };
+
+ thread = new Thread(runnable);
+ thread.setPriority(Thread.MAX_PRIORITY);
+ thread.start();
+ }
+
+ public void close() throws IOException {
+ synchronized (this) {
+ active = false;
+ }
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ //e.printStackTrace();
+ }
+ stream.close();
+ }
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ if (read(b) == -1)
+ return -1;
+ return b[0] & 0xFF;
+ }
+
+ public void fillBuffer() {
+ bbuffer = nextReadBuffer();
+ bbuffer_pos = 0;
+ }
+
+ public int read(byte[] b, int off, int len) {
+ if (bbuffer == null)
+ fillBuffer();
+ int bbuffer_len = bbuffer.length;
+ int offlen = off + len;
+ while (off < offlen) {
+ if (available() == 0)
+ fillBuffer();
+ else {
+ byte[] bbuffer = this.bbuffer;
+ int bbuffer_pos = this.bbuffer_pos;
+ while (off < offlen && bbuffer_pos < bbuffer_len)
+ b[off++] = bbuffer[bbuffer_pos++];
+ this.bbuffer_pos = bbuffer_pos;
+ }
+ }
+ return len;
+ }
+
+ public int available() {
+ return bbuffer.length - bbuffer_pos;
+ }
+ }
+
+ public SoftJitterCorrector(AudioInputStream stream, int buffersize,
+ int smallbuffersize) {
+ super(new JitterStream(stream, buffersize, smallbuffersize),
+ stream.getFormat(), stream.getFrameLength());
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftLanczosResampler.java b/src/share/classes/com/sun/media/sound/SoftLanczosResampler.java
new file mode 100644
index 000000000..526cd4327
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftLanczosResampler.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Lanczos interpolation resampler.
+ *
+ * @author Karl Helgason
+ */
+public class SoftLanczosResampler extends SoftAbstractResampler {
+
+ float[][] sinc_table;
+ int sinc_table_fsize = 2000;
+ int sinc_table_size = 5;
+ int sinc_table_center = sinc_table_size / 2;
+
+ public SoftLanczosResampler() {
+ super();
+ sinc_table = new float[sinc_table_fsize][];
+ for (int i = 0; i < sinc_table_fsize; i++) {
+ sinc_table[i] = sincTable(sinc_table_size, -i
+ / ((float) sinc_table_fsize));
+ }
+ }
+
+ // Normalized sinc function
+ public static double sinc(double x) {
+ return (x == 0.0) ? 1.0 : Math.sin(Math.PI * x) / (Math.PI * x);
+ }
+
+ // Generate sinc table
+ public static float[] sincTable(int size, float offset) {
+ int center = size / 2;
+ float[] w = new float[size];
+ for (int k = 0; k < size; k++) {
+ float x = (-center + k + offset);
+ if (x < -2 || x > 2)
+ w[k] = 0;
+ else if (x == 0)
+ w[k] = 1;
+ else {
+ w[k] = (float)(2.0 * Math.sin(Math.PI * x)
+ * Math.sin(Math.PI * x / 2.0)
+ / ((Math.PI * x) * (Math.PI * x)));
+ }
+ }
+ return w;
+ }
+
+ public int getPadding() // must be at least half of sinc_table_size
+ {
+ return sinc_table_size / 2 + 2;
+ }
+
+ public void interpolate(float[] in, float[] in_offset, float in_end,
+ float[] startpitch, float pitchstep, float[] out, int[] out_offset,
+ int out_end) {
+ float pitch = startpitch[0];
+ float ix = in_offset[0];
+ int ox = out_offset[0];
+ float ix_end = in_end;
+ int ox_end = out_end;
+
+ if (pitchstep == 0) {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float[] sinc_table
+ = this.sinc_table[(int) ((ix - iix) * sinc_table_fsize)];
+ int xx = iix - sinc_table_center;
+ float y = 0;
+ for (int i = 0; i < sinc_table_size; i++, xx++)
+ y += in[xx] * sinc_table[i];
+ out[ox++] = y;
+ ix += pitch;
+ }
+ } else {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float[] sinc_table
+ = this.sinc_table[(int) ((ix - iix) * sinc_table_fsize)];
+ int xx = iix - sinc_table_center;
+ float y = 0;
+ for (int i = 0; i < sinc_table_size; i++, xx++)
+ y += in[xx] * sinc_table[i];
+ out[ox++] = y;
+
+ ix += pitch;
+ pitch += pitchstep;
+ }
+ }
+ in_offset[0] = ix;
+ out_offset[0] = ox;
+ startpitch[0] = pitch;
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftLimiter.java b/src/share/classes/com/sun/media/sound/SoftLimiter.java
new file mode 100644
index 000000000..7ba0ac660
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftLimiter.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A simple look-ahead volume limiter with very fast attack and fast release.
+ * This filter is used for preventing clipping.
+ *
+ * @author Karl Helgason
+ */
+public class SoftLimiter implements SoftAudioProcessor {
+
+ float lastmax = 0;
+ float gain = 1;
+ float[] temp_bufferL;
+ float[] temp_bufferR;
+ boolean mix = false;
+ SoftAudioBuffer bufferL;
+ SoftAudioBuffer bufferR;
+ SoftAudioBuffer bufferLout;
+ SoftAudioBuffer bufferRout;
+ float controlrate;
+
+ public void init(float samplerate, float controlrate) {
+ this.controlrate = controlrate;
+ }
+
+ public void setInput(int pin, SoftAudioBuffer input) {
+ if (pin == 0)
+ bufferL = input;
+ if (pin == 1)
+ bufferR = input;
+ }
+
+ public void setOutput(int pin, SoftAudioBuffer output) {
+ if (pin == 0)
+ bufferLout = output;
+ if (pin == 1)
+ bufferRout = output;
+ }
+
+ public void setMixMode(boolean mix) {
+ this.mix = mix;
+ }
+
+ public void globalParameterControlChange(int[] slothpath, long param,
+ long value) {
+ }
+
+ double silentcounter = 0;
+
+ public void processAudio() {
+ if (this.bufferL.isSilent()
+ && (this.bufferR == null || this.bufferR.isSilent())) {
+ silentcounter += 1 / controlrate;
+
+ if (silentcounter > 60) {
+ if (!mix) {
+ bufferLout.clear();
+ bufferRout.clear();
+ }
+ return;
+ }
+ } else
+ silentcounter = 0;
+
+ float[] bufferL = this.bufferL.array();
+ float[] bufferR = this.bufferR == null ? null : this.bufferR.array();
+ float[] bufferLout = this.bufferLout.array();
+ float[] bufferRout = this.bufferRout == null
+ ? null : this.bufferRout.array();
+
+ if (temp_bufferL == null || temp_bufferL.length < bufferL.length)
+ temp_bufferL = new float[bufferL.length];
+ if (bufferR != null)
+ if (temp_bufferR == null || temp_bufferR.length < bufferR.length)
+ temp_bufferR = new float[bufferR.length];
+
+ float max = 0;
+ int len = bufferL.length;
+
+ if (bufferR == null) {
+ for (int i = 0; i < len; i++) {
+ if (bufferL[i] > max)
+ max = bufferL[i];
+ if (-bufferL[i] > max)
+ max = -bufferL[i];
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ if (bufferL[i] > max)
+ max = bufferL[i];
+ if (bufferR[i] > max)
+ max = bufferR[i];
+ if (-bufferL[i] > max)
+ max = -bufferL[i];
+ if (-bufferR[i] > max)
+ max = -bufferR[i];
+ }
+ }
+
+ float lmax = lastmax;
+ lastmax = max;
+ if (lmax > max)
+ max = lmax;
+
+ float newgain = 1;
+ if (max > 0.99f)
+ newgain = 0.99f / max;
+ else
+ newgain = 1;
+
+ if (newgain > gain)
+ newgain = (newgain + gain * 9) / 10f;
+
+ float gaindelta = (newgain - gain) / len;
+ if (mix) {
+ if (bufferR == null) {
+ for (int i = 0; i < len; i++) {
+ gain += gaindelta;
+ float bL = bufferL[i];
+ float tL = temp_bufferL[i];
+ temp_bufferL[i] = bL;
+ bufferLout[i] += tL * gain;
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ gain += gaindelta;
+ float bL = bufferL[i];
+ float bR = bufferR[i];
+ float tL = temp_bufferL[i];
+ float tR = temp_bufferR[i];
+ temp_bufferL[i] = bL;
+ temp_bufferR[i] = bR;
+ bufferLout[i] += tL * gain;
+ bufferRout[i] += tR * gain;
+ }
+ }
+
+ } else {
+ if (bufferR == null) {
+ for (int i = 0; i < len; i++) {
+ gain += gaindelta;
+ float bL = bufferL[i];
+ float tL = temp_bufferL[i];
+ temp_bufferL[i] = bL;
+ bufferLout[i] = tL * gain;
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ gain += gaindelta;
+ float bL = bufferL[i];
+ float bR = bufferR[i];
+ float tL = temp_bufferL[i];
+ float tR = temp_bufferR[i];
+ temp_bufferL[i] = bL;
+ temp_bufferR[i] = bR;
+ bufferLout[i] = tL * gain;
+ bufferRout[i] = tR * gain;
+ }
+ }
+
+ }
+ gain = newgain;
+ }
+
+ public void processControlLogic() {
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftLinearResampler.java b/src/share/classes/com/sun/media/sound/SoftLinearResampler.java
new file mode 100644
index 000000000..29f714ad4
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftLinearResampler.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A resampler that uses first-order (linear) interpolation.
+ *
+ * @author Karl Helgason
+ */
+public class SoftLinearResampler extends SoftAbstractResampler {
+
+ public int getPadding() {
+ return 2;
+ }
+
+ public void interpolate(float[] in, float[] in_offset, float in_end,
+ float[] startpitch, float pitchstep, float[] out, int[] out_offset,
+ int out_end) {
+
+ float pitch = startpitch[0];
+ float ix = in_offset[0];
+ int ox = out_offset[0];
+ float ix_end = in_end;
+ int ox_end = out_end;
+ if (pitchstep == 0f) {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float fix = ix - iix;
+ float i = in[iix];
+ out[ox++] = i + (in[iix + 1] - i) * fix;
+ ix += pitch;
+ }
+ } else {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float fix = ix - iix;
+ float i = in[iix];
+ out[ox++] = i + (in[iix + 1] - i) * fix;
+ ix += pitch;
+ pitch += pitchstep;
+ }
+ }
+ in_offset[0] = ix;
+ out_offset[0] = ox;
+ startpitch[0] = pitch;
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftLinearResampler2.java b/src/share/classes/com/sun/media/sound/SoftLinearResampler2.java
new file mode 100644
index 000000000..1838b4cfa
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftLinearResampler2.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A resampler that uses first-order (linear) interpolation.
+ *
+ * This one doesn't perform float to int casting inside the processing loop.
+ *
+ * @author Karl Helgason
+ */
+public class SoftLinearResampler2 extends SoftAbstractResampler {
+
+ public int getPadding() {
+ return 2;
+ }
+
+ public void interpolate(float[] in, float[] in_offset, float in_end,
+ float[] startpitch, float pitchstep, float[] out, int[] out_offset,
+ int out_end) {
+
+ float pitch = startpitch[0];
+ float ix = in_offset[0];
+ int ox = out_offset[0];
+ float ix_end = in_end;
+ int ox_end = out_end;
+
+ // Check if we have do anything
+ if (!(ix < ix_end && ox < ox_end))
+ return;
+
+ // 15 bit shift was choosed because
+ // it resulted in no drift between p_ix and ix.
+ int p_ix = (int) (ix * (1 << 15));
+ int p_ix_end = (int) (ix_end * (1 << 15));
+ int p_pitch = (int) (pitch * (1 << 15));
+ // Pitch needs to recalculated
+ // to ensure no drift between p_ix and ix.
+ pitch = p_pitch * (1f / (1 << 15));
+
+ if (pitchstep == 0f) {
+
+ // To reduce
+ // while (p_ix < p_ix_end && ox < ox_end)
+ // into
+ // while (ox < ox_end)
+ // We need to calculate new ox_end value.
+ int p_ix_len = p_ix_end - p_ix;
+ int p_mod = p_ix_len % p_pitch;
+ if (p_mod != 0)
+ p_ix_len += p_pitch - p_mod;
+ int ox_end2 = ox + p_ix_len / p_pitch;
+ if (ox_end2 < ox_end)
+ ox_end = ox_end2;
+
+ while (ox < ox_end) {
+ int iix = p_ix >> 15;
+ float fix = ix - iix;
+ float i = in[iix];
+ out[ox++] = i + (in[iix + 1] - i) * fix;
+ p_ix += p_pitch;
+ ix += pitch;
+ }
+
+ } else {
+
+ int p_pitchstep = (int) (pitchstep * (1 << 15));
+ pitchstep = p_pitchstep * (1f / (1 << 15));
+
+ while (p_ix < p_ix_end && ox < ox_end) {
+ int iix = p_ix >> 15;
+ float fix = ix - iix;
+ float i = in[iix];
+ out[ox++] = i + (in[iix + 1] - i) * fix;
+ ix += pitch;
+ p_ix += p_pitch;
+ pitch += pitchstep;
+ p_pitch += p_pitchstep;
+ }
+ }
+ in_offset[0] = ix;
+ out_offset[0] = ox;
+ startpitch[0] = pitch;
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftLowFrequencyOscillator.java b/src/share/classes/com/sun/media/sound/SoftLowFrequencyOscillator.java
new file mode 100644
index 000000000..adfe9e08d
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftLowFrequencyOscillator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * LFO control signal generator.
+ *
+ * @author Karl Helgason
+ */
+public class SoftLowFrequencyOscillator implements SoftProcess {
+
+ private int max_count = 10;
+ private int used_count = 0;
+ private double[][] out = new double[max_count][1];
+ private double[][] delay = new double[max_count][1];
+ private double[][] delay2 = new double[max_count][1];
+ private double[][] freq = new double[max_count][1];
+ private int[] delay_counter = new int[max_count];
+ private double[] sin_phase = new double[max_count];
+ private double[] sin_stepfreq = new double[max_count];
+ private double[] sin_step = new double[max_count];
+ private double control_time = 0;
+ private double sin_factor = 0;
+ private static double PI2 = 2.0 * Math.PI;
+
+ public void reset() {
+ for (int i = 0; i < used_count; i++) {
+ out[i][0] = 0;
+ delay[i][0] = 0;
+ delay2[i][0] = 0;
+ freq[i][0] = 0;
+ delay_counter[i] = 0;
+ sin_phase[i] = 0;
+ sin_stepfreq[i] = 0;
+ sin_step[i] = 0;
+ }
+ used_count = 0;
+ }
+
+ public void init(SoftSynthesizer synth) {
+ control_time = 1.0 / synth.getControlRate();
+ sin_factor = control_time * 2 * Math.PI;
+ for (int i = 0; i < used_count; i++) {
+ delay_counter[i] = (int)(Math.pow(2,
+ this.delay[i][0] / 1200.0) / control_time);
+ delay_counter[i] += (int)(delay2[i][0] / (control_time * 1000));
+ }
+ processControlLogic();
+ }
+
+ public void processControlLogic() {
+ for (int i = 0; i < used_count; i++) {
+ if (delay_counter[i] > 0) {
+ delay_counter[i]--;
+ out[i][0] = 0.5;
+ } else {
+ double f = freq[i][0];
+
+ if (sin_stepfreq[i] != f) {
+ sin_stepfreq[i] = f;
+ double fr = 440.0 * Math.exp(
+ (f - 6900.0) * (Math.log(2) / 1200.0));
+ sin_step[i] = fr * sin_factor;
+ }
+ /*
+ double fr = 440.0 * Math.pow(2.0,
+ (freq[i][0] - 6900.0) / 1200.0);
+ sin_phase[i] += fr * sin_factor;
+ */
+ /*
+ sin_phase[i] += sin_step[i];
+ while (sin_phase[i] > PI2)
+ sin_phase[i] -= PI2;
+ out[i][0] = 0.5 + Math.sin(sin_phase[i]) * 0.5;
+ */
+ double p = sin_phase[i];
+ p += sin_step[i];
+ while (p > PI2)
+ p -= PI2;
+ out[i][0] = 0.5 + Math.sin(p) * 0.5;
+ sin_phase[i] = p;
+
+ }
+ }
+ }
+
+ public double[] get(int instance, String name) {
+ if (instance >= used_count)
+ used_count = instance + 1;
+ if (name == null)
+ return out[instance];
+ if (name.equals("delay"))
+ return delay[instance];
+ if (name.equals("delay2"))
+ return delay2[instance];
+ if (name.equals("freq"))
+ return freq[instance];
+ return null;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMainMixer.java b/src/share/classes/com/sun/media/sound/SoftMainMixer.java
new file mode 100644
index 000000000..1f38058b0
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMainMixer.java
@@ -0,0 +1,982 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.Patch;
+import javax.sound.midi.ShortMessage;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+
+/**
+ * Software synthesizer main audio mixer.
+ *
+ * @author Karl Helgason
+ */
+public class SoftMainMixer {
+
+ public final static int CHANNEL_LEFT = 0;
+ public final static int CHANNEL_RIGHT = 1;
+ public final static int CHANNEL_EFFECT1 = 2;
+ public final static int CHANNEL_EFFECT2 = 3;
+ public final static int CHANNEL_EFFECT3 = 4;
+ public final static int CHANNEL_EFFECT4 = 5;
+ public final static int CHANNEL_LEFT_DRY = 10;
+ public final static int CHANNEL_RIGHT_DRY = 11;
+ public final static int CHANNEL_SCRATCH1 = 12;
+ public final static int CHANNEL_SCRATCH2 = 13;
+ public final static int CHANNEL_CHANNELMIXER_LEFT = 14;
+ public final static int CHANNEL_CHANNELMIXER_RIGHT = 15;
+ protected boolean active_sensing_on = false;
+ private long msec_last_activity = -1;
+ private boolean pusher_silent = false;
+ private int pusher_silent_count = 0;
+ private long msec_pos = 0;
+ protected boolean readfully = true;
+ private Object control_mutex;
+ private SoftSynthesizer synth;
+ private int nrofchannels = 2;
+ private SoftVoice[] voicestatus = null;
+ private SoftAudioBuffer[] buffers;
+ private SoftReverb reverb;
+ private SoftAudioProcessor chorus;
+ private SoftAudioProcessor agc;
+ private long msec_buffer_len = 0;
+ protected TreeMap<Long, Object> midimessages = new TreeMap<Long, Object>();
+ double last_volume_left = 1.0;
+ double last_volume_right = 1.0;
+ private double[] co_master_balance = new double[1];
+ private double[] co_master_volume = new double[1];
+ private double[] co_master_coarse_tuning = new double[1];
+ private double[] co_master_fine_tuning = new double[1];
+ private AudioInputStream ais;
+ private Set<ModelChannelMixer> registeredMixers = null;
+ private Set<ModelChannelMixer> stoppedMixers = null;
+ private ModelChannelMixer[] cur_registeredMixers = null;
+ protected SoftControl co_master = new SoftControl() {
+
+ double[] balance = co_master_balance;
+ double[] volume = co_master_volume;
+ double[] coarse_tuning = co_master_coarse_tuning;
+ double[] fine_tuning = co_master_fine_tuning;
+
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ if (name.equals("balance"))
+ return balance;
+ if (name.equals("volume"))
+ return volume;
+ if (name.equals("coarse_tuning"))
+ return coarse_tuning;
+ if (name.equals("fine_tuning"))
+ return fine_tuning;
+ return null;
+ }
+ };
+
+ private void processSystemExclusiveMessage(byte[] data) {
+ synchronized (synth.control_mutex) {
+ activity();
+
+ // Universal Non-Real-Time SysEx
+ if ((data[1] & 0xFF) == 0x7E) {
+ int deviceID = data[2] & 0xFF;
+ if (deviceID == 0x7F || deviceID == synth.getDeviceID()) {
+ int subid1 = data[3] & 0xFF;
+ int subid2;
+ switch (subid1) {
+ case 0x08: // MIDI Tuning Standard
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // BULK TUNING DUMP
+ {
+ // http://www.midi.org/about-midi/tuning.shtml
+ SoftTuning tuning = synth.getTuning(new Patch(0,
+ data[5] & 0xFF));
+ tuning.load(data);
+ break;
+ }
+ case 0x04: // KEY-BASED TUNING DUMP
+ case 0x05: // SCALE/OCTAVE TUNING DUMP, 1 byte format
+ case 0x06: // SCALE/OCTAVE TUNING DUMP, 2 byte format
+ case 0x07: // SINGLE NOTE TUNING CHANGE (NON REAL-TIME)
+ // (BANK)
+ {
+ // http://www.midi.org/about-midi/tuning_extens.shtml
+ SoftTuning tuning = synth.getTuning(new Patch(
+ data[5] & 0xFF, data[6] & 0xFF));
+ tuning.load(data);
+ break;
+ }
+ case 0x08: // scale/octave tuning 1-byte form (Non
+ // Real-Time)
+ case 0x09: // scale/octave tuning 2-byte form (Non
+ // Real-Time)
+ {
+ // http://www.midi.org/about-midi/tuning-scale.shtml
+ SoftTuning tuning = new SoftTuning(data);
+ int channelmask = (data[5] & 0xFF) * 16384
+ + (data[6] & 0xFF) * 128 + (data[7] & 0xFF);
+ SoftChannel[] channels = synth.channels;
+ for (int i = 0; i < channels.length; i++)
+ if ((channelmask & (1 << i)) != 0)
+ channels[i].tuning = tuning;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case 0x09: // General Midi Message
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // General Midi 1 On
+ synth.setGeneralMidiMode(1);
+ reset();
+ break;
+ case 0x02: // General Midi Off
+ synth.setGeneralMidiMode(0);
+ reset();
+ break;
+ case 0x03: // General MidI Level 2 On
+ synth.setGeneralMidiMode(2);
+ reset();
+ break;
+ default:
+ break;
+ }
+ break;
+ case 0x0A: // DLS Message
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // DLS On
+ if (synth.getGeneralMidiMode() == 0)
+ synth.setGeneralMidiMode(1);
+ synth.voice_allocation_mode = 1;
+ reset();
+ break;
+ case 0x02: // DLS Off
+ synth.setGeneralMidiMode(0);
+ synth.voice_allocation_mode = 0;
+ reset();
+ break;
+ case 0x03: // DLS Static Voice Allocation Off
+ synth.voice_allocation_mode = 0;
+ break;
+ case 0x04: // DLS Static Voice Allocation On
+ synth.voice_allocation_mode = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // Universal Real-Time SysEx
+ if ((data[1] & 0xFF) == 0x7F) {
+ int deviceID = data[2] & 0xFF;
+ if (deviceID == 0x7F || deviceID == synth.getDeviceID()) {
+ int subid1 = data[3] & 0xFF;
+ int subid2;
+ switch (subid1) {
+ case 0x04: // Device Control
+
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // Master Volume
+ case 0x02: // Master Balane
+ case 0x03: // Master fine tuning
+ case 0x04: // Master coarse tuning
+ int val = (data[5] & 0x7F)
+ + ((data[6] & 0x7F) * 128);
+ if (subid2 == 0x01)
+ setVolume(val);
+ else if (subid2 == 0x02)
+ setBalance(val);
+ else if (subid2 == 0x03)
+ setFineTuning(val);
+ else if (subid2 == 0x04)
+ setCoarseTuning(val);
+ break;
+ case 0x05: // Global Parameter Control
+ int ix = 5;
+ int slotPathLen = (data[ix++] & 0xFF);
+ int paramWidth = (data[ix++] & 0xFF);
+ int valueWidth = (data[ix++] & 0xFF);
+ int[] slotPath = new int[slotPathLen];
+ for (int i = 0; i < slotPathLen; i++) {
+ int msb = (data[ix++] & 0xFF);
+ int lsb = (data[ix++] & 0xFF);
+ slotPath[i] = msb * 128 + lsb;
+ }
+ int paramCount = (data.length - 1 - ix)
+ / (paramWidth + valueWidth);
+ long[] params = new long[paramCount];
+ long[] values = new long[paramCount];
+ for (int i = 0; i < paramCount; i++) {
+ values[i] = 0;
+ for (int j = 0; j < paramWidth; j++)
+ params[i] = params[i] * 128
+ + (data[ix++] & 0xFF);
+ for (int j = 0; j < valueWidth; j++)
+ values[i] = values[i] * 128
+ + (data[ix++] & 0xFF);
+
+ }
+ globalParameterControlChange(slotPath, params, values);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 0x08: // MIDI Tuning Standard
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x02: // SINGLE NOTE TUNING CHANGE (REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning.shtml
+ SoftTuning tuning = synth.getTuning(new Patch(0,
+ data[5] & 0xFF));
+ tuning.load(data);
+ SoftVoice[] voices = synth.getVoices();
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ if (voices[i].tuning == tuning)
+ voices[i].updateTuning(tuning);
+ break;
+ }
+ case 0x07: // SINGLE NOTE TUNING CHANGE (REAL-TIME)
+ // (BANK)
+ {
+ // http://www.midi.org/about-midi/tuning_extens.shtml
+ SoftTuning tuning = synth.getTuning(new Patch(
+ data[5] & 0xFF, data[6] & 0xFF));
+ tuning.load(data);
+ SoftVoice[] voices = synth.getVoices();
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ if (voices[i].tuning == tuning)
+ voices[i].updateTuning(tuning);
+ break;
+ }
+ case 0x08: // scale/octave tuning 1-byte form
+ //(Real-Time)
+ case 0x09: // scale/octave tuning 2-byte form
+ // (Real-Time)
+ {
+ // http://www.midi.org/about-midi/tuning-scale.shtml
+ SoftTuning tuning = new SoftTuning(data);
+ int channelmask = (data[5] & 0xFF) * 16384
+ + (data[6] & 0xFF) * 128 + (data[7] & 0xFF);
+ SoftChannel[] channels = synth.channels;
+ for (int i = 0; i < channels.length; i++)
+ if ((channelmask & (1 << i)) != 0)
+ channels[i].tuning = tuning;
+ SoftVoice[] voices = synth.getVoices();
+ for (int i = 0; i < voices.length; i++)
+ if (voices[i].active)
+ if ((channelmask & (1 << (voices[i].channel))) != 0)
+ voices[i].updateTuning(tuning);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case 0x09: // Control Destination Settings
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // Channel Pressure
+ {
+ int[] destinations = new int[(data.length - 7) / 2];
+ int[] ranges = new int[(data.length - 7) / 2];
+ int ix = 0;
+ for (int j = 6; j < data.length - 1; j += 2) {
+ destinations[ix] = data[j] & 0xFF;
+ ranges[ix] = data[j + 1] & 0xFF;
+ ix++;
+ }
+ int channel = data[5] & 0xFF;
+ SoftChannel softchannel = synth.channels[channel];
+ softchannel.mapChannelPressureToDestination(
+ destinations, ranges);
+ break;
+ }
+ case 0x02: // Poly Pressure
+ {
+ int[] destinations = new int[(data.length - 7) / 2];
+ int[] ranges = new int[(data.length - 7) / 2];
+ int ix = 0;
+ for (int j = 6; j < data.length - 1; j += 2) {
+ destinations[ix] = data[j] & 0xFF;
+ ranges[ix] = data[j + 1] & 0xFF;
+ ix++;
+ }
+ int channel = data[5] & 0xFF;
+ SoftChannel softchannel = synth.channels[channel];
+ softchannel.mapPolyPressureToDestination(
+ destinations, ranges);
+ break;
+ }
+ case 0x03: // Control Change
+ {
+ int[] destinations = new int[(data.length - 7) / 2];
+ int[] ranges = new int[(data.length - 7) / 2];
+ int ix = 0;
+ for (int j = 7; j < data.length - 1; j += 2) {
+ destinations[ix] = data[j] & 0xFF;
+ ranges[ix] = data[j + 1] & 0xFF;
+ ix++;
+ }
+ int channel = data[5] & 0xFF;
+ SoftChannel softchannel = synth.channels[channel];
+ int control = data[6] & 0xFF;
+ softchannel.mapControlToDestination(control,
+ destinations, ranges);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case 0x0A: // Key Based Instrument Control
+ {
+ subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // Basic Message
+ int channel = data[5] & 0xFF;
+ int keynumber = data[6] & 0xFF;
+ SoftChannel softchannel = synth.channels[channel];
+ for (int j = 7; j < data.length - 1; j += 2) {
+ int controlnumber = data[j] & 0xFF;
+ int controlvalue = data[j + 1] & 0xFF;
+ softchannel.controlChangePerNote(keynumber,
+ controlnumber, controlvalue);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ }
+ }
+
+ private void processMessages(long timeStamp) {
+ Iterator<Entry<Long, Object>> iter = midimessages.entrySet().iterator();
+ while (iter.hasNext()) {
+ Entry<Long, Object> entry = iter.next();
+ if (entry.getKey() > (timeStamp + 100))
+ return;
+ processMessage(entry.getValue());
+ iter.remove();
+ }
+ }
+
+ protected void processAudioBuffers() {
+ for (int i = 0; i < buffers.length; i++) {
+ buffers[i].clear();
+ }
+
+ double volume_left;
+ double volume_right;
+
+ ModelChannelMixer[] act_registeredMixers;
+
+ // perform control logic
+ synchronized (control_mutex) {
+
+ processMessages(msec_pos);
+
+ if (active_sensing_on) {
+ // Active Sensing
+ // if no message occurs for max 1000 ms
+ // then do AllSoundOff on all channels
+ if ((msec_pos - msec_last_activity) > 1000000) {
+ active_sensing_on = false;
+ for (SoftChannel c : synth.channels)
+ c.allSoundOff();
+ }
+
+ }
+
+ for (int i = 0; i < voicestatus.length; i++)
+ if (voicestatus[i].active)
+ voicestatus[i].processControlLogic();
+ msec_pos += msec_buffer_len;
+
+ double volume = co_master_volume[0];
+ volume_left = volume;
+ volume_right = volume;
+
+ double balance = co_master_balance[0];
+ if (balance > 0.5)
+ volume_left *= (1 - balance) * 2;
+ else
+ volume_right *= balance * 2;
+
+ chorus.processControlLogic();
+ reverb.processControlLogic();
+ agc.processControlLogic();
+
+ if (cur_registeredMixers == null) {
+ if (registeredMixers != null) {
+ cur_registeredMixers =
+ new ModelChannelMixer[registeredMixers.size()];
+ registeredMixers.toArray(cur_registeredMixers);
+ }
+ }
+
+ act_registeredMixers = cur_registeredMixers;
+ if (act_registeredMixers != null)
+ if (act_registeredMixers.length == 0)
+ act_registeredMixers = null;
+
+ }
+
+ if (act_registeredMixers != null) {
+
+ // Reroute default left,right output
+ // to channelmixer left,right input/output
+ SoftAudioBuffer leftbak = buffers[CHANNEL_LEFT];
+ SoftAudioBuffer rightbak = buffers[CHANNEL_RIGHT];
+ buffers[CHANNEL_LEFT] = buffers[CHANNEL_CHANNELMIXER_LEFT];
+ buffers[CHANNEL_RIGHT] = buffers[CHANNEL_CHANNELMIXER_LEFT];
+
+ int bufferlen = buffers[CHANNEL_LEFT].getSize();
+
+ float[][] cbuffer = new float[nrofchannels][];
+ cbuffer[0] = buffers[CHANNEL_LEFT].array();
+ if (nrofchannels != 1)
+ cbuffer[1] = buffers[CHANNEL_RIGHT].array();
+
+ float[][] obuffer = new float[nrofchannels][];
+ obuffer[0] = leftbak.array();
+ if (nrofchannels != 1)
+ obuffer[1] = rightbak.array();
+
+ for (ModelChannelMixer cmixer : act_registeredMixers) {
+ for (int i = 0; i < cbuffer.length; i++)
+ Arrays.fill(cbuffer[i], 0);
+ boolean hasactivevoices = false;
+ for (int i = 0; i < voicestatus.length; i++)
+ if (voicestatus[i].active)
+ if (voicestatus[i].channelmixer == cmixer) {
+ voicestatus[i].processAudioLogic(buffers);
+ hasactivevoices = true;
+ }
+ if (!cmixer.process(cbuffer, 0, bufferlen)) {
+ synchronized (control_mutex) {
+ registeredMixers.remove(cmixer);
+ cur_registeredMixers = null;
+ }
+ }
+
+ for (int i = 0; i < cbuffer.length; i++) {
+ float[] cbuff = cbuffer[i];
+ float[] obuff = obuffer[i];
+ for (int j = 0; j < bufferlen; j++)
+ obuff[j] += cbuff[j];
+ }
+
+ if (!hasactivevoices) {
+ synchronized (control_mutex) {
+ if (stoppedMixers != null) {
+ if (stoppedMixers.contains(cmixer)) {
+ stoppedMixers.remove(cmixer);
+ cmixer.stop();
+ }
+ }
+ }
+ }
+
+ }
+
+ buffers[CHANNEL_LEFT] = leftbak;
+ buffers[CHANNEL_RIGHT] = rightbak;
+
+ }
+
+ for (int i = 0; i < voicestatus.length; i++)
+ if (voicestatus[i].active)
+ if (voicestatus[i].channelmixer == null)
+ voicestatus[i].processAudioLogic(buffers);
+
+ // Run effects
+ if (synth.chorus_on)
+ chorus.processAudio();
+
+ if (synth.reverb_on)
+ reverb.processAudio();
+
+ if (nrofchannels == 1)
+ volume_left = (volume_left + volume_right) / 2;
+
+ // Set Volume / Balance
+ if (last_volume_left != volume_left || last_volume_right != volume_right) {
+ float[] left = buffers[CHANNEL_LEFT].array();
+ float[] right = buffers[CHANNEL_RIGHT].array();
+ int bufferlen = buffers[CHANNEL_LEFT].getSize();
+
+ float amp;
+ float amp_delta;
+ amp = (float)(last_volume_left * last_volume_left);
+ amp_delta = (float)((volume_left * volume_left - amp) / bufferlen);
+ for (int i = 0; i < bufferlen; i++) {
+ amp += amp_delta;
+ left[i] *= amp;
+ }
+ if (nrofchannels != 1) {
+ amp = (float)(last_volume_right * last_volume_right);
+ amp_delta = (float)((volume_right*volume_right - amp) / bufferlen);
+ for (int i = 0; i < bufferlen; i++) {
+ amp += amp_delta;
+ right[i] *= volume_right;
+ }
+ }
+ last_volume_left = volume_left;
+ last_volume_right = volume_right;
+
+ } else {
+ if (volume_left != 1.0 || volume_right != 1.0) {
+ float[] left = buffers[CHANNEL_LEFT].array();
+ float[] right = buffers[CHANNEL_RIGHT].array();
+ int bufferlen = buffers[CHANNEL_LEFT].getSize();
+ float amp;
+ amp = (float) (volume_left * volume_left);
+ for (int i = 0; i < bufferlen; i++)
+ left[i] *= amp;
+ if (nrofchannels != 1) {
+ amp = (float)(volume_right * volume_right);
+ for (int i = 0; i < bufferlen; i++)
+ right[i] *= amp;
+ }
+
+ }
+ }
+
+ if(buffers[CHANNEL_LEFT].isSilent()
+ && buffers[CHANNEL_RIGHT].isSilent())
+ {
+ pusher_silent_count++;
+ if(pusher_silent_count > 5)
+ {
+ pusher_silent_count = 0;
+ synchronized (control_mutex) {
+ pusher_silent = true;
+ if(synth.weakstream != null)
+ synth.weakstream.setInputStream(null);
+ }
+ }
+ }
+ else
+ pusher_silent_count = 0;
+
+ if (synth.agc_on)
+ agc.processAudio();
+
+ }
+
+ // Must only we called within control_mutex synchronization
+ public void activity()
+ {
+ msec_last_activity = msec_pos;
+ if(pusher_silent)
+ {
+ pusher_silent = false;
+ if(synth.weakstream != null)
+ synth.weakstream.setInputStream(ais);
+ }
+ }
+
+ public void stopMixer(ModelChannelMixer mixer) {
+ if (stoppedMixers == null)
+ stoppedMixers = new HashSet<ModelChannelMixer>();
+ stoppedMixers.add(mixer);
+ }
+
+ public void registerMixer(ModelChannelMixer mixer) {
+ if (registeredMixers == null)
+ registeredMixers = new HashSet<ModelChannelMixer>();
+ registeredMixers.add(mixer);
+ cur_registeredMixers = null;
+ }
+
+ public SoftMainMixer(SoftSynthesizer synth) {
+ this.synth = synth;
+
+ msec_pos = 0;
+
+ co_master_balance[0] = 0.5;
+ co_master_volume[0] = 1;
+ co_master_coarse_tuning[0] = 0.5;
+ co_master_fine_tuning[0] = 0.5;
+
+ msec_buffer_len = (long) (1000000.0 / synth.getControlRate());
+
+ nrofchannels = synth.getFormat().getChannels();
+
+ int buffersize = (int) (synth.getFormat().getSampleRate()
+ / synth.getControlRate());
+
+ control_mutex = synth.control_mutex;
+ buffers = new SoftAudioBuffer[16];
+ for (int i = 0; i < buffers.length; i++) {
+ buffers[i] = new SoftAudioBuffer(buffersize, synth.getFormat());
+ }
+ voicestatus = synth.getVoices();
+
+ reverb = new SoftReverb();
+ chorus = new SoftChorus();
+ agc = new SoftLimiter();
+
+ float samplerate = synth.getFormat().getSampleRate();
+ float controlrate = synth.getControlRate();
+ reverb.init(samplerate, controlrate);
+ chorus.init(samplerate, controlrate);
+ agc.init(samplerate, controlrate);
+
+ reverb.setLightMode(synth.reverb_light);
+
+ reverb.setMixMode(true);
+ chorus.setMixMode(true);
+ agc.setMixMode(false);
+
+ chorus.setInput(0, buffers[CHANNEL_EFFECT2]);
+ chorus.setOutput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ chorus.setOutput(1, buffers[CHANNEL_RIGHT]);
+ chorus.setOutput(2, buffers[CHANNEL_EFFECT1]);
+
+ reverb.setInput(0, buffers[CHANNEL_EFFECT1]);
+ reverb.setOutput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ reverb.setOutput(1, buffers[CHANNEL_RIGHT]);
+
+ agc.setInput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ agc.setInput(1, buffers[CHANNEL_RIGHT]);
+ agc.setOutput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ agc.setOutput(1, buffers[CHANNEL_RIGHT]);
+
+ InputStream in = new InputStream() {
+
+ private SoftAudioBuffer[] buffers = SoftMainMixer.this.buffers;
+ private int nrofchannels
+ = SoftMainMixer.this.synth.getFormat().getChannels();
+ private int buffersize = buffers[0].getSize();
+ private byte[] bbuffer = new byte[buffersize
+ * (SoftMainMixer.this.synth.getFormat()
+ .getSampleSizeInBits() / 8)
+ * nrofchannels];
+ private int bbuffer_pos = 0;
+ private byte[] single = new byte[1];
+
+ public void fillBuffer() {
+ /*
+ boolean pusher_silent2;
+ synchronized (control_mutex) {
+ pusher_silent2 = pusher_silent;
+ }
+ if(!pusher_silent2)*/
+ processAudioBuffers();
+ for (int i = 0; i < nrofchannels; i++)
+ buffers[i].get(bbuffer, i);
+ bbuffer_pos = 0;
+ }
+
+ public int read(byte[] b, int off, int len) {
+ int bbuffer_len = bbuffer.length;
+ int offlen = off + len;
+ int orgoff = off;
+ byte[] bbuffer = this.bbuffer;
+ while (off < offlen) {
+ if (available() == 0)
+ fillBuffer();
+ else {
+ int bbuffer_pos = this.bbuffer_pos;
+ while (off < offlen && bbuffer_pos < bbuffer_len)
+ b[off++] = bbuffer[bbuffer_pos++];
+ this.bbuffer_pos = bbuffer_pos;
+ if (!readfully)
+ return off - orgoff;
+ }
+ }
+ return len;
+ }
+
+ public int read() throws IOException {
+ int ret = read(single);
+ if (ret == -1)
+ return -1;
+ return single[0] & 0xFF;
+ }
+
+ public int available() {
+ return bbuffer.length - bbuffer_pos;
+ }
+
+ public void close() {
+ SoftMainMixer.this.synth.close();
+ }
+ };
+
+ ais = new AudioInputStream(in, synth.getFormat(), AudioSystem.NOT_SPECIFIED);
+
+ }
+
+ public AudioInputStream getInputStream() {
+ return ais;
+ }
+
+ public void reset() {
+
+ SoftChannel[] channels = synth.channels;
+ for (int i = 0; i < channels.length; i++) {
+ channels[i].allSoundOff();
+ channels[i].resetAllControllers(true);
+
+ if (synth.getGeneralMidiMode() == 2) {
+ if (i == 9)
+ channels[i].programChange(0, 0x78 * 128);
+ else
+ channels[i].programChange(0, 0x79 * 128);
+ } else
+ channels[i].programChange(0, 0);
+ }
+ setVolume(0x7F * 128 + 0x7F);
+ setBalance(0x40 * 128 + 0x00);
+ setCoarseTuning(0x40 * 128 + 0x00);
+ setFineTuning(0x40 * 128 + 0x00);
+ // Reset Reverb
+ globalParameterControlChange(
+ new int[]{0x01 * 128 + 0x01}, new long[]{0}, new long[]{4});
+ // Reset Chorus
+ globalParameterControlChange(
+ new int[]{0x01 * 128 + 0x02}, new long[]{0}, new long[]{2});
+ }
+
+ public void setVolume(int value) {
+ synchronized (control_mutex) {
+ co_master_volume[0] = value / 16384.0;
+ }
+ }
+
+ public void setBalance(int value) {
+ synchronized (control_mutex) {
+ co_master_balance[0] = value / 16384.0;
+ }
+ }
+
+ public void setFineTuning(int value) {
+ synchronized (control_mutex) {
+ co_master_fine_tuning[0] = value / 16384.0;
+ }
+ }
+
+ public void setCoarseTuning(int value) {
+ synchronized (control_mutex) {
+ co_master_coarse_tuning[0] = value / 16384.0;
+ }
+ }
+
+ public int getVolume() {
+ synchronized (control_mutex) {
+ return (int) (co_master_volume[0] * 16384.0);
+ }
+ }
+
+ public int getBalance() {
+ synchronized (control_mutex) {
+ return (int) (co_master_balance[0] * 16384.0);
+ }
+ }
+
+ public int getFineTuning() {
+ synchronized (control_mutex) {
+ return (int) (co_master_fine_tuning[0] * 16384.0);
+ }
+ }
+
+ public int getCoarseTuning() {
+ synchronized (control_mutex) {
+ return (int) (co_master_coarse_tuning[0] * 16384.0);
+ }
+ }
+
+ public void globalParameterControlChange(int[] slothpath, long[] params,
+ long[] paramsvalue) {
+ if (slothpath.length == 0)
+ return;
+
+ synchronized (control_mutex) {
+
+ // slothpath: 01xx are reserved only for GM2
+
+ if (slothpath[0] == 0x01 * 128 + 0x01) {
+ for (int i = 0; i < paramsvalue.length; i++) {
+ reverb.globalParameterControlChange(slothpath, params[i],
+ paramsvalue[i]);
+ }
+ }
+ if (slothpath[0] == 0x01 * 128 + 0x02) {
+ for (int i = 0; i < paramsvalue.length; i++) {
+ chorus.globalParameterControlChange(slothpath, params[i],
+ paramsvalue[i]);
+ }
+
+ }
+
+ }
+ }
+
+ public void processMessage(Object object) {
+ if (object instanceof byte[])
+ processMessage((byte[]) object);
+ if (object instanceof MidiMessage)
+ processMessage((MidiMessage)object);
+ }
+
+ public void processMessage(MidiMessage message) {
+ if (message instanceof ShortMessage) {
+ ShortMessage sms = (ShortMessage)message;
+ processMessage(sms.getChannel(), sms.getCommand(),
+ sms.getData1(), sms.getData2());
+ return;
+ }
+ processMessage(message.getMessage());
+ }
+
+ public void processMessage(byte[] data) {
+ int status = 0;
+ if (data.length > 0)
+ status = data[0] & 0xFF;
+
+ if (status == 0xF0) {
+ processSystemExclusiveMessage(data);
+ return;
+ }
+
+ int cmd = (status & 0xF0);
+ int ch = (status & 0x0F);
+
+ int data1;
+ int data2;
+ if (data.length > 1)
+ data1 = data[1] & 0xFF;
+ else
+ data1 = 0;
+ if (data.length > 2)
+ data2 = data[2] & 0xFF;
+ else
+ data2 = 0;
+
+ processMessage(ch, cmd, data1, data2);
+
+ }
+
+ public void processMessage(int ch, int cmd, int data1, int data2) {
+ synchronized (synth.control_mutex) {
+ activity();
+ }
+
+ if (cmd == 0xF0) {
+ int status = cmd | ch;
+ switch (status) {
+ case ShortMessage.ACTIVE_SENSING:
+ synchronized (synth.control_mutex) {
+ active_sensing_on = true;
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ SoftChannel[] channels = synth.channels;
+ if (ch >= channels.length)
+ return;
+ SoftChannel softchannel = channels[ch];
+
+ switch (cmd) {
+ case ShortMessage.NOTE_ON:
+ softchannel.noteOn(data1, data2);
+ break;
+ case ShortMessage.NOTE_OFF:
+ softchannel.noteOff(data1, data2);
+ break;
+ case ShortMessage.POLY_PRESSURE:
+ softchannel.setPolyPressure(data1, data2);
+ break;
+ case ShortMessage.CONTROL_CHANGE:
+ softchannel.controlChange(data1, data2);
+ break;
+ case ShortMessage.PROGRAM_CHANGE:
+ softchannel.programChange(data1);
+ break;
+ case ShortMessage.CHANNEL_PRESSURE:
+ softchannel.setChannelPressure(data1);
+ break;
+ case ShortMessage.PITCH_BEND:
+ softchannel.setPitchBend(data1 + data2 * 128);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ public long getMicrosecondPosition() {
+ return msec_pos;
+ }
+
+ public void close() {
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java b/src/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java
new file mode 100644
index 000000000..caaa0bb98
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MetaMessage;
+import javax.sound.midi.MidiEvent;
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.Track;
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.sound.sampled.AudioFileFormat.Type;
+import javax.sound.sampled.spi.AudioFileReader;
+
+/**
+ * MIDI File Audio Renderer/Reader
+ *
+ * @author Karl Helgason
+ */
+public class SoftMidiAudioFileReader extends AudioFileReader {
+
+ public static final Type MIDI = new Type("MIDI", "mid");
+ private static AudioFormat format = new AudioFormat(44100, 16, 2, true, false);
+
+ public AudioFileFormat getAudioFileFormat(Sequence seq)
+ throws UnsupportedAudioFileException, IOException {
+
+ long totallen = seq.getMicrosecondLength() / 1000000;
+ long len = (long) (format.getFrameRate() * (totallen + 4));
+ return new AudioFileFormat(MIDI, format, (int) len);
+ }
+
+ public AudioInputStream getAudioInputStream(Sequence seq)
+ throws UnsupportedAudioFileException, IOException {
+ AudioSynthesizer synth = (AudioSynthesizer) new SoftSynthesizer();
+ AudioInputStream stream;
+ Receiver recv;
+ try {
+ stream = synth.openStream(format, null);
+ recv = synth.getReceiver();
+ } catch (MidiUnavailableException e) {
+ throw new IOException(e.toString());
+ }
+ float divtype = seq.getDivisionType();
+ Track[] tracks = seq.getTracks();
+ int[] trackspos = new int[tracks.length];
+ int mpq = 500000;
+ int seqres = seq.getResolution();
+ long lasttick = 0;
+ long curtime = 0;
+ while (true) {
+ MidiEvent selevent = null;
+ int seltrack = -1;
+ for (int i = 0; i < tracks.length; i++) {
+ int trackpos = trackspos[i];
+ Track track = tracks[i];
+ if (trackpos < track.size()) {
+ MidiEvent event = track.get(trackpos);
+ if (selevent == null || event.getTick() < selevent.getTick()) {
+ selevent = event;
+ seltrack = i;
+ }
+ }
+ }
+ if (seltrack == -1)
+ break;
+ trackspos[seltrack]++;
+ long tick = selevent.getTick();
+ if (divtype == Sequence.PPQ)
+ curtime += ((tick - lasttick) * mpq) / seqres;
+ else
+ curtime = (long) ((tick * 1000000.0 * divtype) / seqres);
+ lasttick = tick;
+ MidiMessage msg = selevent.getMessage();
+ if (msg instanceof MetaMessage) {
+ if (divtype == Sequence.PPQ) {
+ if (((MetaMessage) msg).getType() == 0x51) {
+ byte[] data = ((MetaMessage) msg).getData();
+ mpq = ((data[0] & 0xff) << 16)
+ | ((data[1] & 0xff) << 8) | (data[2] & 0xff);
+ }
+ }
+ } else {
+ recv.send(msg, curtime);
+ }
+ }
+
+ long totallen = curtime / 1000000;
+ long len = (long) (stream.getFormat().getFrameRate() * (totallen + 4));
+ stream = new AudioInputStream(stream, stream.getFormat(), len);
+ return stream;
+ }
+
+ public AudioInputStream getAudioInputStream(InputStream inputstream)
+ throws UnsupportedAudioFileException, IOException {
+
+ inputstream.mark(200);
+ Sequence seq;
+ try {
+ seq = MidiSystem.getSequence(inputstream);
+ } catch (InvalidMidiDataException e) {
+ inputstream.reset();
+ throw new UnsupportedAudioFileException();
+ } catch (IOException e) {
+ inputstream.reset();
+ throw new UnsupportedAudioFileException();
+ }
+ return getAudioInputStream(seq);
+ }
+
+ public AudioFileFormat getAudioFileFormat(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ Sequence seq;
+ try {
+ seq = MidiSystem.getSequence(url);
+ } catch (InvalidMidiDataException e) {
+ throw new UnsupportedAudioFileException();
+ } catch (IOException e) {
+ throw new UnsupportedAudioFileException();
+ }
+ return getAudioFileFormat(seq);
+ }
+
+ public AudioFileFormat getAudioFileFormat(File file)
+ throws UnsupportedAudioFileException, IOException {
+ Sequence seq;
+ try {
+ seq = MidiSystem.getSequence(file);
+ } catch (InvalidMidiDataException e) {
+ throw new UnsupportedAudioFileException();
+ } catch (IOException e) {
+ throw new UnsupportedAudioFileException();
+ }
+ return getAudioFileFormat(seq);
+ }
+
+ public AudioInputStream getAudioInputStream(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ Sequence seq;
+ try {
+ seq = MidiSystem.getSequence(url);
+ } catch (InvalidMidiDataException e) {
+ throw new UnsupportedAudioFileException();
+ } catch (IOException e) {
+ throw new UnsupportedAudioFileException();
+ }
+ return getAudioInputStream(seq);
+ }
+
+ public AudioInputStream getAudioInputStream(File file)
+ throws UnsupportedAudioFileException, IOException {
+ if (!file.getName().toLowerCase().endsWith(".mid"))
+ throw new UnsupportedAudioFileException();
+ Sequence seq;
+ try {
+ seq = MidiSystem.getSequence(file);
+ } catch (InvalidMidiDataException e) {
+ throw new UnsupportedAudioFileException();
+ } catch (IOException e) {
+ throw new UnsupportedAudioFileException();
+ }
+ return getAudioInputStream(seq);
+ }
+
+ public AudioFileFormat getAudioFileFormat(InputStream inputstream)
+ throws UnsupportedAudioFileException, IOException {
+
+ inputstream.mark(200);
+ Sequence seq;
+ try {
+ seq = MidiSystem.getSequence(inputstream);
+ } catch (InvalidMidiDataException e) {
+ inputstream.reset();
+ throw new UnsupportedAudioFileException();
+ } catch (IOException e) {
+ inputstream.reset();
+ throw new UnsupportedAudioFileException();
+ }
+ return getAudioFileFormat(seq);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMixingClip.java b/src/share/classes/com/sun/media/sound/SoftMixingClip.java
new file mode 100644
index 000000000..5db16d917
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMixingClip.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineUnavailableException;
+
+/**
+ * Clip implemention for the SoftMixingMixer.
+ *
+ * @author Karl Helgason
+ */
+public class SoftMixingClip extends SoftMixingDataLine implements Clip {
+
+ private AudioFormat format;
+
+ private int framesize;
+
+ private byte[] data;
+
+ private InputStream datastream = new InputStream() {
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ int ret = read(b);
+ if (ret < 0)
+ return ret;
+ return b[0] & 0xFF;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+
+ if (_loopcount != 0) {
+ int bloopend = _loopend * framesize;
+ int bloopstart = _loopstart * framesize;
+ int pos = _frameposition * framesize;
+
+ if (pos + len >= bloopend)
+ if (pos < bloopend) {
+ int offend = off + len;
+ int o = off;
+ while (off != offend) {
+ if (pos == bloopend) {
+ if (_loopcount == 0)
+ break;
+ pos = bloopstart;
+ if (_loopcount != LOOP_CONTINUOUSLY)
+ _loopcount--;
+ }
+ len = offend - off;
+ int left = bloopend - pos;
+ if (len > left)
+ len = left;
+ System.arraycopy(data, pos, b, off, len);
+ off += len;
+ }
+ if (_loopcount == 0) {
+ len = offend - off;
+ int left = bloopend - pos;
+ if (len > left)
+ len = left;
+ System.arraycopy(data, pos, b, off, len);
+ off += len;
+ }
+ _frameposition = pos / framesize;
+ return o - off;
+ }
+ }
+
+ int pos = _frameposition * framesize;
+ int left = bufferSize - pos;
+ if (left == 0)
+ return -1;
+ if (len > left)
+ len = left;
+ System.arraycopy(data, pos, b, off, len);
+ _frameposition += len / framesize;
+ return len;
+ }
+
+ };
+
+ private int offset;
+
+ private int bufferSize;
+
+ private float[] readbuffer;
+
+ private boolean open = false;
+
+ private AudioFormat outputformat;
+
+ private int out_nrofchannels;
+
+ private int in_nrofchannels;
+
+ private int frameposition = 0;
+
+ private boolean frameposition_sg = false;
+
+ private boolean active_sg = false;
+
+ private int loopstart = 0;
+
+ private int loopend = -1;
+
+ private boolean active = false;
+
+ private int loopcount = 0;
+
+ private boolean _active = false;
+
+ private int _frameposition = 0;
+
+ private boolean loop_sg = false;
+
+ private int _loopcount = 0;
+
+ private int _loopstart = 0;
+
+ private int _loopend = -1;
+
+ private float _rightgain;
+
+ private float _leftgain;
+
+ private float _eff1gain;
+
+ private float _eff2gain;
+
+ private AudioFloatInputStream afis;
+
+ protected SoftMixingClip(SoftMixingMixer mixer, DataLine.Info info) {
+ super(mixer, info);
+ }
+
+ protected void processControlLogic() {
+
+ _rightgain = rightgain;
+ _leftgain = leftgain;
+ _eff1gain = eff1gain;
+ _eff2gain = eff2gain;
+
+ if (active_sg) {
+ _active = active;
+ active_sg = false;
+ } else {
+ active = _active;
+ }
+
+ if (frameposition_sg) {
+ _frameposition = frameposition;
+ frameposition_sg = false;
+ afis = null;
+ } else {
+ frameposition = _frameposition;
+ }
+ if (loop_sg) {
+ _loopcount = loopcount;
+ _loopstart = loopstart;
+ _loopend = loopend;
+ }
+
+ if (afis == null) {
+ afis = AudioFloatInputStream.getInputStream(new AudioInputStream(
+ datastream, format, AudioSystem.NOT_SPECIFIED));
+
+ if (Math.abs(format.getSampleRate() - outputformat.getSampleRate()) > 0.000001)
+ afis = new AudioFloatInputStreamResampler(afis, outputformat);
+ }
+
+ }
+
+ protected void processAudioLogic(SoftAudioBuffer[] buffers) {
+ if (_active) {
+ float[] left = buffers[SoftMixingMainMixer.CHANNEL_LEFT].array();
+ float[] right = buffers[SoftMixingMainMixer.CHANNEL_RIGHT].array();
+ int bufferlen = buffers[SoftMixingMainMixer.CHANNEL_LEFT].getSize();
+
+ int readlen = bufferlen * in_nrofchannels;
+ if (readbuffer == null || readbuffer.length < readlen) {
+ readbuffer = new float[readlen];
+ }
+ int ret = 0;
+ try {
+ ret = afis.read(readbuffer);
+ if (ret == -1) {
+ _active = false;
+ return;
+ }
+ if (ret != in_nrofchannels)
+ Arrays.fill(readbuffer, ret, readlen, 0);
+ } catch (IOException e) {
+ }
+
+ int in_c = in_nrofchannels;
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ left[i] += readbuffer[ix] * _leftgain;
+ }
+
+ if (out_nrofchannels != 1) {
+ if (in_nrofchannels == 1) {
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ right[i] += readbuffer[ix] * _rightgain;
+ }
+ } else {
+ for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
+ right[i] += readbuffer[ix] * _rightgain;
+ }
+ }
+
+ }
+
+ if (_eff1gain > 0.0002) {
+
+ float[] eff1 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT1]
+ .array();
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ eff1[i] += readbuffer[ix] * _eff1gain;
+ }
+ if (in_nrofchannels == 2) {
+ for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
+ eff1[i] += readbuffer[ix] * _eff1gain;
+ }
+ }
+ }
+
+ if (_eff2gain > 0.0002) {
+ float[] eff2 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT2]
+ .array();
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ eff2[i] += readbuffer[ix] * _eff2gain;
+ }
+ if (in_nrofchannels == 2) {
+ for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
+ eff2[i] += readbuffer[ix] * _eff2gain;
+ }
+ }
+ }
+
+ }
+ }
+
+ public int getFrameLength() {
+ return bufferSize / format.getFrameSize();
+ }
+
+ public long getMicrosecondLength() {
+ return (long) (getFrameLength() * (1000000.0 / (double) getFormat()
+ .getSampleRate()));
+ }
+
+ public void loop(int count) {
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (isOpen()) {
+ if (active)
+ return;
+ active = true;
+ active_sg = true;
+ loopcount = count;
+ event = new LineEvent(this, LineEvent.Type.START,
+ getLongFramePosition());
+ }
+ }
+
+ if (event != null)
+ sendEvent(event);
+
+ }
+
+ public void open(AudioInputStream stream) throws LineUnavailableException,
+ IOException {
+ if (isOpen()) {
+ throw new IllegalStateException("Clip is already open with format "
+ + getFormat() + " and frame lengh of " + getFrameLength());
+ }
+ if (AudioFloatConverter.getConverter(stream.getFormat()) == null)
+ throw new IllegalArgumentException("Invalid format : "
+ + stream.getFormat().toString());
+
+ if (stream.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
+ byte[] data = new byte[(int) stream.getFrameLength()
+ * stream.getFormat().getFrameSize()];
+ int readsize = 512 * stream.getFormat().getFrameSize();
+ int len = 0;
+ while (len != data.length) {
+ if (readsize > data.length - len)
+ readsize = data.length - len;
+ int ret = stream.read(data, len, readsize);
+ if (ret == -1)
+ break;
+ if (ret == 0)
+ Thread.yield();
+ len += ret;
+ }
+ open(stream.getFormat(), data, 0, len);
+ } else {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] b = new byte[512 * stream.getFormat().getFrameSize()];
+ int r = 0;
+ while ((r = stream.read(b)) != -1) {
+ if (r == 0)
+ Thread.yield();
+ baos.write(b, 0, r);
+ }
+ open(stream.getFormat(), baos.toByteArray(), 0, baos.size());
+ }
+
+ }
+
+ public void open(AudioFormat format, byte[] data, int offset, int bufferSize)
+ throws LineUnavailableException {
+ synchronized (control_mutex) {
+ if (isOpen()) {
+ throw new IllegalStateException(
+ "Clip is already open with format " + getFormat()
+ + " and frame lengh of " + getFrameLength());
+ }
+ if (AudioFloatConverter.getConverter(format) == null)
+ throw new IllegalArgumentException("Invalid format : "
+ + format.toString());
+ if (bufferSize % format.getFrameSize() != 0)
+ throw new IllegalArgumentException(
+ "Buffer size does not represent an integral number of sample frames!");
+
+ this.data = data;
+ this.offset = offset;
+ this.bufferSize = bufferSize;
+ this.format = format;
+ this.framesize = format.getFrameSize();
+
+ loopstart = 0;
+ loopend = -1;
+ loop_sg = true;
+
+ if (!mixer.isOpen()) {
+ mixer.open();
+ mixer.implicitOpen = true;
+ }
+
+ outputformat = mixer.getFormat();
+ out_nrofchannels = outputformat.getChannels();
+ in_nrofchannels = format.getChannels();
+
+ open = true;
+
+ mixer.getMainMixer().openLine(this);
+ }
+
+ }
+
+ public void setFramePosition(int frames) {
+ synchronized (control_mutex) {
+ frameposition_sg = true;
+ frameposition = frames;
+ }
+ }
+
+ public void setLoopPoints(int start, int end) {
+ synchronized (control_mutex) {
+ if (end != -1) {
+ if (end < start)
+ throw new IllegalArgumentException("Invalid loop points : "
+ + start + " - " + end);
+ if (end * framesize > bufferSize)
+ throw new IllegalArgumentException("Invalid loop points : "
+ + start + " - " + end);
+ }
+ if (start * framesize > bufferSize)
+ throw new IllegalArgumentException("Invalid loop points : "
+ + start + " - " + end);
+ if (0 < start)
+ throw new IllegalArgumentException("Invalid loop points : "
+ + start + " - " + end);
+ loopstart = start;
+ loopend = end;
+ loop_sg = true;
+ }
+ }
+
+ public void setMicrosecondPosition(long microseconds) {
+ setFramePosition((int) (microseconds * (((double) getFormat()
+ .getSampleRate()) / 1000000.0)));
+ }
+
+ public int available() {
+ return 0;
+ }
+
+ public void drain() {
+ }
+
+ public void flush() {
+ }
+
+ public int getBufferSize() {
+ return bufferSize;
+ }
+
+ public AudioFormat getFormat() {
+ return format;
+ }
+
+ public int getFramePosition() {
+ synchronized (control_mutex) {
+ return frameposition;
+ }
+ }
+
+ public float getLevel() {
+ return AudioSystem.NOT_SPECIFIED;
+ }
+
+ public long getLongFramePosition() {
+ return getFramePosition();
+ }
+
+ public long getMicrosecondPosition() {
+ return (long) (getFramePosition() * (1000000.0 / (double) getFormat()
+ .getSampleRate()));
+ }
+
+ public boolean isActive() {
+ synchronized (control_mutex) {
+ return active;
+ }
+ }
+
+ public boolean isRunning() {
+ synchronized (control_mutex) {
+ return active;
+ }
+ }
+
+ public void start() {
+
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (isOpen()) {
+ if (active)
+ return;
+ active = true;
+ active_sg = true;
+ loopcount = 0;
+ event = new LineEvent(this, LineEvent.Type.START,
+ getLongFramePosition());
+ }
+ }
+
+ if (event != null)
+ sendEvent(event);
+ }
+
+ public void stop() {
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (isOpen()) {
+ if (!active)
+ return;
+ active = false;
+ active_sg = true;
+ event = new LineEvent(this, LineEvent.Type.STOP,
+ getLongFramePosition());
+ }
+ }
+
+ if (event != null)
+ sendEvent(event);
+ }
+
+ public void close() {
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (!isOpen())
+ return;
+ stop();
+
+ event = new LineEvent(this, LineEvent.Type.CLOSE,
+ getLongFramePosition());
+
+ open = false;
+ mixer.getMainMixer().closeLine(this);
+ }
+
+ if (event != null)
+ sendEvent(event);
+
+ }
+
+ public boolean isOpen() {
+ return open;
+ }
+
+ public void open() throws LineUnavailableException {
+ if (data == null) {
+ throw new IllegalArgumentException(
+ "Illegal call to open() in interface Clip");
+ }
+ open(format, data, offset, bufferSize);
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMixingDataLine.java b/src/share/classes/com/sun/media/sound/SoftMixingDataLine.java
new file mode 100644
index 000000000..87af12482
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMixingDataLine.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.BooleanControl;
+import javax.sound.sampled.Control;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.FloatControl;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineListener;
+import javax.sound.sampled.Control.Type;
+
+/**
+ * General software mixing line.
+ *
+ * @author Karl Helgason
+ */
+public abstract class SoftMixingDataLine implements DataLine {
+
+ public static final FloatControl.Type CHORUS_SEND = new FloatControl.Type(
+ "Chorus Send") {
+ };
+
+ protected static class AudioFloatInputStreamResampler extends
+ AudioFloatInputStream {
+
+ private AudioFloatInputStream ais;
+
+ private AudioFormat targetFormat;
+
+ private float[] skipbuffer;
+
+ private SoftAbstractResampler resampler;
+
+ private float[] pitch = new float[1];
+
+ private float[] ibuffer2;
+
+ private float[][] ibuffer;
+
+ private float ibuffer_index = 0;
+
+ private int ibuffer_len = 0;
+
+ private int nrofchannels = 0;
+
+ private float[][] cbuffer;
+
+ private int buffer_len = 512;
+
+ private int pad;
+
+ private int pad2;
+
+ private float[] ix = new float[1];
+
+ private int[] ox = new int[1];
+
+ private float[][] mark_ibuffer = null;
+
+ private float mark_ibuffer_index = 0;
+
+ private int mark_ibuffer_len = 0;
+
+ public AudioFloatInputStreamResampler(AudioFloatInputStream ais,
+ AudioFormat format) {
+ this.ais = ais;
+ AudioFormat sourceFormat = ais.getFormat();
+ targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
+ .getSampleRate(), sourceFormat.getSampleSizeInBits(),
+ sourceFormat.getChannels(), sourceFormat.getFrameSize(),
+ format.getSampleRate(), sourceFormat.isBigEndian());
+ nrofchannels = targetFormat.getChannels();
+ Object interpolation = format.getProperty("interpolation");
+ if (interpolation != null && (interpolation instanceof String)) {
+ String resamplerType = (String) interpolation;
+ if (resamplerType.equalsIgnoreCase("point"))
+ this.resampler = new SoftPointResampler();
+ if (resamplerType.equalsIgnoreCase("linear"))
+ this.resampler = new SoftLinearResampler2();
+ if (resamplerType.equalsIgnoreCase("linear1"))
+ this.resampler = new SoftLinearResampler();
+ if (resamplerType.equalsIgnoreCase("linear2"))
+ this.resampler = new SoftLinearResampler2();
+ if (resamplerType.equalsIgnoreCase("cubic"))
+ this.resampler = new SoftCubicResampler();
+ if (resamplerType.equalsIgnoreCase("lanczos"))
+ this.resampler = new SoftLanczosResampler();
+ if (resamplerType.equalsIgnoreCase("sinc"))
+ this.resampler = new SoftSincResampler();
+ }
+ if (resampler == null)
+ resampler = new SoftLinearResampler2(); // new
+ // SoftLinearResampler2();
+ pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
+ pad = resampler.getPadding();
+ pad2 = pad * 2;
+ ibuffer = new float[nrofchannels][buffer_len + pad2];
+ ibuffer2 = new float[nrofchannels * buffer_len];
+ ibuffer_index = buffer_len + pad;
+ ibuffer_len = buffer_len;
+ }
+
+ public int available() throws IOException {
+ return 0;
+ }
+
+ public void close() throws IOException {
+ ais.close();
+ }
+
+ public AudioFormat getFormat() {
+ return targetFormat;
+ }
+
+ public long getFrameLength() {
+ return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
+ }
+
+ public void mark(int readlimit) {
+ ais.mark((int) (readlimit * pitch[0]));
+ mark_ibuffer_index = ibuffer_index;
+ mark_ibuffer_len = ibuffer_len;
+ if (mark_ibuffer == null) {
+ mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
+ }
+ for (int c = 0; c < ibuffer.length; c++) {
+ float[] from = ibuffer[c];
+ float[] to = mark_ibuffer[c];
+ for (int i = 0; i < to.length; i++) {
+ to[i] = from[i];
+ }
+ }
+ }
+
+ public boolean markSupported() {
+ return ais.markSupported();
+ }
+
+ private void readNextBuffer() throws IOException {
+
+ if (ibuffer_len == -1)
+ return;
+
+ for (int c = 0; c < nrofchannels; c++) {
+ float[] buff = ibuffer[c];
+ int buffer_len_pad = ibuffer_len + pad2;
+ for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
+ buff[ix] = buff[i];
+ }
+ }
+
+ ibuffer_index -= (ibuffer_len);
+
+ ibuffer_len = ais.read(ibuffer2);
+ if (ibuffer_len >= 0) {
+ while (ibuffer_len < ibuffer2.length) {
+ int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
+ - ibuffer_len);
+ if (ret == -1)
+ break;
+ ibuffer_len += ret;
+ }
+ Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
+ ibuffer_len /= nrofchannels;
+ } else {
+ Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
+ }
+
+ int ibuffer2_len = ibuffer2.length;
+ for (int c = 0; c < nrofchannels; c++) {
+ float[] buff = ibuffer[c];
+ for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
+ buff[ix] = ibuffer2[i];
+ }
+ }
+
+ }
+
+ public int read(float[] b, int off, int len) throws IOException {
+
+ if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
+ cbuffer = new float[nrofchannels][len / nrofchannels];
+ }
+ if (ibuffer_len == -1)
+ return -1;
+ if (len < 0)
+ return 0;
+ int remain = len / nrofchannels;
+ int destPos = 0;
+ int in_end = ibuffer_len;
+ while (remain > 0) {
+ if (ibuffer_len >= 0) {
+ if (ibuffer_index >= (ibuffer_len + pad))
+ readNextBuffer();
+ in_end = ibuffer_len + pad;
+ }
+
+ if (ibuffer_len < 0) {
+ in_end = pad2;
+ if (ibuffer_index >= in_end)
+ break;
+ }
+
+ if (ibuffer_index < 0)
+ break;
+ int preDestPos = destPos;
+ for (int c = 0; c < nrofchannels; c++) {
+ ix[0] = ibuffer_index;
+ ox[0] = destPos;
+ float[] buff = ibuffer[c];
+ resampler.interpolate(buff, ix, in_end, pitch, 0,
+ cbuffer[c], ox, len / nrofchannels);
+ }
+ ibuffer_index = ix[0];
+ destPos = ox[0];
+ remain -= destPos - preDestPos;
+ }
+ for (int c = 0; c < nrofchannels; c++) {
+ int ix = 0;
+ float[] buff = cbuffer[c];
+ for (int i = c; i < b.length; i += nrofchannels) {
+ b[i] = buff[ix++];
+ }
+ }
+ return len - remain * nrofchannels;
+ }
+
+ public void reset() throws IOException {
+ ais.reset();
+ if (mark_ibuffer == null)
+ return;
+ ibuffer_index = mark_ibuffer_index;
+ ibuffer_len = mark_ibuffer_len;
+ for (int c = 0; c < ibuffer.length; c++) {
+ float[] from = mark_ibuffer[c];
+ float[] to = ibuffer[c];
+ for (int i = 0; i < to.length; i++) {
+ to[i] = from[i];
+ }
+ }
+
+ }
+
+ public long skip(long len) throws IOException {
+ if (len > 0)
+ return 0;
+ if (skipbuffer == null)
+ skipbuffer = new float[1024 * targetFormat.getFrameSize()];
+ float[] l_skipbuffer = skipbuffer;
+ long remain = len;
+ while (remain > 0) {
+ int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
+ skipbuffer.length));
+ if (ret < 0) {
+ if (remain == len)
+ return ret;
+ break;
+ }
+ remain -= ret;
+ }
+ return len - remain;
+
+ }
+
+ }
+
+ private class Gain extends FloatControl {
+
+ private Gain() {
+
+ super(FloatControl.Type.MASTER_GAIN, -80f, 6.0206f, 80f / 128.0f,
+ -1, 0.0f, "dB", "Minimum", "", "Maximum");
+ }
+
+ public void setValue(float newValue) {
+ super.setValue(newValue);
+ calcVolume();
+ }
+ }
+
+ private class Mute extends BooleanControl {
+
+ private Mute() {
+ super(BooleanControl.Type.MUTE, false, "True", "False");
+ }
+
+ public void setValue(boolean newValue) {
+ super.setValue(newValue);
+ calcVolume();
+ }
+ }
+
+ private class ApplyReverb extends BooleanControl {
+
+ private ApplyReverb() {
+ super(BooleanControl.Type.APPLY_REVERB, false, "True", "False");
+ }
+
+ public void setValue(boolean newValue) {
+ super.setValue(newValue);
+ calcVolume();
+ }
+
+ }
+
+ private class Balance extends FloatControl {
+
+ private Balance() {
+ super(FloatControl.Type.BALANCE, -1.0f, 1.0f, (1.0f / 128.0f), -1,
+ 0.0f, "", "Left", "Center", "Right");
+ }
+
+ public void setValue(float newValue) {
+ super.setValue(newValue);
+ calcVolume();
+ }
+
+ }
+
+ private class Pan extends FloatControl {
+
+ private Pan() {
+ super(FloatControl.Type.PAN, -1.0f, 1.0f, (1.0f / 128.0f), -1,
+ 0.0f, "", "Left", "Center", "Right");
+ }
+
+ public void setValue(float newValue) {
+ super.setValue(newValue);
+ balance_control.setValue(newValue);
+ }
+
+ public float getValue() {
+ return balance_control.getValue();
+ }
+
+ }
+
+ private class ReverbSend extends FloatControl {
+
+ private ReverbSend() {
+ super(FloatControl.Type.REVERB_SEND, -80f, 6.0206f, 80f / 128.0f,
+ -1, -80f, "dB", "Minimum", "", "Maximum");
+ }
+
+ public void setValue(float newValue) {
+ super.setValue(newValue);
+ balance_control.setValue(newValue);
+ }
+
+ }
+
+ private class ChorusSend extends FloatControl {
+
+ private ChorusSend() {
+ super(CHORUS_SEND, -80f, 6.0206f, 80f / 128.0f, -1, -80f, "dB",
+ "Minimum", "", "Maximum");
+ }
+
+ public void setValue(float newValue) {
+ super.setValue(newValue);
+ balance_control.setValue(newValue);
+ }
+
+ }
+
+ private Gain gain_control = new Gain();
+
+ private Mute mute_control = new Mute();
+
+ private Balance balance_control = new Balance();
+
+ private Pan pan_control = new Pan();
+
+ private ReverbSend reverbsend_control = new ReverbSend();
+
+ private ChorusSend chorussend_control = new ChorusSend();
+
+ private ApplyReverb apply_reverb = new ApplyReverb();
+
+ private Control[] controls;
+
+ protected float leftgain = 1;
+
+ protected float rightgain = 1;
+
+ protected float eff1gain = 0;
+
+ protected float eff2gain = 0;
+
+ protected List<LineListener> listeners = new ArrayList<LineListener>();
+
+ protected Object control_mutex;
+
+ protected SoftMixingMixer mixer;
+
+ protected DataLine.Info info;
+
+ protected abstract void processControlLogic();
+
+ protected abstract void processAudioLogic(SoftAudioBuffer[] buffers);
+
+ protected SoftMixingDataLine(SoftMixingMixer mixer, DataLine.Info info) {
+ this.mixer = mixer;
+ this.info = info;
+ this.control_mutex = mixer.control_mutex;
+
+ controls = new Control[] { gain_control, mute_control, balance_control,
+ pan_control, reverbsend_control, chorussend_control,
+ apply_reverb };
+ calcVolume();
+ }
+
+ protected void calcVolume() {
+ synchronized (control_mutex) {
+ double gain = Math.pow(10.0, gain_control.getValue() / 20.0);
+ if (mute_control.getValue())
+ gain = 0;
+ leftgain = (float) gain;
+ rightgain = (float) gain;
+ if (mixer.getFormat().getChannels() > 1) {
+ // -1 = Left, 0 Center, 1 = Right
+ double balance = balance_control.getValue();
+ if (balance > 0)
+ leftgain *= (1 - balance);
+ else
+ rightgain *= (1 + balance);
+
+ }
+ }
+
+ eff1gain = (float) Math.pow(10.0, reverbsend_control.getValue() / 20.0);
+ eff2gain = (float) Math.pow(10.0, chorussend_control.getValue() / 20.0);
+
+ if (!apply_reverb.getValue()) {
+ eff1gain = 0;
+ }
+ }
+
+ protected void sendEvent(LineEvent event) {
+ if (listeners.size() == 0)
+ return;
+ LineListener[] listener_array = listeners
+ .toArray(new LineListener[listeners.size()]);
+ for (LineListener listener : listener_array) {
+ listener.update(event);
+ }
+ }
+
+ public void addLineListener(LineListener listener) {
+ synchronized (control_mutex) {
+ listeners.add(listener);
+ }
+ }
+
+ public void removeLineListener(LineListener listener) {
+ synchronized (control_mutex) {
+ listeners.add(listener);
+ }
+ }
+
+ public javax.sound.sampled.Line.Info getLineInfo() {
+ return info;
+ }
+
+ public Control getControl(Type control) {
+ if (control != null) {
+ for (int i = 0; i < controls.length; i++) {
+ if (controls[i].getType() == control) {
+ return controls[i];
+ }
+ }
+ }
+ throw new IllegalArgumentException("Unsupported control type : "
+ + control);
+ }
+
+ public Control[] getControls() {
+ return controls;
+ }
+
+ public boolean isControlSupported(Type control) {
+ if (control != null) {
+ for (int i = 0; i < controls.length; i++) {
+ if (controls[i].getType() == control) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMixingMainMixer.java b/src/share/classes/com/sun/media/sound/SoftMixingMainMixer.java
new file mode 100644
index 000000000..e016d3f4b
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMixingMainMixer.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+
+/**
+ * Main mixer for SoftMixingMixer.
+ *
+ * @author Karl Helgason
+ */
+public class SoftMixingMainMixer {
+
+ public final static int CHANNEL_LEFT = 0;
+
+ public final static int CHANNEL_RIGHT = 1;
+
+ public final static int CHANNEL_EFFECT1 = 2;
+
+ public final static int CHANNEL_EFFECT2 = 3;
+
+ public final static int CHANNEL_EFFECT3 = 4;
+
+ public final static int CHANNEL_EFFECT4 = 5;
+
+ public final static int CHANNEL_LEFT_DRY = 10;
+
+ public final static int CHANNEL_RIGHT_DRY = 11;
+
+ public final static int CHANNEL_SCRATCH1 = 12;
+
+ public final static int CHANNEL_SCRATCH2 = 13;
+
+ public final static int CHANNEL_CHANNELMIXER_LEFT = 14;
+
+ public final static int CHANNEL_CHANNELMIXER_RIGHT = 15;
+
+ private SoftMixingMixer mixer;
+
+ private AudioInputStream ais;
+
+ private SoftAudioBuffer[] buffers;
+
+ private SoftAudioProcessor reverb;
+
+ private SoftAudioProcessor chorus;
+
+ private SoftAudioProcessor agc;
+
+ private int nrofchannels;
+
+ private Object control_mutex;
+
+ private List<SoftMixingDataLine> openLinesList = new ArrayList<SoftMixingDataLine>();
+
+ private SoftMixingDataLine[] openLines = new SoftMixingDataLine[0];
+
+ public AudioInputStream getInputStream() {
+ return ais;
+ }
+
+ protected void processAudioBuffers() {
+ for (int i = 0; i < buffers.length; i++) {
+ buffers[i].clear();
+ }
+
+ SoftMixingDataLine[] openLines;
+ synchronized (control_mutex) {
+ openLines = this.openLines;
+ for (int i = 0; i < openLines.length; i++) {
+ openLines[i].processControlLogic();
+ }
+ chorus.processControlLogic();
+ reverb.processControlLogic();
+ agc.processControlLogic();
+ }
+ for (int i = 0; i < openLines.length; i++) {
+ openLines[i].processAudioLogic(buffers);
+ }
+
+ chorus.processAudio();
+ reverb.processAudio();
+
+ agc.processAudio();
+
+ }
+
+ public SoftMixingMainMixer(SoftMixingMixer mixer) {
+ this.mixer = mixer;
+
+ nrofchannels = mixer.getFormat().getChannels();
+
+ int buffersize = (int) (mixer.getFormat().getSampleRate() / mixer
+ .getControlRate());
+
+ control_mutex = mixer.control_mutex;
+ buffers = new SoftAudioBuffer[16];
+ for (int i = 0; i < buffers.length; i++) {
+ buffers[i] = new SoftAudioBuffer(buffersize, mixer.getFormat());
+
+ }
+
+ reverb = new SoftReverb();
+ chorus = new SoftChorus();
+ agc = new SoftLimiter();
+
+ float samplerate = mixer.getFormat().getSampleRate();
+ float controlrate = mixer.getControlRate();
+ reverb.init(samplerate, controlrate);
+ chorus.init(samplerate, controlrate);
+ agc.init(samplerate, controlrate);
+
+ reverb.setMixMode(true);
+ chorus.setMixMode(true);
+ agc.setMixMode(false);
+
+ chorus.setInput(0, buffers[CHANNEL_EFFECT2]);
+ chorus.setOutput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ chorus.setOutput(1, buffers[CHANNEL_RIGHT]);
+ chorus.setOutput(2, buffers[CHANNEL_EFFECT1]);
+
+ reverb.setInput(0, buffers[CHANNEL_EFFECT1]);
+ reverb.setOutput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ reverb.setOutput(1, buffers[CHANNEL_RIGHT]);
+
+ agc.setInput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ agc.setInput(1, buffers[CHANNEL_RIGHT]);
+ agc.setOutput(0, buffers[CHANNEL_LEFT]);
+ if (nrofchannels != 1)
+ agc.setOutput(1, buffers[CHANNEL_RIGHT]);
+
+ InputStream in = new InputStream() {
+
+ private SoftAudioBuffer[] buffers = SoftMixingMainMixer.this.buffers;
+
+ private int nrofchannels = SoftMixingMainMixer.this.mixer
+ .getFormat().getChannels();
+
+ private int buffersize = buffers[0].getSize();
+
+ private byte[] bbuffer = new byte[buffersize
+ * (SoftMixingMainMixer.this.mixer.getFormat()
+ .getSampleSizeInBits() / 8) * nrofchannels];
+
+ private int bbuffer_pos = 0;
+
+ private byte[] single = new byte[1];
+
+ public void fillBuffer() {
+ processAudioBuffers();
+ for (int i = 0; i < nrofchannels; i++)
+ buffers[i].get(bbuffer, i);
+ bbuffer_pos = 0;
+ }
+
+ public int read(byte[] b, int off, int len) {
+ int bbuffer_len = bbuffer.length;
+ int offlen = off + len;
+ byte[] bbuffer = this.bbuffer;
+ while (off < offlen)
+ if (available() == 0)
+ fillBuffer();
+ else {
+ int bbuffer_pos = this.bbuffer_pos;
+ while (off < offlen && bbuffer_pos < bbuffer_len)
+ b[off++] = bbuffer[bbuffer_pos++];
+ this.bbuffer_pos = bbuffer_pos;
+ }
+ return len;
+ }
+
+ public int read() throws IOException {
+ int ret = read(single);
+ if (ret == -1)
+ return -1;
+ return single[0] & 0xFF;
+ }
+
+ public int available() {
+ return bbuffer.length - bbuffer_pos;
+ }
+
+ public void close() {
+ SoftMixingMainMixer.this.mixer.close();
+ }
+
+ };
+
+ ais = new AudioInputStream(in, mixer.getFormat(),
+ AudioSystem.NOT_SPECIFIED);
+
+ }
+
+ public void openLine(SoftMixingDataLine line) {
+ synchronized (control_mutex) {
+ openLinesList.add(line);
+ openLines = openLinesList
+ .toArray(new SoftMixingDataLine[openLinesList.size()]);
+ }
+ }
+
+ public void closeLine(SoftMixingDataLine line) {
+ synchronized (control_mutex) {
+ openLinesList.remove(line);
+ openLines = openLinesList
+ .toArray(new SoftMixingDataLine[openLinesList.size()]);
+ if (openLines.length == 0)
+ if (mixer.implicitOpen)
+ mixer.close();
+ }
+
+ }
+
+ public SoftMixingDataLine[] getOpenLines() {
+ synchronized (control_mutex) {
+ return openLines;
+ }
+
+ }
+
+ public void close() {
+ SoftMixingDataLine[] openLines = this.openLines;
+ for (int i = 0; i < openLines.length; i++) {
+ openLines[i].close();
+ }
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMixingMixer.java b/src/share/classes/com/sun/media/sound/SoftMixingMixer.java
new file mode 100644
index 000000000..6419109c4
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMixingMixer.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.Control;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.Line;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineListener;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.Mixer;
+import javax.sound.sampled.SourceDataLine;
+import javax.sound.sampled.AudioFormat.Encoding;
+import javax.sound.sampled.Control.Type;
+
+/**
+ * Software audio mixer
+ *
+ * @author Karl Helgason
+ */
+public class SoftMixingMixer implements Mixer {
+
+ private static class Info extends Mixer.Info {
+ public Info() {
+ super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION);
+ }
+ }
+
+ protected static final String INFO_NAME = "Gervill Sound Mixer";
+
+ protected static final String INFO_VENDOR = "OpenJDK Proposal";
+
+ protected static final String INFO_DESCRIPTION = "Software Sound Mixer";
+
+ protected static final String INFO_VERSION = "1.0";
+
+ protected final static Mixer.Info info = new Info();
+
+ protected Object control_mutex = this;
+
+ protected boolean implicitOpen = false;
+
+ private boolean open = false;
+
+ private SoftMixingMainMixer mainmixer = null;
+
+ private AudioFormat format = new AudioFormat(44100, 16, 2, true, false);
+
+ private SourceDataLine sourceDataLine = null;
+
+ private SoftAudioPusher pusher = null;
+
+ private AudioInputStream pusher_stream = null;
+
+ private float controlrate = 147f;
+
+ private long latency = 100000; // 100 msec
+
+ private boolean jitter_correction = false;
+
+ private List<LineListener> listeners = new ArrayList<LineListener>();
+
+ private javax.sound.sampled.Line.Info[] sourceLineInfo;
+
+ public SoftMixingMixer() {
+
+ sourceLineInfo = new javax.sound.sampled.Line.Info[2];
+
+ ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>();
+ for (int channels = 1; channels <= 2; channels++) {
+ formats.add(new AudioFormat(Encoding.PCM_SIGNED,
+ AudioSystem.NOT_SPECIFIED, 8, channels, channels,
+ AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
+ AudioSystem.NOT_SPECIFIED, 8, channels, channels,
+ AudioSystem.NOT_SPECIFIED, false));
+ for (int bits = 16; bits < 32; bits += 8) {
+ formats.add(new AudioFormat(Encoding.PCM_SIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(Encoding.PCM_SIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, true));
+ formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
+ AudioSystem.NOT_SPECIFIED, bits, channels, channels
+ * bits / 8, AudioSystem.NOT_SPECIFIED, true));
+ }
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
+ AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
+ AudioSystem.NOT_SPECIFIED, true));
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
+ AudioSystem.NOT_SPECIFIED, false));
+ formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
+ AudioSystem.NOT_SPECIFIED, true));
+ }
+ AudioFormat[] formats_array = formats.toArray(new AudioFormat[formats
+ .size()]);
+ sourceLineInfo[0] = new DataLine.Info(SourceDataLine.class,
+ formats_array, AudioSystem.NOT_SPECIFIED,
+ AudioSystem.NOT_SPECIFIED);
+ sourceLineInfo[1] = new DataLine.Info(Clip.class, formats_array,
+ AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED);
+ }
+
+ public Line getLine(Line.Info info) throws LineUnavailableException {
+
+ if (!isLineSupported(info))
+ throw new IllegalArgumentException("Line unsupported: " + info);
+
+ if ((info.getLineClass() == SourceDataLine.class)) {
+ return new SoftMixingSourceDataLine(this, (DataLine.Info) info);
+ }
+ if ((info.getLineClass() == Clip.class)) {
+ return new SoftMixingClip(this, (DataLine.Info) info);
+ }
+
+ throw new IllegalArgumentException("Line unsupported: " + info);
+ }
+
+ public int getMaxLines(Line.Info info) {
+ if (info.getLineClass() == SourceDataLine.class)
+ return AudioSystem.NOT_SPECIFIED;
+ if (info.getLineClass() == Clip.class)
+ return AudioSystem.NOT_SPECIFIED;
+ return 0;
+ }
+
+ public javax.sound.sampled.Mixer.Info getMixerInfo() {
+ return info;
+ }
+
+ public javax.sound.sampled.Line.Info[] getSourceLineInfo() {
+ Line.Info[] localArray = new Line.Info[sourceLineInfo.length];
+ System.arraycopy(sourceLineInfo, 0, localArray, 0,
+ sourceLineInfo.length);
+ return localArray;
+ }
+
+ public javax.sound.sampled.Line.Info[] getSourceLineInfo(
+ javax.sound.sampled.Line.Info info) {
+ int i;
+ ArrayList<javax.sound.sampled.Line.Info> infos = new ArrayList<javax.sound.sampled.Line.Info>();
+
+ for (i = 0; i < sourceLineInfo.length; i++) {
+ if (info.matches(sourceLineInfo[i])) {
+ infos.add(sourceLineInfo[i]);
+ }
+ }
+ return infos.toArray(new Line.Info[infos.size()]);
+ }
+
+ public Line[] getSourceLines() {
+
+ Line[] localLines;
+
+ synchronized (control_mutex) {
+
+ if (mainmixer == null)
+ return new Line[0];
+ SoftMixingDataLine[] sourceLines = mainmixer.getOpenLines();
+
+ localLines = new Line[sourceLines.length];
+
+ for (int i = 0; i < localLines.length; i++) {
+ localLines[i] = sourceLines[i];
+ }
+ }
+
+ return localLines;
+ }
+
+ public javax.sound.sampled.Line.Info[] getTargetLineInfo() {
+ return new javax.sound.sampled.Line.Info[0];
+ }
+
+ public javax.sound.sampled.Line.Info[] getTargetLineInfo(
+ javax.sound.sampled.Line.Info info) {
+ return new javax.sound.sampled.Line.Info[0];
+ }
+
+ public Line[] getTargetLines() {
+ return new Line[0];
+ }
+
+ public boolean isLineSupported(javax.sound.sampled.Line.Info info) {
+ if (info != null) {
+ for (int i = 0; i < sourceLineInfo.length; i++) {
+ if (info.matches(sourceLineInfo[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isSynchronizationSupported(Line[] lines, boolean maintainSync) {
+ return false;
+ }
+
+ public void synchronize(Line[] lines, boolean maintainSync) {
+ throw new IllegalArgumentException(
+ "Synchronization not supported by this mixer.");
+ }
+
+ public void unsynchronize(Line[] lines) {
+ throw new IllegalArgumentException(
+ "Synchronization not supported by this mixer.");
+ }
+
+ public void addLineListener(LineListener listener) {
+ synchronized (control_mutex) {
+ listeners.add(listener);
+ }
+ }
+
+ private void sendEvent(LineEvent event) {
+ if (listeners.size() == 0)
+ return;
+ LineListener[] listener_array = listeners
+ .toArray(new LineListener[listeners.size()]);
+ for (LineListener listener : listener_array) {
+ listener.update(event);
+ }
+ }
+
+ public void close() {
+ if (!isOpen())
+ return;
+
+ sendEvent(new LineEvent(this, LineEvent.Type.CLOSE,
+ AudioSystem.NOT_SPECIFIED));
+
+ SoftAudioPusher pusher_to_be_closed = null;
+ AudioInputStream pusher_stream_to_be_closed = null;
+ synchronized (control_mutex) {
+ if (pusher != null) {
+ pusher_to_be_closed = pusher;
+ pusher_stream_to_be_closed = pusher_stream;
+ pusher = null;
+ pusher_stream = null;
+ }
+ }
+
+ if (pusher_to_be_closed != null) {
+ // Pusher must not be closed synchronized against control_mutex
+ // this may result in synchronized conflict between pusher and
+ // current thread.
+ pusher_to_be_closed.stop();
+
+ try {
+ pusher_stream_to_be_closed.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ synchronized (control_mutex) {
+
+ if (mainmixer != null)
+ mainmixer.close();
+ open = false;
+
+ if (sourceDataLine != null) {
+ sourceDataLine.drain();
+ sourceDataLine.close();
+ sourceDataLine = null;
+ }
+
+ }
+
+ }
+
+ public Control getControl(Type control) {
+ throw new IllegalArgumentException("Unsupported control type : "
+ + control);
+ }
+
+ public Control[] getControls() {
+ return new Control[0];
+ }
+
+ public javax.sound.sampled.Line.Info getLineInfo() {
+ return new Line.Info(Mixer.class);
+ }
+
+ public boolean isControlSupported(Type control) {
+ return false;
+ }
+
+ public boolean isOpen() {
+ synchronized (control_mutex) {
+ return open;
+ }
+ }
+
+ public void open() throws LineUnavailableException {
+ if (isOpen()) {
+ implicitOpen = false;
+ return;
+ }
+ open(null);
+ }
+
+ public void open(SourceDataLine line) throws LineUnavailableException {
+ if (isOpen()) {
+ implicitOpen = false;
+ return;
+ }
+ synchronized (control_mutex) {
+
+ try {
+
+ if (line != null)
+ format = line.getFormat();
+
+ AudioInputStream ais = openStream(getFormat());
+
+ if (line == null) {
+ synchronized (SoftMixingMixerProvider.mutex) {
+ SoftMixingMixerProvider.lockthread = Thread
+ .currentThread();
+ }
+
+ try {
+ Mixer defaultmixer = AudioSystem.getMixer(null);
+ if (defaultmixer != null)
+ {
+ // Search for suitable line
+
+ DataLine.Info idealinfo = null;
+ AudioFormat idealformat = null;
+
+ Line.Info[] lineinfos = defaultmixer.getSourceLineInfo();
+ idealFound:
+ for (int i = 0; i < lineinfos.length; i++) {
+ if(lineinfos[i].getLineClass() == SourceDataLine.class)
+ {
+ DataLine.Info info = (DataLine.Info)lineinfos[i];
+ AudioFormat[] formats = info.getFormats();
+ for (int j = 0; j < formats.length; j++) {
+ AudioFormat format = formats[j];
+ if(format.getChannels() == 2 ||
+ format.getChannels() == AudioSystem.NOT_SPECIFIED)
+ if(format.getEncoding().equals(Encoding.PCM_SIGNED) ||
+ format.getEncoding().equals(Encoding.PCM_UNSIGNED))
+ if(format.getSampleRate() == AudioSystem.NOT_SPECIFIED ||
+ format.getSampleRate() == 48000.0)
+ if(format.getSampleSizeInBits() == AudioSystem.NOT_SPECIFIED ||
+ format.getSampleSizeInBits() == 16)
+ {
+ idealinfo = info;
+ int ideal_channels = format.getChannels();
+ boolean ideal_signed = format.getEncoding().equals(Encoding.PCM_SIGNED);
+ float ideal_rate = format.getSampleRate();
+ boolean ideal_endian = format.isBigEndian();
+ int ideal_bits = format.getSampleSizeInBits();
+ if(ideal_bits == AudioSystem.NOT_SPECIFIED) ideal_bits = 16;
+ if(ideal_channels == AudioSystem.NOT_SPECIFIED) ideal_channels = 2;
+ if(ideal_rate == AudioSystem.NOT_SPECIFIED) ideal_rate = 48000;
+ idealformat = new AudioFormat(ideal_rate, ideal_bits,
+ ideal_channels, ideal_signed, ideal_endian);
+ break idealFound;
+ }
+ }
+ }
+ }
+
+ if(idealformat != null)
+ {
+ format = idealformat;
+ line = (SourceDataLine) defaultmixer.getLine(idealinfo);
+ }
+ }
+
+ if(line == null)
+ line = AudioSystem.getSourceDataLine(format);
+ } finally {
+ synchronized (SoftMixingMixerProvider.mutex) {
+ SoftMixingMixerProvider.lockthread = null;
+ }
+ }
+
+ if (line == null)
+ throw new IllegalArgumentException("No line matching "
+ + info.toString() + " is supported.");
+ }
+
+ double latency = this.latency;
+
+ if (!line.isOpen()) {
+ int bufferSize = getFormat().getFrameSize()
+ * (int) (getFormat().getFrameRate() * (latency / 1000000f));
+ line.open(getFormat(), bufferSize);
+
+ // Remember that we opened that line
+ // so we can close again in SoftSynthesizer.close()
+ sourceDataLine = line;
+ }
+ if (!line.isActive())
+ line.start();
+
+ int controlbuffersize = 512;
+ try {
+ controlbuffersize = ais.available();
+ } catch (IOException e) {
+ }
+
+ // Tell mixer not fill read buffers fully.
+ // This lowers latency, and tells DataPusher
+ // to read in smaller amounts.
+ // mainmixer.readfully = false;
+ // pusher = new DataPusher(line, ais);
+
+ int buffersize = line.getBufferSize();
+ buffersize -= buffersize % controlbuffersize;
+
+ if (buffersize < 3 * controlbuffersize)
+ buffersize = 3 * controlbuffersize;
+
+ if (jitter_correction) {
+ ais = new SoftJitterCorrector(ais, buffersize,
+ controlbuffersize);
+ }
+ pusher = new SoftAudioPusher(line, ais, controlbuffersize);
+ pusher_stream = ais;
+ pusher.start();
+
+ } catch (LineUnavailableException e) {
+ if (isOpen())
+ close();
+ throw new LineUnavailableException(e.toString());
+ }
+
+ }
+ }
+
+ public AudioInputStream openStream(AudioFormat targetFormat)
+ throws LineUnavailableException {
+
+ if (isOpen())
+ throw new LineUnavailableException("Mixer is already open");
+
+ synchronized (control_mutex) {
+
+ open = true;
+
+ implicitOpen = false;
+
+ if (targetFormat != null)
+ format = targetFormat;
+
+ mainmixer = new SoftMixingMainMixer(this);
+
+ sendEvent(new LineEvent(this, LineEvent.Type.OPEN,
+ AudioSystem.NOT_SPECIFIED));
+
+ return mainmixer.getInputStream();
+
+ }
+
+ }
+
+ public void removeLineListener(LineListener listener) {
+ synchronized (control_mutex) {
+ listeners.remove(listener);
+ }
+ }
+
+ public long getLatency() {
+ synchronized (control_mutex) {
+ return latency;
+ }
+ }
+
+ public AudioFormat getFormat() {
+ synchronized (control_mutex) {
+ return format;
+ }
+ }
+
+ protected float getControlRate() {
+ return controlrate;
+ }
+
+ protected SoftMixingMainMixer getMainMixer() {
+ if (!isOpen())
+ return null;
+ return mainmixer;
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMixingMixerProvider.java b/src/share/classes/com/sun/media/sound/SoftMixingMixerProvider.java
new file mode 100644
index 000000000..8c805a707
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMixingMixerProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.sampled.Mixer;
+import javax.sound.sampled.Mixer.Info;
+import javax.sound.sampled.spi.MixerProvider;
+
+/**
+ * Provider for software audio mixer
+ *
+ * @author Karl Helgason
+ */
+public class SoftMixingMixerProvider extends MixerProvider {
+
+ static SoftMixingMixer globalmixer = null;
+
+ static Thread lockthread = null;
+
+ protected final static Object mutex = new Object();
+
+ public Mixer getMixer(Info info) {
+ if (!(info == null || info == SoftMixingMixer.info)) {
+ throw new IllegalArgumentException("Mixer " + info.toString()
+ + " not supported by this provider.");
+ }
+ synchronized (mutex) {
+ if (lockthread != null)
+ if (Thread.currentThread() == lockthread)
+ throw new IllegalArgumentException("Mixer "
+ + info.toString()
+ + " not supported by this provider.");
+ if (globalmixer == null)
+ globalmixer = new SoftMixingMixer();
+ return globalmixer;
+ }
+
+ }
+
+ public Info[] getMixerInfo() {
+ return new Info[] { SoftMixingMixer.info };
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftMixingSourceDataLine.java b/src/share/classes/com/sun/media/sound/SoftMixingSourceDataLine.java
new file mode 100644
index 000000000..c59fa0ad5
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftMixingSourceDataLine.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.SourceDataLine;
+
+/**
+ * SourceDataLine implemention for the SoftMixingMixer.
+ *
+ * @author Karl Helgason
+ */
+public class SoftMixingSourceDataLine extends SoftMixingDataLine implements
+ SourceDataLine {
+
+ private boolean open = false;
+
+ private AudioFormat format = new AudioFormat(44100.0f, 16, 2, true, false);
+
+ private int framesize;
+
+ private int bufferSize = -1;
+
+ private float[] readbuffer;
+
+ private boolean active = false;
+
+ private byte[] cycling_buffer;
+
+ private int cycling_read_pos = 0;
+
+ private int cycling_write_pos = 0;
+
+ private int cycling_avail = 0;
+
+ private long cycling_framepos = 0;
+
+ private AudioFloatInputStream afis;
+
+ private static class NonBlockingFloatInputStream extends
+ AudioFloatInputStream {
+ AudioFloatInputStream ais;
+
+ public NonBlockingFloatInputStream(AudioFloatInputStream ais) {
+ this.ais = ais;
+ }
+
+ public int available() throws IOException {
+ return ais.available();
+ }
+
+ public void close() throws IOException {
+ ais.close();
+ }
+
+ public AudioFormat getFormat() {
+ return ais.getFormat();
+ }
+
+ public long getFrameLength() {
+ return ais.getFrameLength();
+ }
+
+ public void mark(int readlimit) {
+ ais.mark(readlimit);
+ }
+
+ public boolean markSupported() {
+ return ais.markSupported();
+ }
+
+ public int read(float[] b, int off, int len) throws IOException {
+ int avail = available();
+ if (len > avail) {
+ int ret = ais.read(b, off, avail);
+ Arrays.fill(b, off + ret, off + len, 0);
+ return len;
+ }
+ return ais.read(b, off, len);
+ }
+
+ public void reset() throws IOException {
+ ais.reset();
+ }
+
+ public long skip(long len) throws IOException {
+ return ais.skip(len);
+ }
+
+ }
+
+ protected SoftMixingSourceDataLine(SoftMixingMixer mixer, DataLine.Info info) {
+ super(mixer, info);
+ }
+
+ public int write(byte[] b, int off, int len) {
+ if (!isOpen())
+ return 0;
+ if (len % framesize != 0)
+ throw new IllegalArgumentException(
+ "Number of bytes does not represent an integral number of sample frames.");
+
+ byte[] buff = cycling_buffer;
+ int buff_len = cycling_buffer.length;
+
+ int l = 0;
+ while (l != len) {
+ int avail;
+ synchronized (cycling_buffer) {
+ int pos = cycling_write_pos;
+ avail = cycling_avail;
+ while (l != len) {
+ if (avail == buff_len)
+ break;
+ buff[pos++] = b[off++];
+ l++;
+ avail++;
+ if (pos == buff_len)
+ pos = 0;
+ }
+ cycling_avail = avail;
+ cycling_write_pos = pos;
+ if (l == len)
+ return l;
+ }
+ if (avail == buff_len) {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ return l;
+ }
+ if (!isRunning())
+ return l;
+ }
+ }
+
+ return l;
+ }
+
+ //
+ // BooleanControl.Type.APPLY_REVERB
+ // BooleanControl.Type.MUTE
+ // EnumControl.Type.REVERB
+ //
+ // FloatControl.Type.SAMPLE_RATE
+ // FloatControl.Type.REVERB_SEND
+ // FloatControl.Type.VOLUME
+ // FloatControl.Type.PAN
+ // FloatControl.Type.MASTER_GAIN
+ // FloatControl.Type.BALANCE
+
+ private boolean _active = false;
+
+ private AudioFormat outputformat;
+
+ private int out_nrofchannels;
+
+ private int in_nrofchannels;
+
+ private float _rightgain;
+
+ private float _leftgain;
+
+ private float _eff1gain;
+
+ private float _eff2gain;
+
+ protected void processControlLogic() {
+ _active = active;
+ _rightgain = rightgain;
+ _leftgain = leftgain;
+ _eff1gain = eff1gain;
+ _eff2gain = eff2gain;
+ }
+
+ protected void processAudioLogic(SoftAudioBuffer[] buffers) {
+ if (_active) {
+ float[] left = buffers[SoftMixingMainMixer.CHANNEL_LEFT].array();
+ float[] right = buffers[SoftMixingMainMixer.CHANNEL_RIGHT].array();
+ int bufferlen = buffers[SoftMixingMainMixer.CHANNEL_LEFT].getSize();
+
+ int readlen = bufferlen * in_nrofchannels;
+ if (readbuffer == null || readbuffer.length < readlen) {
+ readbuffer = new float[readlen];
+ }
+ int ret = 0;
+ try {
+ ret = afis.read(readbuffer);
+ if (ret != in_nrofchannels)
+ Arrays.fill(readbuffer, ret, readlen, 0);
+ } catch (IOException e) {
+ }
+
+ int in_c = in_nrofchannels;
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ left[i] += readbuffer[ix] * _leftgain;
+ }
+ if (out_nrofchannels != 1) {
+ if (in_nrofchannels == 1) {
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ right[i] += readbuffer[ix] * _rightgain;
+ }
+ } else {
+ for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
+ right[i] += readbuffer[ix] * _rightgain;
+ }
+ }
+
+ }
+
+ if (_eff1gain > 0.0001) {
+ float[] eff1 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT1]
+ .array();
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ eff1[i] += readbuffer[ix] * _eff1gain;
+ }
+ if (in_nrofchannels == 2) {
+ for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
+ eff1[i] += readbuffer[ix] * _eff1gain;
+ }
+ }
+ }
+
+ if (_eff2gain > 0.0001) {
+ float[] eff2 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT2]
+ .array();
+ for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
+ eff2[i] += readbuffer[ix] * _eff2gain;
+ }
+ if (in_nrofchannels == 2) {
+ for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
+ eff2[i] += readbuffer[ix] * _eff2gain;
+ }
+ }
+ }
+
+ }
+ }
+
+ public void open() throws LineUnavailableException {
+ open(format);
+ }
+
+ public void open(AudioFormat format) throws LineUnavailableException {
+ if (bufferSize == -1)
+ bufferSize = ((int) (format.getFrameRate() / 2))
+ * format.getFrameSize();
+ open(format, bufferSize);
+ }
+
+ public void open(AudioFormat format, int bufferSize)
+ throws LineUnavailableException {
+
+ LineEvent event = null;
+
+ if (bufferSize < format.getFrameSize() * 32)
+ bufferSize = format.getFrameSize() * 32;
+
+ synchronized (control_mutex) {
+
+ if (!isOpen()) {
+ if (!mixer.isOpen()) {
+ mixer.open();
+ mixer.implicitOpen = true;
+ }
+
+ event = new LineEvent(this, LineEvent.Type.OPEN, 0);
+
+ this.bufferSize = bufferSize - bufferSize
+ % format.getFrameSize();
+ this.format = format;
+ this.framesize = format.getFrameSize();
+ this.outputformat = mixer.getFormat();
+ out_nrofchannels = outputformat.getChannels();
+ in_nrofchannels = format.getChannels();
+
+ open = true;
+
+ mixer.getMainMixer().openLine(this);
+
+ cycling_buffer = new byte[framesize * bufferSize];
+ cycling_read_pos = 0;
+ cycling_write_pos = 0;
+ cycling_avail = 0;
+ cycling_framepos = 0;
+
+ InputStream cycling_inputstream = new InputStream() {
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ int ret = read(b);
+ if (ret < 0)
+ return ret;
+ return b[0] & 0xFF;
+ }
+
+ public int available() throws IOException {
+ synchronized (cycling_buffer) {
+ return cycling_avail;
+ }
+ }
+
+ public int read(byte[] b, int off, int len)
+ throws IOException {
+
+ synchronized (cycling_buffer) {
+ if (len > cycling_avail)
+ len = cycling_avail;
+ int pos = cycling_read_pos;
+ byte[] buff = cycling_buffer;
+ int buff_len = buff.length;
+ for (int i = 0; i < len; i++) {
+ b[off++] = buff[pos];
+ pos++;
+ if (pos == buff_len)
+ pos = 0;
+ }
+ cycling_read_pos = pos;
+ cycling_avail -= len;
+ cycling_framepos += len / framesize;
+ }
+ return len;
+ }
+
+ };
+
+ afis = AudioFloatInputStream
+ .getInputStream(new AudioInputStream(
+ cycling_inputstream, format,
+ AudioSystem.NOT_SPECIFIED));
+ afis = new NonBlockingFloatInputStream(afis);
+
+ if (Math.abs(format.getSampleRate()
+ - outputformat.getSampleRate()) > 0.000001)
+ afis = new AudioFloatInputStreamResampler(afis,
+ outputformat);
+
+ } else {
+ if (!format.matches(getFormat())) {
+ throw new IllegalStateException(
+ "Line is already open with format " + getFormat()
+ + " and bufferSize " + getBufferSize());
+ }
+ }
+
+ }
+
+ if (event != null)
+ sendEvent(event);
+
+ }
+
+ public int available() {
+ synchronized (cycling_buffer) {
+ return cycling_buffer.length - cycling_avail;
+ }
+ }
+
+ public void drain() {
+ while (true) {
+ int avail;
+ synchronized (cycling_buffer) {
+ avail = cycling_avail;
+ }
+ if (avail != 0)
+ return;
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+
+ public void flush() {
+ synchronized (cycling_buffer) {
+ cycling_read_pos = 0;
+ cycling_write_pos = 0;
+ cycling_avail = 0;
+ }
+ }
+
+ public int getBufferSize() {
+ synchronized (control_mutex) {
+ return bufferSize;
+ }
+ }
+
+ public AudioFormat getFormat() {
+ synchronized (control_mutex) {
+ return format;
+ }
+ }
+
+ public int getFramePosition() {
+ return (int) getLongFramePosition();
+ }
+
+ public float getLevel() {
+ return AudioSystem.NOT_SPECIFIED;
+ }
+
+ public long getLongFramePosition() {
+ synchronized (cycling_buffer) {
+ return cycling_framepos;
+ }
+ }
+
+ public long getMicrosecondPosition() {
+ return (long) (getLongFramePosition() * (1000000.0 / (double) getFormat()
+ .getSampleRate()));
+ }
+
+ public boolean isActive() {
+ synchronized (control_mutex) {
+ return active;
+ }
+ }
+
+ public boolean isRunning() {
+ synchronized (control_mutex) {
+ return active;
+ }
+ }
+
+ public void start() {
+
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (isOpen()) {
+ if (active)
+ return;
+ active = true;
+ event = new LineEvent(this, LineEvent.Type.START,
+ getLongFramePosition());
+ }
+ }
+
+ if (event != null)
+ sendEvent(event);
+ }
+
+ public void stop() {
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (isOpen()) {
+ if (!active)
+ return;
+ active = false;
+ event = new LineEvent(this, LineEvent.Type.STOP,
+ getLongFramePosition());
+ }
+ }
+
+ if (event != null)
+ sendEvent(event);
+ }
+
+ public void close() {
+
+ LineEvent event = null;
+
+ synchronized (control_mutex) {
+ if (!isOpen())
+ return;
+ stop();
+
+ event = new LineEvent(this, LineEvent.Type.CLOSE,
+ getLongFramePosition());
+
+ open = false;
+ mixer.getMainMixer().closeLine(this);
+ }
+
+ if (event != null)
+ sendEvent(event);
+
+ }
+
+ public boolean isOpen() {
+ synchronized (control_mutex) {
+ return open;
+ }
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftPerformer.java b/src/share/classes/com/sun/media/sound/SoftPerformer.java
new file mode 100644
index 000000000..97024f29a
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftPerformer.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class decodes information from ModelPeformer for use in SoftVoice.
+ * It also adds default connections if they where missing in ModelPerformer.
+ *
+ * @author Karl Helgason
+ */
+public class SoftPerformer {
+
+ static ModelConnectionBlock[] defaultconnections
+ = new ModelConnectionBlock[42];
+
+ static {
+ int o = 0;
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("noteon", "on", 0),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1, new ModelDestination(new ModelIdentifier("eg", "on", 0)));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("noteon", "on", 0),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1, new ModelDestination(new ModelIdentifier("eg", "on", 1)));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("eg", "active", 0),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1, new ModelDestination(new ModelIdentifier("mixer", "active", 0)));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("eg", 0),
+ ModelStandardTransform.DIRECTION_MAX2MIN,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("noteon", "velocity"),
+ ModelStandardTransform.DIRECTION_MAX2MIN,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_CONCAVE),
+ -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi", "pitch"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ new ModelSource(new ModelIdentifier("midi_rpn", "0"),
+ new ModelTransform() {
+ public double transform(double value) {
+ int v = (int) (value * 16384.0);
+ int msb = v >> 7;
+ int lsb = v & 127;
+ return msb * 100 + lsb;
+ }
+ }),
+ new ModelDestination(new ModelIdentifier("osc", "pitch")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("noteon", "keynumber"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "7"),
+ ModelStandardTransform.DIRECTION_MAX2MIN,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_CONCAVE),
+ -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "8"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1000, new ModelDestination(new ModelIdentifier("mixer", "balance")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "10"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1000, new ModelDestination(new ModelIdentifier("mixer", "pan")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "11"),
+ ModelStandardTransform.DIRECTION_MAX2MIN,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_CONCAVE),
+ -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "91"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1000, new ModelDestination(new ModelIdentifier("mixer", "reverb")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "93"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 1000, new ModelDestination(new ModelIdentifier("mixer", "chorus")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "71"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 200, new ModelDestination(new ModelIdentifier("filter", "q")));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "74"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 9600, new ModelDestination(new ModelIdentifier("filter", "freq")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "72"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 6000, new ModelDestination(new ModelIdentifier("eg", "release2")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "73"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 2000, new ModelDestination(new ModelIdentifier("eg", "attack2")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "75"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 6000, new ModelDestination(new ModelIdentifier("eg", "decay2")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "67"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_SWITCH),
+ -50, new ModelDestination(ModelDestination.DESTINATION_GAIN));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_cc", "67"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_SWITCH),
+ -2400, new ModelDestination(ModelDestination.DESTINATION_FILTER_FREQ));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_rpn", "1"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 100, new ModelDestination(new ModelIdentifier("osc", "pitch")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("midi_rpn", "2"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("master", "fine_tuning"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 100, new ModelDestination(new ModelIdentifier("osc", "pitch")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ new ModelSource(
+ new ModelIdentifier("master", "coarse_tuning"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
+
+ defaultconnections[o++] = new ModelConnectionBlock(13500,
+ new ModelDestination(new ModelIdentifier("filter", "freq", 0)));
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "delay", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "attack", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "hold", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "decay", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(1000,
+ new ModelDestination(new ModelIdentifier("eg", "sustain", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "release", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(1200.0
+ * Math.log(0.015) / Math.log(2), new ModelDestination(
+ new ModelIdentifier("eg", "shutdown", 0))); // 15 msec default
+
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "delay", 1)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "attack", 1)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "hold", 1)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "decay", 1)));
+ defaultconnections[o++] = new ModelConnectionBlock(1000,
+ new ModelDestination(new ModelIdentifier("eg", "sustain", 1)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("eg", "release", 1)));
+
+ defaultconnections[o++] = new ModelConnectionBlock(-8.51318,
+ new ModelDestination(new ModelIdentifier("lfo", "freq", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("lfo", "delay", 0)));
+ defaultconnections[o++] = new ModelConnectionBlock(-8.51318,
+ new ModelDestination(new ModelIdentifier("lfo", "freq", 1)));
+ defaultconnections[o++] = new ModelConnectionBlock(
+ Float.NEGATIVE_INFINITY, new ModelDestination(
+ new ModelIdentifier("lfo", "delay", 1)));
+
+ }
+ public int keyFrom = 0;
+ public int keyTo = 127;
+ public int velFrom = 0;
+ public int velTo = 127;
+ public int exclusiveClass = 0;
+ public boolean selfNonExclusive = false;
+ public boolean forcedVelocity = false;
+ public boolean forcedKeynumber = false;
+ public ModelPerformer performer;
+ public ModelConnectionBlock[] connections;
+ public ModelOscillator[] oscillators;
+ public Map<Integer, int[]> midi_rpn_connections = new HashMap<Integer, int[]>();
+ public Map<Integer, int[]> midi_nrpn_connections = new HashMap<Integer, int[]>();
+ public int[][] midi_ctrl_connections;
+ public int[][] midi_connections;
+ public int[] ctrl_connections;
+ private List<Integer> ctrl_connections_list = new ArrayList<Integer>();
+
+ private static class KeySortComparator implements Comparator<ModelSource> {
+
+ public int compare(ModelSource o1, ModelSource o2) {
+ return o1.getIdentifier().toString().compareTo(
+ o2.getIdentifier().toString());
+ }
+ }
+ private static KeySortComparator keySortComparator = new KeySortComparator();
+
+ private String extractKeys(ModelConnectionBlock conn) {
+ StringBuffer sb = new StringBuffer();
+ if (conn.getSources() != null) {
+ sb.append("[");
+ ModelSource[] srcs = conn.getSources();
+ ModelSource[] srcs2 = new ModelSource[srcs.length];
+ for (int i = 0; i < srcs.length; i++)
+ srcs2[i] = srcs[i];
+ Arrays.sort(srcs2, keySortComparator);
+ for (int i = 0; i < srcs.length; i++) {
+ sb.append(srcs[i].getIdentifier());
+ sb.append(";");
+ }
+ sb.append("]");
+ }
+ sb.append(";");
+ if (conn.getDestination() != null) {
+ sb.append(conn.getDestination().getIdentifier());
+ }
+ sb.append(";");
+ return sb.toString();
+ }
+
+ private void processSource(ModelSource src, int ix) {
+ ModelIdentifier id = src.getIdentifier();
+ String o = id.getObject();
+ if (o.equals("midi_cc"))
+ processMidiControlSource(src, ix);
+ else if (o.equals("midi_rpn"))
+ processMidiRpnSource(src, ix);
+ else if (o.equals("midi_nrpn"))
+ processMidiNrpnSource(src, ix);
+ else if (o.equals("midi"))
+ processMidiSource(src, ix);
+ else if (o.equals("noteon"))
+ processNoteOnSource(src, ix);
+ else if (o.equals("osc"))
+ return;
+ else if (o.equals("mixer"))
+ return;
+ else
+ ctrl_connections_list.add(ix);
+ }
+
+ private void processMidiControlSource(ModelSource src, int ix) {
+ String v = src.getIdentifier().getVariable();
+ if (v == null)
+ return;
+ int c = Integer.parseInt(v);
+ if (midi_ctrl_connections[c] == null)
+ midi_ctrl_connections[c] = new int[]{ix};
+ else {
+ int[] olda = midi_ctrl_connections[c];
+ int[] newa = new int[olda.length + 1];
+ for (int i = 0; i < olda.length; i++)
+ newa[i] = olda[i];
+ newa[newa.length - 1] = ix;
+ midi_ctrl_connections[c] = newa;
+ }
+ }
+
+ private void processNoteOnSource(ModelSource src, int ix) {
+ String v = src.getIdentifier().getVariable();
+ int c = -1;
+ if (v.equals("on"))
+ c = 3;
+ if (v.equals("keynumber"))
+ c = 4;
+ if (c == -1)
+ return;
+ if (midi_connections[c] == null)
+ midi_connections[c] = new int[]{ix};
+ else {
+ int[] olda = midi_connections[c];
+ int[] newa = new int[olda.length + 1];
+ for (int i = 0; i < olda.length; i++)
+ newa[i] = olda[i];
+ newa[newa.length - 1] = ix;
+ midi_connections[c] = newa;
+ }
+ }
+
+ private void processMidiSource(ModelSource src, int ix) {
+ String v = src.getIdentifier().getVariable();
+ int c = -1;
+ if (v.equals("pitch"))
+ c = 0;
+ if (v.equals("channel_pressure"))
+ c = 1;
+ if (v.equals("poly_pressure"))
+ c = 2;
+ if (c == -1)
+ return;
+ if (midi_connections[c] == null)
+ midi_connections[c] = new int[]{ix};
+ else {
+ int[] olda = midi_connections[c];
+ int[] newa = new int[olda.length + 1];
+ for (int i = 0; i < olda.length; i++)
+ newa[i] = olda[i];
+ newa[newa.length - 1] = ix;
+ midi_connections[c] = newa;
+ }
+ }
+
+ private void processMidiRpnSource(ModelSource src, int ix) {
+ String v = src.getIdentifier().getVariable();
+ if (v == null)
+ return;
+ int c = Integer.parseInt(v);
+ if (midi_rpn_connections.get(c) == null)
+ midi_rpn_connections.put(c, new int[]{ix});
+ else {
+ int[] olda = midi_rpn_connections.get(c);
+ int[] newa = new int[olda.length + 1];
+ for (int i = 0; i < olda.length; i++)
+ newa[i] = olda[i];
+ newa[newa.length - 1] = ix;
+ midi_rpn_connections.put(c, newa);
+ }
+ }
+
+ private void processMidiNrpnSource(ModelSource src, int ix) {
+ String v = src.getIdentifier().getVariable();
+ if (v == null)
+ return;
+ int c = Integer.parseInt(v);
+ if (midi_nrpn_connections.get(c) == null)
+ midi_nrpn_connections.put(c, new int[]{ix});
+ else {
+ int[] olda = midi_nrpn_connections.get(c);
+ int[] newa = new int[olda.length + 1];
+ for (int i = 0; i < olda.length; i++)
+ newa[i] = olda[i];
+ newa[newa.length - 1] = ix;
+ midi_nrpn_connections.put(c, newa);
+ }
+ }
+
+ public SoftPerformer(ModelPerformer performer) {
+ this.performer = performer;
+
+ keyFrom = performer.getKeyFrom();
+ keyTo = performer.getKeyTo();
+ velFrom = performer.getVelFrom();
+ velTo = performer.getVelTo();
+ exclusiveClass = performer.getExclusiveClass();
+ selfNonExclusive = performer.isSelfNonExclusive();
+
+ Map<String, ModelConnectionBlock> connmap = new HashMap<String, ModelConnectionBlock>();
+
+ List<ModelConnectionBlock> performer_connections = new ArrayList<ModelConnectionBlock>();
+ performer_connections.addAll(performer.getConnectionBlocks());
+
+ if (performer.isDefaultConnectionsEnabled()) {
+
+ // Add modulation depth range (RPN 5) to the modulation wheel (cc#1)
+
+ boolean isModulationWheelConectionFound = false;
+ for (int j = 0; j < performer_connections.size(); j++) {
+ ModelConnectionBlock connection = performer_connections.get(j);
+ ModelSource[] sources = connection.getSources();
+ ModelDestination dest = connection.getDestination();
+ boolean isModulationWheelConection = false;
+ if (dest != null && sources != null && sources.length > 1) {
+ for (int i = 0; i < sources.length; i++) {
+ // check if connection block has the source "modulation
+ // wheel cc#1"
+ if (sources[i].getIdentifier().getObject().equals(
+ "midi_cc")) {
+ if (sources[i].getIdentifier().getVariable()
+ .equals("1")) {
+ isModulationWheelConection = true;
+ isModulationWheelConectionFound = true;
+ break;
+ }
+ }
+ }
+ }
+ if (isModulationWheelConection) {
+
+ ModelConnectionBlock newconnection = new ModelConnectionBlock();
+ newconnection.setSources(connection.getSources());
+ newconnection.setDestination(connection.getDestination());
+ newconnection.addSource(new ModelSource(
+ new ModelIdentifier("midi_rpn", "5")));
+ newconnection.setScale(connection.getScale() * 256.0);
+ performer_connections.set(j, newconnection);
+ }
+ }
+
+ if (!isModulationWheelConectionFound) {
+ ModelConnectionBlock conn = new ModelConnectionBlock(
+ new ModelSource(ModelSource.SOURCE_LFO1,
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ new ModelSource(new ModelIdentifier("midi_cc", "1", 0),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_UNIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 50,
+ new ModelDestination(ModelDestination.DESTINATION_PITCH));
+ conn.addSource(new ModelSource(new ModelIdentifier("midi_rpn",
+ "5")));
+ conn.setScale(conn.getScale() * 256.0);
+ performer_connections.add(conn);
+
+ }
+
+ // Let Aftertouch to behave just like modulation wheel (cc#1)
+ boolean channel_pressure_set = false;
+ boolean poly_pressure = false;
+ ModelConnectionBlock mod_cc_1_connection = null;
+ int mod_cc_1_connection_src_ix = 0;
+
+ for (ModelConnectionBlock connection : performer_connections) {
+ ModelSource[] sources = connection.getSources();
+ ModelDestination dest = connection.getDestination();
+ // if(dest != null && sources != null)
+ if (dest != null && sources != null) {
+ for (int i = 0; i < sources.length; i++) {
+ ModelIdentifier srcid = sources[i].getIdentifier();
+ // check if connection block has the source "modulation
+ // wheel cc#1"
+ if (srcid.getObject().equals("midi_cc")) {
+ if (srcid.getVariable().equals("1")) {
+ mod_cc_1_connection = connection;
+ mod_cc_1_connection_src_ix = i;
+ }
+ }
+ // check if channel or poly pressure are already
+ // connected
+ if (srcid.getObject().equals("midi")) {
+ if (srcid.getVariable().equals("channel_pressure"))
+ channel_pressure_set = true;
+ if (srcid.getVariable().equals("poly_pressure"))
+ poly_pressure = true;
+ }
+ }
+ }
+
+ }
+
+ if (mod_cc_1_connection != null) {
+ if (!channel_pressure_set) {
+ ModelConnectionBlock mc = new ModelConnectionBlock();
+ mc.setDestination(mod_cc_1_connection.getDestination());
+ mc.setScale(mod_cc_1_connection.getScale());
+ ModelSource[] src_list = mod_cc_1_connection.getSources();
+ ModelSource[] src_list_new = new ModelSource[src_list.length];
+ for (int i = 0; i < src_list_new.length; i++)
+ src_list_new[i] = src_list[i];
+ src_list_new[mod_cc_1_connection_src_ix] = new ModelSource(
+ new ModelIdentifier("midi", "channel_pressure"));
+ mc.setSources(src_list_new);
+ connmap.put(extractKeys(mc), mc);
+ }
+ if (!poly_pressure) {
+ ModelConnectionBlock mc = new ModelConnectionBlock();
+ mc.setDestination(mod_cc_1_connection.getDestination());
+ mc.setScale(mod_cc_1_connection.getScale());
+ ModelSource[] src_list = mod_cc_1_connection.getSources();
+ ModelSource[] src_list_new = new ModelSource[src_list.length];
+ for (int i = 0; i < src_list_new.length; i++)
+ src_list_new[i] = src_list[i];
+ src_list_new[mod_cc_1_connection_src_ix] = new ModelSource(
+ new ModelIdentifier("midi", "poly_pressure"));
+ mc.setSources(src_list_new);
+ connmap.put(extractKeys(mc), mc);
+ }
+ }
+
+ // Enable Vibration Sound Controllers : 76, 77, 78
+ ModelConnectionBlock found_vib_connection = null;
+ for (ModelConnectionBlock connection : performer_connections) {
+ ModelSource[] sources = connection.getSources();
+ if (sources.length != 0
+ && sources[0].getIdentifier().getObject().equals("lfo")) {
+ if (connection.getDestination().getIdentifier().equals(
+ ModelDestination.DESTINATION_PITCH)) {
+ if (found_vib_connection == null)
+ found_vib_connection = connection;
+ else {
+ if (found_vib_connection.getSources().length > sources.length)
+ found_vib_connection = connection;
+ else if (found_vib_connection.getSources()[0]
+ .getIdentifier().getInstance() < 1) {
+ if (found_vib_connection.getSources()[0]
+ .getIdentifier().getInstance() >
+ sources[0].getIdentifier().getInstance()) {
+ found_vib_connection = connection;
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ int instance = 1;
+
+ if (found_vib_connection != null) {
+ instance = found_vib_connection.getSources()[0].getIdentifier()
+ .getInstance();
+ }
+ ModelConnectionBlock connection;
+
+ connection = new ModelConnectionBlock(
+ new ModelSource(new ModelIdentifier("midi_cc", "78"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 2000, new ModelDestination(
+ new ModelIdentifier("lfo", "delay2", instance)));
+ connmap.put(extractKeys(connection), connection);
+
+ final double scale = found_vib_connection == null ? 0
+ : found_vib_connection.getScale();
+ connection = new ModelConnectionBlock(
+ new ModelSource(new ModelIdentifier("lfo", instance)),
+ new ModelSource(new ModelIdentifier("midi_cc", "77"),
+ new ModelTransform() {
+ double s = scale;
+ public double transform(double value) {
+ value = value * 2 - 1;
+ value *= 600;
+ if (s == 0) {
+ return value;
+ } else if (s > 0) {
+ if (value < -s)
+ value = -s;
+ return value;
+ } else {
+ if (value < s)
+ value = -s;
+ return -value;
+ }
+ }
+ }), new ModelDestination(ModelDestination.DESTINATION_PITCH));
+ connmap.put(extractKeys(connection), connection);
+
+ connection = new ModelConnectionBlock(
+ new ModelSource(new ModelIdentifier("midi_cc", "76"),
+ ModelStandardTransform.DIRECTION_MIN2MAX,
+ ModelStandardTransform.POLARITY_BIPOLAR,
+ ModelStandardTransform.TRANSFORM_LINEAR),
+ 2400, new ModelDestination(
+ new ModelIdentifier("lfo", "freq", instance)));
+ connmap.put(extractKeys(connection), connection);
+
+ }
+
+ // Add default connection blocks
+ if (performer.isDefaultConnectionsEnabled())
+ for (ModelConnectionBlock connection : defaultconnections)
+ connmap.put(extractKeys(connection), connection);
+ // Add connection blocks from modelperformer
+ for (ModelConnectionBlock connection : performer_connections)
+ connmap.put(extractKeys(connection), connection);
+ // seperate connection blocks : Init time, Midi Time, Midi/Control Time,
+ // Control Time
+ List<ModelConnectionBlock> connections = new ArrayList<ModelConnectionBlock>();
+
+ midi_ctrl_connections = new int[128][];
+ for (int i = 0; i < midi_ctrl_connections.length; i++) {
+ midi_ctrl_connections[i] = null;
+ }
+ midi_connections = new int[5][];
+ for (int i = 0; i < midi_connections.length; i++) {
+ midi_connections[i] = null;
+ }
+
+ int ix = 0;
+ boolean mustBeOnTop = false;
+
+ for (ModelConnectionBlock connection : connmap.values()) {
+ if (connection.getDestination() != null) {
+ ModelDestination dest = connection.getDestination();
+ ModelIdentifier id = dest.getIdentifier();
+ if (id.getObject().equals("noteon")) {
+ mustBeOnTop = true;
+ if (id.getVariable().equals("keynumber"))
+ forcedKeynumber = true;
+ if (id.getVariable().equals("velocity"))
+ forcedVelocity = true;
+ }
+ }
+ if (mustBeOnTop) {
+ connections.add(0, connection);
+ mustBeOnTop = false;
+ } else
+ connections.add(connection);
+ }
+
+ for (ModelConnectionBlock connection : connections) {
+ if (connection.getSources() != null) {
+ ModelSource[] srcs = connection.getSources();
+ for (int i = 0; i < srcs.length; i++) {
+ processSource(srcs[i], ix);
+ }
+ }
+ ix++;
+ }
+
+ this.connections = new ModelConnectionBlock[connections.size()];
+ connections.toArray(this.connections);
+
+ this.ctrl_connections = new int[ctrl_connections_list.size()];
+
+ for (int i = 0; i < this.ctrl_connections.length; i++)
+ this.ctrl_connections[i] = ctrl_connections_list.get(i);
+
+ oscillators = new ModelOscillator[performer.getOscillators().size()];
+ performer.getOscillators().toArray(oscillators);
+
+ for (ModelConnectionBlock conn : connections) {
+ if (conn.getDestination() != null) {
+ if (isUnnecessaryTransform(conn.getDestination().getTransform())) {
+ conn.getDestination().setTransform(null);
+ }
+ }
+ if (conn.getSources() != null) {
+ for (ModelSource src : conn.getSources()) {
+ if (isUnnecessaryTransform(src.getTransform())) {
+ src.setTransform(null);
+ }
+ }
+ }
+ }
+
+ }
+
+ private static boolean isUnnecessaryTransform(ModelTransform transform) {
+ if (transform == null)
+ return false;
+ if (!(transform instanceof ModelStandardTransform))
+ return false;
+ ModelStandardTransform stransform = (ModelStandardTransform)transform;
+ if (stransform.getDirection() != ModelStandardTransform.DIRECTION_MIN2MAX)
+ return false;
+ if (stransform.getPolarity() != ModelStandardTransform.POLARITY_UNIPOLAR)
+ return false;
+ if (stransform.getTransform() != ModelStandardTransform.TRANSFORM_LINEAR)
+ return false;
+ return false;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftPointResampler.java b/src/share/classes/com/sun/media/sound/SoftPointResampler.java
new file mode 100644
index 000000000..56849ad4c
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftPointResampler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * A resampler that uses 0-order (nearest-neighbor) interpolation.
+ *
+ * @author Karl Helgason
+ */
+public class SoftPointResampler extends SoftAbstractResampler {
+
+ public int getPadding() {
+ return 100;
+ }
+
+ public void interpolate(float[] in, float[] in_offset, float in_end,
+ float[] startpitch, float pitchstep, float[] out, int[] out_offset,
+ int out_end) {
+ float pitch = startpitch[0];
+ float ix = in_offset[0];
+ int ox = out_offset[0];
+ float ix_end = in_end;
+ float ox_end = out_end;
+ if (pitchstep == 0) {
+ while (ix < ix_end && ox < ox_end) {
+ out[ox++] = in[(int) ix];
+ ix += pitch;
+ }
+ } else {
+ while (ix < ix_end && ox < ox_end) {
+ out[ox++] = in[(int) ix];
+ ix += pitch;
+ pitch += pitchstep;
+ }
+ }
+ in_offset[0] = ix;
+ out_offset[0] = ox;
+ startpitch[0] = pitch;
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftProcess.java b/src/share/classes/com/sun/media/sound/SoftProcess.java
new file mode 100644
index 000000000..9b08ab068
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftProcess.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Control signal processor interface
+ *
+ * @author Karl Helgason
+ */
+public interface SoftProcess extends SoftControl {
+
+ public void init(SoftSynthesizer synth);
+
+ public double[] get(int instance, String name);
+
+ public void processControlLogic();
+
+ public void reset();
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftProvider.java b/src/share/classes/com/sun/media/sound/SoftProvider.java
new file mode 100644
index 000000000..9472d52d2
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiDevice.Info;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+/**
+ * Software synthesizer provider class.
+ *
+ * @author Karl Helgason
+ */
+public class SoftProvider extends MidiDeviceProvider {
+
+ protected final static Info softinfo = SoftSynthesizer.info;
+ private static Info[] softinfos = {softinfo};
+
+ public MidiDevice.Info[] getDeviceInfo() {
+ return softinfos;
+ }
+
+ public MidiDevice getDevice(MidiDevice.Info info) {
+ if (info == softinfo) {
+ return new SoftSynthesizer();
+ }
+ return null;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftReceiver.java b/src/share/classes/com/sun/media/sound/SoftReceiver.java
new file mode 100644
index 000000000..e7f1edcfa
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.TreeMap;
+
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.ShortMessage;
+
+/**
+ * Software synthesizer MIDI receiver class.
+ *
+ * @author Karl Helgason
+ */
+public class SoftReceiver implements Receiver {
+
+ protected boolean open = true;
+ private Object control_mutex;
+ private SoftSynthesizer synth;
+ protected TreeMap<Long, Object> midimessages;
+ protected SoftMainMixer mainmixer;
+
+ public SoftReceiver(SoftSynthesizer synth) {
+ this.control_mutex = synth.control_mutex;
+ this.synth = synth;
+ this.mainmixer = synth.getMainMixer();
+ if (mainmixer != null)
+ this.midimessages = mainmixer.midimessages;
+ }
+
+ public void send(MidiMessage message, long timeStamp) {
+
+ synchronized (control_mutex) {
+ if (!open)
+ throw new IllegalStateException("Receiver is not open");
+ }
+
+ if (timeStamp != -1) {
+ synchronized (control_mutex) {
+ while (midimessages.get(timeStamp) != null)
+ timeStamp++;
+ if (message instanceof ShortMessage
+ && (((ShortMessage)message).getChannel() > 0xF)) {
+ midimessages.put(timeStamp, message.clone());
+ } else {
+ midimessages.put(timeStamp, message.getMessage());
+ }
+ }
+ } else {
+ mainmixer.processMessage(message);
+ }
+ }
+
+ public void close() {
+ synchronized (control_mutex) {
+ open = false;
+ }
+ synth.removeReceiver(this);
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftResampler.java b/src/share/classes/com/sun/media/sound/SoftResampler.java
new file mode 100644
index 000000000..887f40845
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftResampler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Basic resampler interface.
+ *
+ * @author Karl Helgason
+ */
+public interface SoftResampler {
+
+ public SoftResamplerStreamer openStreamer();
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftResamplerStreamer.java b/src/share/classes/com/sun/media/sound/SoftResamplerStreamer.java
new file mode 100644
index 000000000..1662fceae
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftResamplerStreamer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+
+/**
+ * Resampler stream interface.
+ *
+ * @author Karl Helgason
+ */
+public interface SoftResamplerStreamer extends ModelOscillatorStream {
+
+ public void open(ModelWavetable osc, float outputsamplerate)
+ throws IOException;
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftReverb.java b/src/share/classes/com/sun/media/sound/SoftReverb.java
new file mode 100644
index 000000000..7dcb77252
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftReverb.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.util.Arrays;
+
+/**
+ * Reverb effect based on allpass/comb filters. First audio is send to 8
+ * parelled comb filters and then mixed together and then finally send thru 3
+ * different allpass filters.
+ *
+ * @author Karl Helgason
+ */
+public class SoftReverb implements SoftAudioProcessor {
+
+ private final static class Delay {
+
+ private float[] delaybuffer;
+ private int rovepos = 0;
+
+ public Delay() {
+ delaybuffer = null;
+ }
+
+ public void setDelay(int delay) {
+ if (delay == 0)
+ delaybuffer = null;
+ else
+ delaybuffer = new float[delay];
+ rovepos = 0;
+ }
+
+ public void processReplace(float[] inout) {
+ if (delaybuffer == null)
+ return;
+ int len = inout.length;
+ int rnlen = delaybuffer.length;
+ int rovepos = this.rovepos;
+
+ for (int i = 0; i < len; i++) {
+ float x = inout[i];
+ inout[i] = delaybuffer[rovepos];
+ delaybuffer[rovepos] = x;
+ if (++rovepos == rnlen)
+ rovepos = 0;
+ }
+ this.rovepos = rovepos;
+ }
+ }
+
+ private final static class AllPass {
+
+ private final float[] delaybuffer;
+ private final int delaybuffersize;
+ private int rovepos = 0;
+ private float feedback;
+
+ public AllPass(int size) {
+ delaybuffer = new float[size];
+ delaybuffersize = size;
+ }
+
+ public void setFeedBack(float feedback) {
+ this.feedback = feedback;
+ }
+
+ public void processReplace(float inout[]) {
+ int len = inout.length;
+ int delaybuffersize = this.delaybuffersize;
+ int rovepos = this.rovepos;
+ for (int i = 0; i < len; i++) {
+ float delayout = delaybuffer[rovepos];
+ float input = inout[i];
+ inout[i] = delayout - input;
+ delaybuffer[rovepos] = input + delayout * feedback;
+ if (++rovepos == delaybuffersize)
+ rovepos = 0;
+ }
+ this.rovepos = rovepos;
+ }
+
+ public void processReplace(float in[], float out[]) {
+ int len = in.length;
+ int delaybuffersize = this.delaybuffersize;
+ int rovepos = this.rovepos;
+ for (int i = 0; i < len; i++) {
+ float delayout = delaybuffer[rovepos];
+ float input = in[i];
+ out[i] = delayout - input;
+ delaybuffer[rovepos] = input + delayout * feedback;
+ if (++rovepos == delaybuffersize)
+ rovepos = 0;
+ }
+ this.rovepos = rovepos;
+ }
+ }
+
+ private final static class Comb {
+
+ private final float[] delaybuffer;
+ private final int delaybuffersize;
+ private int rovepos = 0;
+ private float feedback;
+ private float filtertemp = 0;
+ private float filtercoeff1 = 0;
+ private float filtercoeff2 = 1;
+
+ public Comb(int size) {
+ delaybuffer = new float[size];
+ delaybuffersize = size;
+ }
+
+ public void setFeedBack(float feedback) {
+ this.feedback = feedback;
+ filtercoeff2 = (1 - filtercoeff1)* feedback;
+ }
+
+ public void processMix(float in[], float out[]) {
+ int len = in.length;
+ int delaybuffersize = this.delaybuffersize;
+ int rovepos = this.rovepos;
+ float filtertemp = this.filtertemp;
+ float filtercoeff1 = this.filtercoeff1;
+ float filtercoeff2 = this.filtercoeff2;
+ for (int i = 0; i < len; i++) {
+ float delayout = delaybuffer[rovepos];
+ // One Pole Lowpass Filter
+ filtertemp = (delayout * filtercoeff2)
+ + (filtertemp * filtercoeff1);
+ out[i] += delayout;
+ delaybuffer[rovepos] = in[i] + filtertemp;
+ if (++rovepos == delaybuffersize)
+ rovepos = 0;
+ }
+ this.filtertemp = filtertemp;
+ this.rovepos = rovepos;
+ }
+
+ public void processReplace(float in[], float out[]) {
+ int len = in.length;
+ int delaybuffersize = this.delaybuffersize;
+ int rovepos = this.rovepos;
+ float filtertemp = this.filtertemp;
+ float filtercoeff1 = this.filtercoeff1;
+ float filtercoeff2 = this.filtercoeff2;
+ for (int i = 0; i < len; i++) {
+ float delayout = delaybuffer[rovepos];
+ // One Pole Lowpass Filter
+ filtertemp = (delayout * filtercoeff2)
+ + (filtertemp * filtercoeff1);
+ out[i] = delayout;
+ delaybuffer[rovepos] = in[i] + filtertemp;
+ if (++rovepos == delaybuffersize)
+ rovepos = 0;
+ }
+ this.filtertemp = filtertemp;
+ this.rovepos = rovepos;
+ }
+
+ public void setDamp(float val) {
+ filtercoeff1 = val;
+ filtercoeff2 = (1 - filtercoeff1)* feedback;
+ }
+ }
+ private float roomsize;
+ private float damp;
+ private float gain = 1;
+ private Delay delay;
+ private Comb[] combL;
+ private Comb[] combR;
+ private AllPass[] allpassL;
+ private AllPass[] allpassR;
+ private float[] input;
+ private float[] out;
+ private float[] pre1;
+ private float[] pre2;
+ private float[] pre3;
+ private boolean denormal_flip = false;
+ private boolean mix = true;
+ private SoftAudioBuffer inputA;
+ private SoftAudioBuffer left;
+ private SoftAudioBuffer right;
+ private boolean dirty = true;
+ private float dirty_roomsize;
+ private float dirty_damp;
+ private float dirty_predelay;
+ private float dirty_gain;
+ private float samplerate;
+ private boolean light = true;
+
+ public void init(float samplerate, float controlrate) {
+ this.samplerate = samplerate;
+
+ double freqscale = ((double) samplerate) / 44100.0;
+ // freqscale = 1.0/ freqscale;
+
+ int stereospread = 23;
+
+ delay = new Delay();
+
+ combL = new Comb[8];
+ combR = new Comb[8];
+ combL[0] = new Comb((int) (freqscale * (1116)));
+ combR[0] = new Comb((int) (freqscale * (1116 + stereospread)));
+ combL[1] = new Comb((int) (freqscale * (1188)));
+ combR[1] = new Comb((int) (freqscale * (1188 + stereospread)));
+ combL[2] = new Comb((int) (freqscale * (1277)));
+ combR[2] = new Comb((int) (freqscale * (1277 + stereospread)));
+ combL[3] = new Comb((int) (freqscale * (1356)));
+ combR[3] = new Comb((int) (freqscale * (1356 + stereospread)));
+ combL[4] = new Comb((int) (freqscale * (1422)));
+ combR[4] = new Comb((int) (freqscale * (1422 + stereospread)));
+ combL[5] = new Comb((int) (freqscale * (1491)));
+ combR[5] = new Comb((int) (freqscale * (1491 + stereospread)));
+ combL[6] = new Comb((int) (freqscale * (1557)));
+ combR[6] = new Comb((int) (freqscale * (1557 + stereospread)));
+ combL[7] = new Comb((int) (freqscale * (1617)));
+ combR[7] = new Comb((int) (freqscale * (1617 + stereospread)));
+
+ allpassL = new AllPass[4];
+ allpassR = new AllPass[4];
+ allpassL[0] = new AllPass((int) (freqscale * (556)));
+ allpassR[0] = new AllPass((int) (freqscale * (556 + stereospread)));
+ allpassL[1] = new AllPass((int) (freqscale * (441)));
+ allpassR[1] = new AllPass((int) (freqscale * (441 + stereospread)));
+ allpassL[2] = new AllPass((int) (freqscale * (341)));
+ allpassR[2] = new AllPass((int) (freqscale * (341 + stereospread)));
+ allpassL[3] = new AllPass((int) (freqscale * (225)));
+ allpassR[3] = new AllPass((int) (freqscale * (225 + stereospread)));
+
+ for (int i = 0; i < allpassL.length; i++) {
+ allpassL[i].setFeedBack(0.5f);
+ allpassR[i].setFeedBack(0.5f);
+ }
+
+ /* Init other settings */
+ globalParameterControlChange(new int[]{0x01 * 128 + 0x01}, 0, 4);
+
+ }
+
+ public void setInput(int pin, SoftAudioBuffer input) {
+ if (pin == 0)
+ inputA = input;
+ }
+
+ public void setOutput(int pin, SoftAudioBuffer output) {
+ if (pin == 0)
+ left = output;
+ if (pin == 1)
+ right = output;
+ }
+
+ public void setMixMode(boolean mix) {
+ this.mix = mix;
+ }
+
+ private boolean silent = true;
+
+ public void processAudio() {
+ boolean silent_input = this.inputA.isSilent();
+ if(!silent_input)
+ silent = false;
+ if(silent)
+ {
+ if (!mix) {
+ left.clear();
+ right.clear();
+ }
+ return;
+ }
+
+ float[] inputA = this.inputA.array();
+ float[] left = this.left.array();
+ float[] right = this.right == null ? null : this.right.array();
+
+ int numsamples = inputA.length;
+ if (input == null || input.length < numsamples)
+ input = new float[numsamples];
+
+ float again = gain * 0.018f / 2;
+
+ denormal_flip = !denormal_flip;
+ if(denormal_flip)
+ for (int i = 0; i < numsamples; i++)
+ input[i] = inputA[i] * again + 1E-20f;
+ else
+ for (int i = 0; i < numsamples; i++)
+ input[i] = inputA[i] * again - 1E-20f;
+
+ delay.processReplace(input);
+
+ if(light && (right != null))
+ {
+ if (pre1 == null || pre1.length < numsamples)
+ {
+ pre1 = new float[numsamples];
+ pre2 = new float[numsamples];
+ pre3 = new float[numsamples];
+ }
+
+ for (int i = 0; i < allpassL.length; i++)
+ allpassL[i].processReplace(input);
+
+ combL[0].processReplace(input, pre3);
+ combL[1].processReplace(input, pre3);
+
+ combL[2].processReplace(input, pre1);
+ for (int i = 4; i < combL.length-2; i+=2)
+ combL[i].processMix(input, pre1);
+
+ combL[3].processReplace(input, pre2);;
+ for (int i = 5; i < combL.length-2; i+=2)
+ combL[i].processMix(input, pre2);
+
+ if (!mix)
+ {
+ Arrays.fill(right, 0);
+ Arrays.fill(left, 0);
+ }
+ for (int i = combR.length-2; i < combR.length; i++)
+ combR[i].processMix(input, right);
+ for (int i = combL.length-2; i < combL.length; i++)
+ combL[i].processMix(input, left);
+
+ for (int i = 0; i < numsamples; i++)
+ {
+ float p = pre1[i] - pre2[i];
+ float m = pre3[i];
+ left[i] += m + p;
+ right[i] += m - p;
+ }
+ }
+ else
+ {
+ if (out == null || out.length < numsamples)
+ out = new float[numsamples];
+
+ if (right != null) {
+ if (!mix)
+ Arrays.fill(right, 0);
+ allpassR[0].processReplace(input, out);
+ for (int i = 1; i < allpassR.length; i++)
+ allpassR[i].processReplace(out);
+ for (int i = 0; i < combR.length; i++)
+ combR[i].processMix(out, right);
+ }
+
+ if (!mix)
+ Arrays.fill(left, 0);
+ allpassL[0].processReplace(input, out);
+ for (int i = 1; i < allpassL.length; i++)
+ allpassL[i].processReplace(out);
+ for (int i = 0; i < combL.length; i++)
+ combL[i].processMix(out, left);
+ }
+
+
+
+
+
+
+ if (silent_input) {
+ silent = true;
+ for (int i = 0; i < numsamples; i++)
+ {
+ float v = left[i];
+ if(v > 1E-10 || v < -1E-10)
+ {
+ silent = false;
+ break;
+ }
+ }
+ }
+
+ }
+
+ public void globalParameterControlChange(int[] slothpath, long param,
+ long value) {
+ if (slothpath.length == 1) {
+ if (slothpath[0] == 0x01 * 128 + 0x01) {
+
+ if (param == 0) {
+ if (value == 0) {
+ // Small Room A small size room with a length
+ // of 5m or so.
+ dirty_roomsize = (1.1f);
+ dirty_damp = (5000);
+ dirty_predelay = (0);
+ dirty_gain = (4);
+ dirty = true;
+ }
+ if (value == 1) {
+ // Medium Room A medium size room with a length
+ // of 10m or so.
+ dirty_roomsize = (1.3f);
+ dirty_damp = (5000);
+ dirty_predelay = (0);
+ dirty_gain = (3);
+ dirty = true;
+ }
+ if (value == 2) {
+ // Large Room A large size room suitable for
+ // live performances.
+ dirty_roomsize = (1.5f);
+ dirty_damp = (5000);
+ dirty_predelay = (0);
+ dirty_gain = (2);
+ dirty = true;
+ }
+ if (value == 3) {
+ // Medium Hall A medium size concert hall.
+ dirty_roomsize = (1.8f);
+ dirty_damp = (24000);
+ dirty_predelay = (0.02f);
+ dirty_gain = (1.5f);
+ dirty = true;
+ }
+ if (value == 4) {
+ // Large Hall A large size concert hall
+ // suitable for a full orchestra.
+ dirty_roomsize = (1.8f);
+ dirty_damp = (24000);
+ dirty_predelay = (0.03f);
+ dirty_gain = (1.5f);
+ dirty = true;
+ }
+ if (value == 8) {
+ // Plate A plate reverb simulation.
+ dirty_roomsize = (1.3f);
+ dirty_damp = (2500);
+ dirty_predelay = (0);
+ dirty_gain = (6);
+ dirty = true;
+ }
+ } else if (param == 1) {
+ dirty_roomsize = ((float) (Math.exp((value - 40) * 0.025)));
+ dirty = true;
+ }
+
+ }
+ }
+ }
+
+ public void processControlLogic() {
+ if (dirty) {
+ dirty = false;
+ setRoomSize(dirty_roomsize);
+ setDamp(dirty_damp);
+ setPreDelay(dirty_predelay);
+ setGain(dirty_gain);
+ }
+ }
+
+ public void setRoomSize(float value) {
+ roomsize = 1 - (0.17f / value);
+
+ for (int i = 0; i < combL.length; i++) {
+ combL[i].feedback = roomsize;
+ combR[i].feedback = roomsize;
+ }
+ }
+
+ public void setPreDelay(float value) {
+ delay.setDelay((int)(value * samplerate));
+ }
+
+ public void setGain(float gain) {
+ this.gain = gain;
+ }
+
+ public void setDamp(float value) {
+ double x = (value / samplerate) * (2 * Math.PI);
+ double cx = 2 - Math.cos(x);
+ damp = (float)(cx - Math.sqrt(cx * cx - 1));
+ if (damp > 1)
+ damp = 1;
+ if (damp < 0)
+ damp = 0;
+
+ // damp = value * 0.4f;
+ for (int i = 0; i < combL.length; i++) {
+ combL[i].setDamp(damp);
+ combR[i].setDamp(damp);
+ }
+
+ }
+
+ public void setLightMode(boolean light)
+ {
+ this.light = light;
+ }
+}
+
diff --git a/src/share/classes/com/sun/media/sound/SoftShortMessage.java b/src/share/classes/com/sun/media/sound/SoftShortMessage.java
new file mode 100644
index 000000000..9d16e82ac
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftShortMessage.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.ShortMessage;
+
+/**
+ * A short message class that support for than 16 midi channels.
+ *
+ * @author Karl Helgason
+ */
+public class SoftShortMessage extends ShortMessage {
+
+ int channel = 0;
+
+ public int getChannel() {
+ return channel;
+ }
+
+ public void setMessage(int command, int channel, int data1, int data2)
+ throws InvalidMidiDataException {
+ this.channel = channel;
+ super.setMessage(command, channel & 0xF, data1, data2);
+ }
+
+ public Object clone() {
+ SoftShortMessage clone = new SoftShortMessage();
+ try {
+ clone.setMessage(getCommand(), getChannel(), getData1(), getData2());
+ } catch (InvalidMidiDataException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return clone;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftSincResampler.java b/src/share/classes/com/sun/media/sound/SoftSincResampler.java
new file mode 100644
index 000000000..e4e039214
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftSincResampler.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+/**
+ * Hann windowed sinc interpolation resampler with anti-alias filtering.
+ *
+ * Using 30 points for the interpolation.
+ *
+ * @author Karl Helgason
+ */
+public class SoftSincResampler extends SoftAbstractResampler {
+
+ float[][][] sinc_table;
+ int sinc_scale_size = 100;
+ int sinc_table_fsize = 800;
+ int sinc_table_size = 30;
+ int sinc_table_center = sinc_table_size / 2;
+
+ public SoftSincResampler() {
+ super();
+ sinc_table = new float[sinc_scale_size][sinc_table_fsize][];
+ for (int s = 0; s < sinc_scale_size; s++) {
+ float scale = (float) (1.0 / (1.0 + Math.pow(s, 1.1) / 10.0));
+ for (int i = 0; i < sinc_table_fsize; i++) {
+ sinc_table[s][i] = sincTable(sinc_table_size,
+ -i / ((float)sinc_table_fsize), scale);
+ }
+ }
+ }
+
+ // Normalized sinc function
+ public static double sinc(double x) {
+ return (x == 0.0) ? 1.0 : Math.sin(Math.PI * x) / (Math.PI * x);
+ }
+
+ // Generate hann window suitable for windowing sinc
+ public static float[] wHanning(int size, float offset) {
+ float[] window_table = new float[size];
+ for (int k = 0; k < size; k++) {
+ window_table[k] = (float)(-0.5
+ * Math.cos(2.0 * Math.PI * (double)(k + offset)
+ / (double) size) + 0.5);
+ }
+ return window_table;
+ }
+
+ // Generate sinc table
+ public static float[] sincTable(int size, float offset, float scale) {
+ int center = size / 2;
+ float[] w = wHanning(size, offset);
+ for (int k = 0; k < size; k++)
+ w[k] *= sinc((-center + k + offset) * scale) * scale;
+ return w;
+ }
+
+ public int getPadding() // must be at least half of sinc_table_size
+ {
+ return sinc_table_size / 2 + 2;
+ }
+
+ public void interpolate(float[] in, float[] in_offset, float in_end,
+ float[] startpitch, float pitchstep, float[] out, int[] out_offset,
+ int out_end) {
+ float pitch = startpitch[0];
+ float ix = in_offset[0];
+ int ox = out_offset[0];
+ float ix_end = in_end;
+ int ox_end = out_end;
+ int max_p = sinc_scale_size - 1;
+ if (pitchstep == 0) {
+
+ int p = (int) ((pitch - 1) * 10.0f);
+ if (p < 0)
+ p = 0;
+ else if (p > max_p)
+ p = max_p;
+ float[][] sinc_table_f = this.sinc_table[p];
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ float[] sinc_table =
+ sinc_table_f[(int)((ix - iix) * sinc_table_fsize)];
+ int xx = iix - sinc_table_center;
+ float y = 0;
+ for (int i = 0; i < sinc_table_size; i++, xx++)
+ y += in[xx] * sinc_table[i];
+ out[ox++] = y;
+ ix += pitch;
+ }
+ } else {
+ while (ix < ix_end && ox < ox_end) {
+ int iix = (int) ix;
+ int p = (int) ((pitch - 1) * 10.0f);
+ if (p < 0)
+ p = 0;
+ else if (p > max_p)
+ p = max_p;
+ float[][] sinc_table_f = this.sinc_table[p];
+
+ float[] sinc_table =
+ sinc_table_f[(int)((ix - iix) * sinc_table_fsize)];
+ int xx = iix - sinc_table_center;
+ float y = 0;
+ for (int i = 0; i < sinc_table_size; i++, xx++)
+ y += in[xx] * sinc_table[i];
+ out[ox++] = y;
+
+ ix += pitch;
+ pitch += pitchstep;
+ }
+ }
+ in_offset[0] = ix;
+ out_offset[0] = ox;
+ startpitch[0] = pitch;
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/share/classes/com/sun/media/sound/SoftSynthesizer.java
new file mode 100644
index 000000000..3f6c12fc7
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftSynthesizer.java
@@ -0,0 +1,1179 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sound.midi.Instrument;
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Patch;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Soundbank;
+import javax.sound.midi.Transmitter;
+import javax.sound.midi.VoiceStatus;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.SourceDataLine;
+
+/**
+ * The software synthesizer class.
+ *
+ * @author Karl Helgason
+ */
+public class SoftSynthesizer implements AudioSynthesizer,
+ ReferenceCountingDevice {
+
+ protected static class WeakAudioStream extends InputStream
+ {
+ private volatile AudioInputStream stream;
+ public SoftAudioPusher pusher = null;
+ public AudioInputStream jitter_stream = null;
+ public SourceDataLine sourceDataLine = null;
+ private WeakReference<AudioInputStream> weak_stream_link;
+ private AudioFloatConverter converter;
+ private float[] silentbuffer = null;
+ private int samplesize;
+
+ public void setInputStream(AudioInputStream stream)
+ {
+ this.stream = stream;
+ }
+
+ public int available() throws IOException {
+ AudioInputStream local_stream = stream;
+ if(local_stream != null)
+ return local_stream.available();
+ return 0;
+ }
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ if (read(b) == -1)
+ return -1;
+ return b[0] & 0xFF;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ AudioInputStream local_stream = stream;
+ if(local_stream != null)
+ return local_stream.read(b, off, len);
+ else
+ {
+ int flen = len / samplesize;
+ if(silentbuffer == null || silentbuffer.length < flen)
+ silentbuffer = new float[flen];
+ converter.toByteArray(silentbuffer, flen, b, off);
+
+ if(pusher != null)
+ if(weak_stream_link.get() == null)
+ {
+ Runnable runnable = new Runnable()
+ {
+ SoftAudioPusher _pusher = pusher;
+ AudioInputStream _jitter_stream = jitter_stream;
+ SourceDataLine _sourceDataLine = sourceDataLine;
+ public void run()
+ {
+ _pusher.stop();
+ if(_jitter_stream != null)
+ try {
+ _jitter_stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if(_sourceDataLine != null)
+ _sourceDataLine.close();
+ }
+ };
+ pusher = null;
+ jitter_stream = null;
+ sourceDataLine = null;
+ new Thread(runnable).start();
+ }
+ return len;
+ }
+ }
+
+ public WeakAudioStream(AudioInputStream stream) {
+ this.stream = stream;
+ weak_stream_link = new WeakReference<AudioInputStream>(stream);
+ converter = AudioFloatConverter.getConverter(stream.getFormat());
+ samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
+ }
+
+ public AudioInputStream getAudioInputStream()
+ {
+ return new AudioInputStream(this, stream.getFormat(), AudioSystem.NOT_SPECIFIED);
+ }
+
+ public void close() throws IOException
+ {
+ AudioInputStream astream = weak_stream_link.get();
+ if(astream != null)
+ astream.close();
+ }
+ }
+
+ private static class Info extends MidiDevice.Info {
+ public Info() {
+ super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION);
+ }
+ }
+
+ protected static final String INFO_NAME = "Gervill";
+ protected static final String INFO_VENDOR = "OpenJDK";
+ protected static final String INFO_DESCRIPTION = "Software MIDI Synthesizer";
+ protected static final String INFO_VERSION = "1.0";
+ protected final static MidiDevice.Info info = new Info();
+
+ private static SourceDataLine testline = null;
+
+ private static Soundbank defaultSoundBank = null;
+
+ protected WeakAudioStream weakstream = null;
+
+ protected Object control_mutex = this;
+
+ protected int voiceIDCounter = 0;
+
+ // 0: default
+ // 1: DLS Voice Allocation
+ protected int voice_allocation_mode = 0;
+
+ protected boolean reverb_light = true;
+ protected boolean reverb_on = true;
+ protected boolean chorus_on = true;
+ protected boolean agc_on = true;
+
+ protected SoftChannel[] channels;
+ protected SoftChannelProxy[] external_channels = null;
+
+ private boolean largemode = false;
+
+ // 0: GM Mode off (default)
+ // 1: GM Level 1
+ // 2: GM Level 2
+ private int gmmode = 0;
+
+ private int deviceid = 0;
+
+ private AudioFormat format = new AudioFormat(44100, 16, 2, true, false);
+
+ private SourceDataLine sourceDataLine = null;
+
+ private SoftAudioPusher pusher = null;
+ private AudioInputStream pusher_stream = null;
+
+ private float controlrate = 147f;
+
+ private boolean open = false;
+ private boolean implicitOpen = false;
+
+ private String resamplerType = "linear";
+ private SoftResampler resampler = new SoftLinearResampler();
+
+ private int number_of_midi_channels = 16;
+ private int maxpoly = 64;
+ private long latency = 200000; // 200 msec
+ private boolean jitter_correction = false;
+
+ private SoftMainMixer mainmixer;
+ private SoftVoice[] voices;
+
+ private Map<String, SoftTuning> tunings
+ = new HashMap<String, SoftTuning>();
+ private Map<String, SoftInstrument> inslist
+ = new HashMap<String, SoftInstrument>();
+ private Map<String, ModelInstrument> availlist
+ = new HashMap<String, ModelInstrument>();
+ private Map<String, ModelInstrument> loadedlist
+ = new HashMap<String, ModelInstrument>();
+
+ private ArrayList<Receiver> recvslist = new ArrayList<Receiver>();
+
+ private void getBuffers(ModelInstrument instrument,
+ List<ModelByteBuffer> buffers) {
+ for (ModelPerformer performer : instrument.getPerformers()) {
+ if (performer.getOscillators() != null) {
+ for (ModelOscillator osc : performer.getOscillators()) {
+ if (osc instanceof ModelByteBufferWavetable) {
+ ModelByteBufferWavetable w = (ModelByteBufferWavetable)osc;
+ ModelByteBuffer buff = w.getBuffer();
+ if (buff != null)
+ buffers.add(buff);
+ buff = w.get8BitExtensionBuffer();
+ if (buff != null)
+ buffers.add(buff);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean loadSamples(List<ModelInstrument> instruments) {
+ if (largemode)
+ return true;
+ List<ModelByteBuffer> buffers = new ArrayList<ModelByteBuffer>();
+ for (ModelInstrument instrument : instruments)
+ getBuffers(instrument, buffers);
+ try {
+ ModelByteBuffer.loadAll(buffers);
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean loadInstruments(List<ModelInstrument> instruments) {
+ if (!isOpen())
+ return false;
+ if (!loadSamples(instruments))
+ return false;
+
+ synchronized (control_mutex) {
+ if (channels != null)
+ for (SoftChannel c : channels)
+ c.current_instrument = null;
+ for (Instrument instrument : instruments) {
+ String pat = patchToString(instrument.getPatch());
+ availlist.remove(pat);
+ SoftInstrument softins
+ = new SoftInstrument((ModelInstrument) instrument);
+ inslist.put(pat, softins);
+ loadedlist.put(pat, (ModelInstrument) instrument);
+ }
+ }
+
+ return true;
+ }
+
+ private void processPropertyInfo(Map<String, Object> info) {
+ AudioSynthesizerPropertyInfo[] items = getPropertyInfo(info);
+
+ String resamplerType = (String)items[0].value;
+ if (resamplerType.equalsIgnoreCase("point"))
+ {
+ this.resampler = new SoftPointResampler();
+ this.resamplerType = "point";
+ }
+ else if (resamplerType.equalsIgnoreCase("linear"))
+ {
+ this.resampler = new SoftLinearResampler2();
+ this.resamplerType = "linear";
+ }
+ else if (resamplerType.equalsIgnoreCase("linear1"))
+ {
+ this.resampler = new SoftLinearResampler();
+ this.resamplerType = "linear1";
+ }
+ else if (resamplerType.equalsIgnoreCase("linear2"))
+ {
+ this.resampler = new SoftLinearResampler2();
+ this.resamplerType = "linear2";
+ }
+ else if (resamplerType.equalsIgnoreCase("cubic"))
+ {
+ this.resampler = new SoftCubicResampler();
+ this.resamplerType = "cubic";
+ }
+ else if (resamplerType.equalsIgnoreCase("lanczos"))
+ {
+ this.resampler = new SoftLanczosResampler();
+ this.resamplerType = "lanczos";
+ }
+ else if (resamplerType.equalsIgnoreCase("sinc"))
+ {
+ this.resampler = new SoftSincResampler();
+ this.resamplerType = "sinc";
+ }
+
+ setFormat((AudioFormat)items[2].value);
+ controlrate = (Float)items[1].value;
+ latency = (Long)items[3].value;
+ deviceid = (Integer)items[4].value;
+ maxpoly = (Integer)items[5].value;
+ reverb_on = (Boolean)items[6].value;
+ chorus_on = (Boolean)items[7].value;
+ agc_on = (Boolean)items[8].value;
+ largemode = (Boolean)items[9].value;
+ number_of_midi_channels = (Integer)items[10].value;
+ jitter_correction = (Boolean)items[11].value;
+ reverb_light = (Boolean)items[12].value;
+ }
+
+ private String patchToString(Patch patch) {
+ if (patch instanceof ModelPatch && ((ModelPatch) patch).isPercussion())
+ return "p." + patch.getProgram() + "." + patch.getBank();
+ else
+ return patch.getProgram() + "." + patch.getBank();
+ }
+
+ private void setFormat(AudioFormat format) {
+ if (format.getChannels() > 2) {
+ throw new IllegalArgumentException(
+ "Only mono and stereo audio supported.");
+ }
+ if (AudioFloatConverter.getConverter(format) == null)
+ throw new IllegalArgumentException("Audio format not supported.");
+ this.format = format;
+ }
+
+ protected void removeReceiver(Receiver recv) {
+ boolean perform_close = false;
+ synchronized (control_mutex) {
+ if (recvslist.remove(recv)) {
+ if (implicitOpen && recvslist.isEmpty())
+ perform_close = true;
+ }
+ }
+ if (perform_close)
+ close();
+ }
+
+ protected SoftMainMixer getMainMixer() {
+ if (!isOpen())
+ return null;
+ return mainmixer;
+ }
+
+ protected SoftInstrument findInstrument(int program, int bank, int channel) {
+
+ // Add support for GM2 banks 0x78 and 0x79
+ // as specified in DLS 2.2 in Section 1.4.6
+ // which allows using percussion and melodic instruments
+ // on all channels
+ if (bank >> 7 == 0x78 || bank >> 7 == 0x79) {
+ SoftInstrument current_instrument
+ = inslist.get(program + "." + bank);
+ if (current_instrument != null)
+ return current_instrument;
+
+ String p_plaf;
+ if (bank >> 7 == 0x78)
+ p_plaf = "p.";
+ else
+ p_plaf = "";
+
+ // Instrument not found fallback to MSB:bank, LSB:0
+ current_instrument = inslist.get(p_plaf + program + "."
+ + ((bank & 128) << 7));
+ if (current_instrument != null)
+ return current_instrument;
+ // Instrument not found fallback to MSB:0, LSB:bank
+ current_instrument = inslist.get(p_plaf + program + "."
+ + (bank & 128));
+ if (current_instrument != null)
+ return current_instrument;
+ // Instrument not found fallback to MSB:0, LSB:0
+ current_instrument = inslist.get(p_plaf + program + ".0");
+ if (current_instrument != null)
+ return current_instrument;
+ // Instrument not found fallback to MSB:0, LSB:0, program=0
+ current_instrument = inslist.get(p_plaf + program + "0.0");
+ if (current_instrument != null)
+ return current_instrument;
+ return null;
+ }
+
+ // Channel 10 uses percussion instruments
+ String p_plaf;
+ if (channel == 9)
+ p_plaf = "p.";
+ else
+ p_plaf = "";
+
+ SoftInstrument current_instrument
+ = inslist.get(p_plaf + program + "." + bank);
+ if (current_instrument != null)
+ return current_instrument;
+ // Instrument not found fallback to MSB:0, LSB:0
+ current_instrument = inslist.get(p_plaf + program + ".0");
+ if (current_instrument != null)
+ return current_instrument;
+ // Instrument not found fallback to MSB:0, LSB:0, program=0
+ current_instrument = inslist.get(p_plaf + "0.0");
+ if (current_instrument != null)
+ return current_instrument;
+ return null;
+ }
+
+ protected int getVoiceAllocationMode() {
+ return voice_allocation_mode;
+ }
+
+ protected int getGeneralMidiMode() {
+ return gmmode;
+ }
+
+ protected void setGeneralMidiMode(int gmmode) {
+ this.gmmode = gmmode;
+ }
+
+ protected int getDeviceID() {
+ return deviceid;
+ }
+
+ protected float getControlRate() {
+ return controlrate;
+ }
+
+ protected SoftVoice[] getVoices() {
+ return voices;
+ }
+
+ protected SoftTuning getTuning(Patch patch) {
+ String t_id = patchToString(patch);
+ SoftTuning tuning = tunings.get(t_id);
+ if (tuning == null) {
+ tuning = new SoftTuning(patch);
+ tunings.put(t_id, tuning);
+ }
+ return tuning;
+ }
+
+ public long getLatency() {
+ synchronized (control_mutex) {
+ return latency;
+ }
+ }
+
+ public AudioFormat getFormat() {
+ synchronized (control_mutex) {
+ return format;
+ }
+ }
+
+ public int getMaxPolyphony() {
+ synchronized (control_mutex) {
+ return maxpoly;
+ }
+ }
+
+ public MidiChannel[] getChannels() {
+
+ synchronized (control_mutex) {
+ // if (external_channels == null) => the synthesizer is not open,
+ // create 16 proxy channels
+ // otherwise external_channels has the same length as channels array
+ if (external_channels == null) {
+ external_channels = new SoftChannelProxy[16];
+ for (int i = 0; i < external_channels.length; i++)
+ external_channels[i] = new SoftChannelProxy();
+ }
+ MidiChannel[] ret;
+ if (isOpen())
+ ret = new MidiChannel[channels.length];
+ else
+ ret = new MidiChannel[16];
+ for (int i = 0; i < ret.length; i++)
+ ret[i] = external_channels[i];
+ return ret;
+ }
+ }
+
+ public VoiceStatus[] getVoiceStatus() {
+ if (!isOpen()) {
+ VoiceStatus[] tempVoiceStatusArray
+ = new VoiceStatus[getMaxPolyphony()];
+ for (int i = 0; i < tempVoiceStatusArray.length; i++) {
+ VoiceStatus b = new VoiceStatus();
+ b.active = false;
+ b.bank = 0;
+ b.channel = 0;
+ b.note = 0;
+ b.program = 0;
+ b.volume = 0;
+ tempVoiceStatusArray[i] = b;
+ }
+ return tempVoiceStatusArray;
+ }
+
+ synchronized (control_mutex) {
+ VoiceStatus[] tempVoiceStatusArray = new VoiceStatus[voices.length];
+ for (int i = 0; i < voices.length; i++) {
+ VoiceStatus a = voices[i];
+ VoiceStatus b = new VoiceStatus();
+ b.active = a.active;
+ b.bank = a.bank;
+ b.channel = a.channel;
+ b.note = a.note;
+ b.program = a.program;
+ b.volume = a.volume;
+ tempVoiceStatusArray[i] = b;
+ }
+ return tempVoiceStatusArray;
+ }
+ }
+
+ public boolean isSoundbankSupported(Soundbank soundbank) {
+ for (Instrument ins: soundbank.getInstruments())
+ if (!(ins instanceof ModelInstrument))
+ return false;
+ return true;
+ }
+
+ public boolean loadInstrument(Instrument instrument) {
+ if (instrument == null || (!(instrument instanceof ModelInstrument))) {
+ throw new IllegalArgumentException("Unsupported instrument: " +
+ instrument);
+ }
+ List<ModelInstrument> instruments = new ArrayList<ModelInstrument>();
+ instruments.add((ModelInstrument)instrument);
+ return loadInstruments(instruments);
+ }
+
+ public void unloadInstrument(Instrument instrument) {
+ if (instrument == null || (!(instrument instanceof ModelInstrument))) {
+ throw new IllegalArgumentException("Unsupported instrument: " +
+ instrument);
+ }
+ if (!isOpen())
+ return;
+
+ String pat = patchToString(instrument.getPatch());
+ synchronized (control_mutex) {
+ for (SoftChannel c: channels)
+ c.current_instrument = null;
+ inslist.remove(pat);
+ loadedlist.remove(pat);
+ availlist.remove(pat);
+ }
+ }
+
+ public boolean remapInstrument(Instrument from, Instrument to) {
+
+ if (from == null)
+ throw new NullPointerException();
+ if (to == null)
+ throw new NullPointerException();
+ if (!(from instanceof ModelInstrument)) {
+ throw new IllegalArgumentException("Unsupported instrument: " +
+ from.toString());
+ }
+ if (!(to instanceof ModelInstrument)) {
+ throw new IllegalArgumentException("Unsupported instrument: " +
+ to.toString());
+ }
+ if (!isOpen())
+ return false;
+
+ synchronized (control_mutex) {
+ if (!loadedlist.containsValue(to) && !availlist.containsValue(to))
+ throw new IllegalArgumentException("Instrument to is not loaded.");
+ unloadInstrument(from);
+ ModelMappedInstrument mfrom = new ModelMappedInstrument(
+ (ModelInstrument)to, from.getPatch());
+ return loadInstrument(mfrom);
+ }
+ }
+
+ public synchronized Soundbank getDefaultSoundbank() {
+ if (defaultSoundBank == null) {
+ try {
+ File javahome = new File(System.getProperties().getProperty(
+ "java.home"));
+ File libaudio = new File(new File(javahome, "lib"), "audio");
+
+ if (libaudio.exists()) {
+ File foundfile = null;
+ File[] files = libaudio.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ if (file.isFile()) {
+ String lname = file.getName().toLowerCase();
+ if (lname.endsWith(".sf2") ||
+ lname.endsWith(".dls")) {
+ if (foundfile == null || (file.length() >
+ foundfile.length())) {
+ foundfile = file;
+ }
+ }
+ }
+ }
+ }
+ if (foundfile != null) {
+ try {
+ Soundbank sbk = MidiSystem.getSoundbank(foundfile);
+ defaultSoundBank = sbk;
+ return defaultSoundBank;
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ }
+ }
+
+ if (System.getProperties().getProperty("os.name")
+ .startsWith("Windows")) {
+ File gm_dls = new File(System.getenv("SystemRoot")
+ + "\\system32\\drivers\\gm.dls");
+ if (gm_dls.exists()) {
+ try {
+ Soundbank sbk = MidiSystem.getSoundbank(gm_dls);
+ defaultSoundBank = sbk;
+ return defaultSoundBank;
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ }
+ }
+ } catch (AccessControlException e) {
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+
+ File userhome = null;
+ File emg_soundbank_file = null;
+
+ /*
+ * Try to load saved generated soundbank
+ */
+ try {
+ userhome = new File(System.getProperty("user.home"),
+ ".gervill");
+ emg_soundbank_file = new File(userhome, "soundbank-emg.sf2");
+ Soundbank sbk = MidiSystem.getSoundbank(emg_soundbank_file);
+ defaultSoundBank = sbk;
+ return defaultSoundBank;
+ } catch (AccessControlException e) {
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+
+ try {
+
+ /*
+ * Generate emergency soundbank
+ */
+ defaultSoundBank = EmergencySoundbank.createSoundbank();
+
+ /*
+ * Save generated soundbank to disk for faster future use.
+ */
+ if(defaultSoundBank != null)
+ {
+ if(!userhome.exists()) userhome.mkdirs();
+ if(!emg_soundbank_file.exists())
+ ((SF2Soundbank)defaultSoundBank).save(emg_soundbank_file);
+ }
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+
+ }
+ return defaultSoundBank;
+ }
+
+ public Instrument[] getAvailableInstruments() {
+ if (!isOpen()) {
+ Soundbank defsbk = getDefaultSoundbank();
+ if (defsbk == null)
+ return new Instrument[0];
+ return defsbk.getInstruments();
+ }
+
+ synchronized (control_mutex) {
+ ModelInstrument[] inslist_array =
+ new ModelInstrument[availlist.values().size()];
+ availlist.values().toArray(inslist_array);
+ Arrays.sort(inslist_array, new ModelInstrumentComparator());
+ return inslist_array;
+ }
+ }
+
+ public Instrument[] getLoadedInstruments() {
+ if (!isOpen())
+ return new Instrument[0];
+
+ synchronized (control_mutex) {
+ ModelInstrument[] inslist_array =
+ new ModelInstrument[loadedlist.values().size()];
+ loadedlist.values().toArray(inslist_array);
+ Arrays.sort(inslist_array, new ModelInstrumentComparator());
+ return inslist_array;
+ }
+ }
+
+ public boolean loadAllInstruments(Soundbank soundbank) {
+ List<ModelInstrument> instruments = new ArrayList<ModelInstrument>();
+ for (Instrument ins: soundbank.getInstruments()) {
+ if (ins == null || !(ins instanceof ModelInstrument)) {
+ throw new IllegalArgumentException(
+ "Unsupported instrument: " + ins);
+ }
+ instruments.add((ModelInstrument)ins);
+ }
+ return loadInstruments(instruments);
+ }
+
+ public void unloadAllInstruments(Soundbank soundbank) {
+ if (soundbank == null || !isSoundbankSupported(soundbank))
+ throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);
+
+ if (!isOpen())
+ return;
+
+ for (Instrument ins: soundbank.getInstruments()) {
+ if (ins instanceof ModelInstrument) {
+ unloadInstrument(ins);
+ }
+ }
+ }
+
+ public boolean loadInstruments(Soundbank soundbank, Patch[] patchList) {
+ List<ModelInstrument> instruments = new ArrayList<ModelInstrument>();
+ for (Patch patch: patchList) {
+ Instrument ins = soundbank.getInstrument(patch);
+ if (ins == null || !(ins instanceof ModelInstrument)) {
+ throw new IllegalArgumentException(
+ "Unsupported instrument: " + ins);
+ }
+ instruments.add((ModelInstrument)ins);
+ }
+ return loadInstruments(instruments);
+ }
+
+ public void unloadInstruments(Soundbank soundbank, Patch[] patchList) {
+ if (soundbank == null || !isSoundbankSupported(soundbank))
+ throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);
+
+ if (!isOpen())
+ return;
+
+ for (Patch pat: patchList) {
+ Instrument ins = soundbank.getInstrument(pat);
+ if (ins instanceof ModelInstrument) {
+ unloadInstrument(ins);
+ }
+ }
+ }
+
+ public MidiDevice.Info getDeviceInfo() {
+ return info;
+ }
+
+ public AudioSynthesizerPropertyInfo[] getPropertyInfo(Map<String, Object> info) {
+ List<AudioSynthesizerPropertyInfo> list =
+ new ArrayList<AudioSynthesizerPropertyInfo>();
+
+ AudioSynthesizerPropertyInfo item;
+
+ // If info != null or synthesizer is closed
+ // we return how the synthesizer will be set on next open
+ // If info == null and synthesizer is open
+ // we return current synthesizer properties.
+ boolean o = info == null && open;
+
+ item = new AudioSynthesizerPropertyInfo("interpolation", o?resamplerType:"linear");
+ item.choices = new String[]{"linear", "linear1", "linear2", "cubic",
+ "lanczos", "sinc", "point"};
+ item.description = "Interpolation method";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("control rate", o?controlrate:147f);
+ item.description = "Control rate";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("format",
+ o?format:new AudioFormat(44100, 16, 2, true, false));
+ item.description = "Default audio format";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("latency", o?latency:120000L);
+ item.description = "Default latency";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("device id", o?deviceid:0);
+ item.description = "Device ID for SysEx Messages";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("max polyphony", o?maxpoly:64);
+ item.description = "Maximum polyphony";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("reverb", o?reverb_on:true);
+ item.description = "Turn reverb effect on or off";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("chorus", o?chorus_on:true);
+ item.description = "Turn chorus effect on or off";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("auto gain control", o?agc_on:true);
+ item.description = "Turn auto gain control on or off";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("large mode", o?largemode:false);
+ item.description = "Turn large mode on or off.";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("midi channels", o?channels.length:16);
+ item.description = "Number of midi channels.";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("jitter correction", o?jitter_correction:true);
+ item.description = "Turn jitter correction on or off.";
+ list.add(item);
+
+ item = new AudioSynthesizerPropertyInfo("light reverb", o?reverb_light:true);
+ item.description = "Turn light reverb mode on or off";
+ list.add(item);
+
+ AudioSynthesizerPropertyInfo[] items;
+ items = list.toArray(new AudioSynthesizerPropertyInfo[list.size()]);
+
+ if (info != null)
+ for (AudioSynthesizerPropertyInfo item2: items) {
+ Object v = info.get(item2.name);
+ Class c = (item2.valueClass);
+ if (v != null)
+ if (c.isInstance(v))
+ item2.value = v;
+ }
+
+ return items;
+ }
+
+ public void open() throws MidiUnavailableException {
+ if (isOpen()) {
+ synchronized (control_mutex) {
+ implicitOpen = false;
+ }
+ return;
+ }
+ open(null, null);
+ }
+
+ public void open(SourceDataLine line, Map<String, Object> info) throws MidiUnavailableException {
+ if (isOpen()) {
+ synchronized (control_mutex) {
+ implicitOpen = false;
+ }
+ return;
+ }
+ synchronized (control_mutex) {
+ try {
+ if (line != null)
+ setFormat(line.getFormat());
+
+ AudioInputStream ais = openStream(getFormat(), info);
+
+ weakstream = new WeakAudioStream(ais);
+ ais = weakstream.getAudioInputStream();
+
+ if (line == null)
+ {
+ if(testline != null)
+ line = testline;
+ else
+ line = AudioSystem.getSourceDataLine(getFormat());
+ }
+
+ double latency = this.latency;
+
+ if (!line.isOpen()) {
+ int bufferSize = getFormat().getFrameSize()
+ * (int)(getFormat().getFrameRate() * (latency/1000000f));
+ line.open(getFormat(), bufferSize);
+
+ // Remember that we opened that line
+ // so we can close again in SoftSynthesizer.close()
+ sourceDataLine = line;
+ }
+ if (!line.isActive())
+ line.start();
+
+ int controlbuffersize = 512;
+ try {
+ controlbuffersize = ais.available();
+ } catch (IOException e) {
+ }
+
+ // Tell mixer not fill read buffers fully.
+ // This lowers latency, and tells DataPusher
+ // to read in smaller amounts.
+ //mainmixer.readfully = false;
+ //pusher = new DataPusher(line, ais);
+
+ int buffersize = line.getBufferSize();
+ buffersize -= buffersize % controlbuffersize;
+
+ if (buffersize < 3 * controlbuffersize)
+ buffersize = 3 * controlbuffersize;
+
+ if (jitter_correction) {
+ ais = new SoftJitterCorrector(ais, buffersize,
+ controlbuffersize);
+ if(weakstream != null)
+ weakstream.jitter_stream = ais;
+ }
+ pusher = new SoftAudioPusher(line, ais, controlbuffersize);
+ pusher_stream = ais;
+ pusher.start();
+
+ if(weakstream != null)
+ {
+ weakstream.pusher = pusher;
+ weakstream.sourceDataLine = sourceDataLine;
+ }
+
+
+
+ } catch (LineUnavailableException e) {
+ if (isOpen())
+ close();
+ // am: need MidiUnavailableException(Throwable) ctor!
+ throw new MidiUnavailableException(e.toString());
+ }
+
+ }
+ }
+
+ public AudioInputStream openStream(AudioFormat targetFormat,
+ Map<String, Object> info) throws MidiUnavailableException {
+
+ if (isOpen())
+ throw new MidiUnavailableException("Synthesizer is already open");
+
+ synchronized (control_mutex) {
+
+ gmmode = 0;
+ voice_allocation_mode = 0;
+
+ processPropertyInfo(info);
+
+ open = true;
+ implicitOpen = false;
+
+ if (targetFormat != null)
+ setFormat(targetFormat);
+
+ Soundbank defbank = getDefaultSoundbank();
+ if (defbank != null) {
+ loadAllInstruments(defbank);
+ availlist.putAll(loadedlist);
+ loadedlist.clear();
+ }
+
+ voices = new SoftVoice[maxpoly];
+ for (int i = 0; i < maxpoly; i++)
+ voices[i] = new SoftVoice(this);
+
+ mainmixer = new SoftMainMixer(this);
+
+ channels = new SoftChannel[number_of_midi_channels];
+ for (int i = 0; i < channels.length; i++)
+ channels[i] = new SoftChannel(this, i);
+
+ if (external_channels == null) {
+ // Always create external_channels array
+ // with 16 or more channels
+ // so getChannels works correctly
+ // when the synhtesizer is closed.
+ if (channels.length < 16)
+ external_channels = new SoftChannelProxy[16];
+ else
+ external_channels = new SoftChannelProxy[channels.length];
+ for (int i = 0; i < external_channels.length; i++)
+ external_channels[i] = new SoftChannelProxy();
+ } else {
+ // We must resize external_channels array
+ // but we must also copy the old SoftChannelProxy
+ // into the new one
+ if (channels.length > external_channels.length) {
+ SoftChannelProxy[] new_external_channels
+ = new SoftChannelProxy[channels.length];
+ for (int i = 0; i < external_channels.length; i++)
+ new_external_channels[i] = external_channels[i];
+ for (int i = external_channels.length;
+ i < new_external_channels.length; i++) {
+ new_external_channels[i] = new SoftChannelProxy();
+ }
+ }
+ }
+
+ for (int i = 0; i < channels.length; i++)
+ external_channels[i].setChannel(channels[i]);
+
+ for (SoftVoice voice: getVoices())
+ voice.resampler = resampler.openStreamer();
+
+ for (Receiver recv: getReceivers()) {
+ SoftReceiver srecv = ((SoftReceiver)recv);
+ srecv.open = open;
+ srecv.mainmixer = mainmixer;
+ srecv.midimessages = mainmixer.midimessages;
+ }
+
+ return mainmixer.getInputStream();
+ }
+ }
+
+ public void close() {
+
+ if (!isOpen())
+ return;
+
+ SoftAudioPusher pusher_to_be_closed = null;
+ AudioInputStream pusher_stream_to_be_closed = null;
+ synchronized (control_mutex) {
+ if (pusher != null) {
+ pusher_to_be_closed = pusher;
+ pusher_stream_to_be_closed = pusher_stream;
+ pusher = null;
+ pusher_stream = null;
+ }
+ }
+
+ if (pusher_to_be_closed != null) {
+ // Pusher must not be closed synchronized against control_mutex,
+ // this may result in synchronized conflict between pusher
+ // and current thread.
+ pusher_to_be_closed.stop();
+
+ try {
+ pusher_stream_to_be_closed.close();
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+ }
+
+ synchronized (control_mutex) {
+
+ if (mainmixer != null)
+ mainmixer.close();
+ open = false;
+ implicitOpen = false;
+ mainmixer = null;
+ voices = null;
+ channels = null;
+
+ if (external_channels != null)
+ for (int i = 0; i < external_channels.length; i++)
+ external_channels[i].setChannel(null);
+
+ if (sourceDataLine != null) {
+ sourceDataLine.close();
+ sourceDataLine = null;
+ }
+
+ inslist.clear();
+ availlist.clear();
+ loadedlist.clear();
+ tunings.clear();
+
+ while (recvslist.size() != 0)
+ recvslist.get(recvslist.size() - 1).close();
+
+ }
+ }
+
+ public boolean isOpen() {
+ synchronized (control_mutex) {
+ return open;
+ }
+ }
+
+ public long getMicrosecondPosition() {
+
+ if (!isOpen())
+ return 0;
+
+ synchronized (control_mutex) {
+ return mainmixer.getMicrosecondPosition();
+ }
+ }
+
+ public int getMaxReceivers() {
+ return -1;
+ }
+
+ public int getMaxTransmitters() {
+ return 0;
+ }
+
+ public Receiver getReceiver() throws MidiUnavailableException {
+
+ synchronized (control_mutex) {
+ SoftReceiver receiver = new SoftReceiver(this);
+ receiver.open = open;
+ recvslist.add(receiver);
+ return receiver;
+ }
+ }
+
+ public List<Receiver> getReceivers() {
+
+ synchronized (control_mutex) {
+ ArrayList<Receiver> recvs = new ArrayList<Receiver>();
+ recvs.addAll(recvslist);
+ return recvs;
+ }
+ }
+
+ public Transmitter getTransmitter() throws MidiUnavailableException {
+
+ throw new MidiUnavailableException("No transmitter available");
+ }
+
+ public List<Transmitter> getTransmitters() {
+
+ return new ArrayList<Transmitter>();
+ }
+
+ public Receiver getReceiverReferenceCounting()
+ throws MidiUnavailableException {
+
+ if (!isOpen()) {
+ open();
+ synchronized (control_mutex) {
+ implicitOpen = true;
+ }
+ }
+
+ return getReceiver();
+ }
+
+ public Transmitter getTransmitterReferenceCounting()
+ throws MidiUnavailableException {
+
+ throw new MidiUnavailableException("No transmitter available");
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftTuning.java b/src/share/classes/com/sun/media/sound/SoftTuning.java
new file mode 100644
index 000000000..a5a1eafe1
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftTuning.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.sound.midi.Patch;
+
+/**
+ * A tuning program container, for use with MIDI Tuning.
+ * See: http://www.midi.org
+ *
+ * @author Karl Helgason
+ */
+public class SoftTuning {
+
+ private String name = null;
+ private double[] tuning = new double[128];
+ private Patch patch = null;
+
+ public SoftTuning() {
+ name = "12-TET";
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100;
+ }
+
+ public SoftTuning(byte[] data) {
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100;
+ load(data);
+ }
+
+ public SoftTuning(Patch patch) {
+ this.patch = patch;
+ name = "12-TET";
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100;
+ }
+
+ public SoftTuning(Patch patch, byte[] data) {
+ this.patch = patch;
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100;
+ load(data);
+ }
+
+ private boolean checksumOK(byte[] data) {
+ int x = data[1] & 0xFF;
+ for (int i = 2; i < data.length - 2; i++)
+ x = x ^ (data[i] & 0xFF);
+ return (data[data.length - 2] & 0xFF) == (x & 127);
+ }
+
+ /*
+ private boolean checksumOK2(byte[] data) {
+ int x = data[1] & 0xFF; // 7E
+ x = x ^ (data[2] & 0xFF); // <device ID>
+ x = x ^ (data[4] & 0xFF); // nn
+ x = x ^ (data[5] & 0xFF); // tt
+ for (int i = 22; i < data.length - 2; i++)
+ x = x ^ (data[i] & 0xFF);
+ return (data[data.length - 2] & 0xFF) == (x & 127);
+ }
+ */
+ public void load(byte[] data) {
+ // Universal Non-Real-Time / Real-Time SysEx
+ if ((data[1] & 0xFF) == 0x7E || (data[1] & 0xFF) == 0x7F) {
+ int subid1 = data[3] & 0xFF;
+ switch (subid1) {
+ case 0x08: // MIDI Tuning Standard
+ int subid2 = data[4] & 0xFF;
+ switch (subid2) {
+ case 0x01: // BULK TUNING DUMP (NON-REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning.shtml
+ //if (!checksumOK2(data))
+ // break;
+ try {
+ name = new String(data, 6, 16, "ascii");
+ } catch (UnsupportedEncodingException e) {
+ name = null;
+ }
+ int r = 22;
+ for (int i = 0; i < 128; i++) {
+ int xx = data[r++] & 0xFF;
+ int yy = data[r++] & 0xFF;
+ int zz = data[r++] & 0xFF;
+ if (!(xx == 127 && yy == 127 && zz == 127))
+ tuning[i] = 100.0 *
+ (((xx * 16384) + (yy * 128) + zz) / 16384.0);
+ }
+ break;
+ }
+ case 0x02: // SINGLE NOTE TUNING CHANGE (REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning.shtml
+ int ll = data[6] & 0xFF;
+ int r = 7;
+ for (int i = 0; i < ll; i++) {
+ int kk = data[r++] & 0xFF;
+ int xx = data[r++] & 0xFF;
+ int yy = data[r++] & 0xFF;
+ int zz = data[r++] & 0xFF;
+ if (!(xx == 127 && yy == 127 && zz == 127))
+ tuning[kk] = 100.0*(((xx*16384) + (yy*128) + zz)/16384.0);
+ }
+ break;
+ }
+ case 0x04: // KEY-BASED TUNING DUMP (NON-REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning_extens.shtml
+ if (!checksumOK(data))
+ break;
+ try {
+ name = new String(data, 7, 16, "ascii");
+ } catch (UnsupportedEncodingException e) {
+ name = null;
+ }
+ int r = 23;
+ for (int i = 0; i < 128; i++) {
+ int xx = data[r++] & 0xFF;
+ int yy = data[r++] & 0xFF;
+ int zz = data[r++] & 0xFF;
+ if (!(xx == 127 && yy == 127 && zz == 127))
+ tuning[i] = 100.0*(((xx*16384) + (yy*128) + zz)/16384.0);
+ }
+ break;
+ }
+ case 0x05: // SCALE/OCTAVE TUNING DUMP, 1 byte format
+ // (NON-REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning_extens.shtml
+ if (!checksumOK(data))
+ break;
+ try {
+ name = new String(data, 7, 16, "ascii");
+ } catch (UnsupportedEncodingException e) {
+ name = null;
+ }
+ int[] octave_tuning = new int[12];
+ for (int i = 0; i < 12; i++)
+ octave_tuning[i] = (data[i + 23] & 0xFF) - 64;
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100 + octave_tuning[i % 12];
+ break;
+ }
+ case 0x06: // SCALE/OCTAVE TUNING DUMP, 2 byte format
+ // (NON-REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning_extens.shtml
+ if (!checksumOK(data))
+ break;
+ try {
+ name = new String(data, 7, 16, "ascii");
+ } catch (UnsupportedEncodingException e) {
+ name = null;
+ }
+ double[] octave_tuning = new double[12];
+ for (int i = 0; i < 12; i++) {
+ int v = (data[i * 2 + 23] & 0xFF) * 128
+ + (data[i * 2 + 24] & 0xFF);
+ octave_tuning[i] = (v / 8192.0 - 1) * 100.0;
+ }
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100 + octave_tuning[i % 12];
+ break;
+ }
+ case 0x07: // SINGLE NOTE TUNING CHANGE (NON
+ // REAL-TIME/REAL-TIME) (BANK)
+ // http://www.midi.org/about-midi/tuning_extens.shtml
+ int ll = data[7] & 0xFF;
+ int r = 8;
+ for (int i = 0; i < ll; i++) {
+ int kk = data[r++] & 0xFF;
+ int xx = data[r++] & 0xFF;
+ int yy = data[r++] & 0xFF;
+ int zz = data[r++] & 0xFF;
+ if (!(xx == 127 && yy == 127 && zz == 127))
+ tuning[kk] = 100.0
+ * (((xx*16384) + (yy*128) + zz) / 16384.0);
+ }
+ break;
+ case 0x08: // scale/octave tuning 1-byte form (Non
+ // Real-Time/REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning-scale.shtml
+ int[] octave_tuning = new int[12];
+ for (int i = 0; i < 12; i++)
+ octave_tuning[i] = (data[i + 8] & 0xFF) - 64;
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100 + octave_tuning[i % 12];
+ break;
+ }
+ case 0x09: // scale/octave tuning 2-byte form (Non
+ // Real-Time/REAL-TIME)
+ {
+ // http://www.midi.org/about-midi/tuning-scale.shtml
+ double[] octave_tuning = new double[12];
+ for (int i = 0; i < 12; i++) {
+ int v = (data[i * 2 + 8] & 0xFF) * 128
+ + (data[i * 2 + 9] & 0xFF);
+ octave_tuning[i] = (v / 8192.0 - 1) * 100.0;
+ }
+ for (int i = 0; i < tuning.length; i++)
+ tuning[i] = i * 100 + octave_tuning[i % 12];
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ public double[] getTuning() {
+ return tuning;
+ }
+
+ public double getTuning(int noteNumber) {
+ return tuning[noteNumber];
+ }
+
+ public Patch getPatch() {
+ return patch;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/SoftVoice.java b/src/share/classes/com/sun/media/sound/SoftVoice.java
new file mode 100644
index 000000000..49662b787
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/SoftVoice.java
@@ -0,0 +1,841 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sound.midi.VoiceStatus;
+
+/**
+ * Software synthesizer voice class.
+ *
+ * @author Karl Helgason
+ */
+public class SoftVoice extends VoiceStatus {
+
+ public int exclusiveClass = 0;
+ public boolean releaseTriggered = false;
+ private int noteOn_noteNumber = 0;
+ private int noteOn_velocity = 0;
+ private int noteOff_velocity = 0;
+ protected ModelChannelMixer channelmixer = null;
+ protected double tunedKey = 0;
+ protected SoftTuning tuning = null;
+ protected SoftChannel stealer_channel = null;
+ protected ModelConnectionBlock[] stealer_extendedConnectionBlocks = null;
+ protected SoftPerformer stealer_performer = null;
+ protected ModelChannelMixer stealer_channelmixer = null;
+ protected int stealer_voiceID = -1;
+ protected int stealer_noteNumber = 0;
+ protected int stealer_velocity = 0;
+ protected boolean stealer_releaseTriggered = false;
+ protected int voiceID = -1;
+ protected boolean sustain = false;
+ protected boolean sostenuto = false;
+ protected boolean portamento = false;
+ private SoftFilter filter_left;
+ private SoftFilter filter_right;
+ private SoftProcess eg = new SoftEnvelopeGenerator();
+ private SoftProcess lfo = new SoftLowFrequencyOscillator();
+ protected Map<String, SoftControl> objects =
+ new HashMap<String, SoftControl>();
+ protected SoftSynthesizer synthesizer;
+ protected SoftInstrument instrument;
+ protected SoftPerformer performer;
+ protected SoftChannel softchannel = null;
+ protected boolean on = false;
+ private boolean audiostarted = false;
+ private boolean started = false;
+ private boolean stopping = false;
+ private float osc_attenuation = 0.0f;
+ private ModelOscillatorStream osc_stream;
+ private int osc_stream_nrofchannels;
+ private float[][] osc_buff = new float[2][];
+ private boolean osc_stream_off_transmitted = false;
+ private boolean out_mixer_end = false;
+ private float out_mixer_left = 0;
+ private float out_mixer_right = 0;
+ private float out_mixer_effect1 = 0;
+ private float out_mixer_effect2 = 0;
+ private float last_out_mixer_left = 0;
+ private float last_out_mixer_right = 0;
+ private float last_out_mixer_effect1 = 0;
+ private float last_out_mixer_effect2 = 0;
+ protected ModelConnectionBlock[] extendedConnectionBlocks = null;
+ private ModelConnectionBlock[] connections;
+ // Last value added to destination
+ private double[] connections_last = new double[50];
+ // Pointer to source value
+ private double[][][] connections_src = new double[50][3][];
+ // Key-based override (if any)
+ private int[][] connections_src_kc = new int[50][3];
+ // Pointer to destination value
+ private double[][] connections_dst = new double[50][];
+ private boolean soundoff = false;
+ private float lastMuteValue = 0;
+ private float lastSoloMuteValue = 0;
+ protected double[] co_noteon_keynumber = new double[1];
+ protected double[] co_noteon_velocity = new double[1];
+ protected double[] co_noteon_on = new double[1];
+ private SoftControl co_noteon = new SoftControl() {
+ double[] keynumber = co_noteon_keynumber;
+ double[] velocity = co_noteon_velocity;
+ double[] on = co_noteon_on;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ if (name.equals("keynumber"))
+ return keynumber;
+ if (name.equals("velocity"))
+ return velocity;
+ if (name.equals("on"))
+ return on;
+ return null;
+ }
+ };
+ private double[] co_mixer_active = new double[1];
+ private double[] co_mixer_gain = new double[1];
+ private double[] co_mixer_pan = new double[1];
+ private double[] co_mixer_balance = new double[1];
+ private double[] co_mixer_reverb = new double[1];
+ private double[] co_mixer_chorus = new double[1];
+ private SoftControl co_mixer = new SoftControl() {
+ double[] active = co_mixer_active;
+ double[] gain = co_mixer_gain;
+ double[] pan = co_mixer_pan;
+ double[] balance = co_mixer_balance;
+ double[] reverb = co_mixer_reverb;
+ double[] chorus = co_mixer_chorus;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ if (name.equals("active"))
+ return active;
+ if (name.equals("gain"))
+ return gain;
+ if (name.equals("pan"))
+ return pan;
+ if (name.equals("balance"))
+ return balance;
+ if (name.equals("reverb"))
+ return reverb;
+ if (name.equals("chorus"))
+ return chorus;
+ return null;
+ }
+ };
+ private double[] co_osc_pitch = new double[1];
+ private SoftControl co_osc = new SoftControl() {
+ double[] pitch = co_osc_pitch;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ if (name.equals("pitch"))
+ return pitch;
+ return null;
+ }
+ };
+ private double[] co_filter_freq = new double[1];
+ private double[] co_filter_type = new double[1];
+ private double[] co_filter_q = new double[1];
+ private SoftControl co_filter = new SoftControl() {
+ double[] freq = co_filter_freq;
+ double[] ftype = co_filter_type;
+ double[] q = co_filter_q;
+ public double[] get(int instance, String name) {
+ if (name == null)
+ return null;
+ if (name.equals("freq"))
+ return freq;
+ if (name.equals("type"))
+ return ftype;
+ if (name.equals("q"))
+ return q;
+ return null;
+ }
+ };
+ protected SoftResamplerStreamer resampler;
+ private int nrofchannels;
+
+ public SoftVoice(SoftSynthesizer synth) {
+ synthesizer = synth;
+ filter_left = new SoftFilter(synth.getFormat().getSampleRate());
+ filter_right = new SoftFilter(synth.getFormat().getSampleRate());
+ nrofchannels = synth.getFormat().getChannels();
+ }
+
+ private int getValueKC(ModelIdentifier id) {
+ if (id.getObject().equals("midi_cc")) {
+ int ic = Integer.parseInt(id.getVariable());
+ if (ic != 0 && ic != 32) {
+ if (ic < 120)
+ return ic;
+ }
+ } else if (id.getObject().equals("midi_rpn")) {
+ if (id.getVariable().equals("1"))
+ return 120; // Fine tuning
+ if (id.getVariable().equals("2"))
+ return 121; // Coarse tuning
+ }
+ return -1;
+ }
+
+ private double[] getValue(ModelIdentifier id) {
+ SoftControl o = objects.get(id.getObject());
+ if (o == null)
+ return null;
+ return o.get(id.getInstance(), id.getVariable());
+ }
+
+ private double transformValue(double value, ModelSource src) {
+ if (src.getTransform() != null)
+ return src.getTransform().transform(value);
+ else
+ return value;
+ }
+
+ private double transformValue(double value, ModelDestination dst) {
+ if (dst.getTransform() != null)
+ return dst.getTransform().transform(value);
+ else
+ return value;
+ }
+
+ private double processKeyBasedController(double value, int keycontrol) {
+ if (keycontrol == -1)
+ return value;
+ if (softchannel.keybasedcontroller_active != null)
+ if (softchannel.keybasedcontroller_active[note] != null)
+ if (softchannel.keybasedcontroller_active[note][keycontrol]) {
+ double key_controlvalue =
+ softchannel.keybasedcontroller_value[note][keycontrol];
+ if (keycontrol == 10 || keycontrol == 91 || keycontrol == 93)
+ return key_controlvalue;
+ value += key_controlvalue * 2.0 - 1.0;
+ if (value > 1)
+ value = 1;
+ else if (value < 0)
+ value = 0;
+ }
+ return value;
+ }
+
+ private void processConnection(int ix) {
+ ModelConnectionBlock conn = connections[ix];
+ double[][] src = connections_src[ix];
+ double[] dst = connections_dst[ix];
+ if (dst == null || Double.isInfinite(dst[0]))
+ return;
+
+ double value = conn.getScale();
+ if (softchannel.keybasedcontroller_active == null) {
+ ModelSource[] srcs = conn.getSources();
+ for (int i = 0; i < srcs.length; i++) {
+ value *= transformValue(src[i][0], srcs[i]);
+ if (value == 0)
+ break;
+ }
+ } else {
+ ModelSource[] srcs = conn.getSources();
+ int[] src_kc = connections_src_kc[ix];
+ for (int i = 0; i < srcs.length; i++) {
+ value *= transformValue(processKeyBasedController(src[i][0],
+ src_kc[i]), srcs[i]);
+ if (value == 0)
+ break;
+ }
+ }
+
+ value = transformValue(value, conn.getDestination());
+ dst[0] = dst[0] - connections_last[ix] + value;
+ connections_last[ix] = value;
+ // co_mixer_gain[0] = 0;
+ }
+
+ protected void updateTuning(SoftTuning newtuning) {
+ tunedKey = tuning.getTuning(note) / 100.0;
+ if (!portamento) {
+ co_noteon_keynumber[0] = tunedKey * (1.0 / 128.0);
+ int[] c = performer.midi_connections[4];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+ }
+
+ protected void setNote(int noteNumber) {
+ note = noteNumber;
+ tunedKey = tuning.getTuning(noteNumber) / 100.0;
+ }
+
+ protected void noteOn(int noteNumber, int velocity) {
+
+ sustain = false;
+ sostenuto = false;
+ portamento = false;
+
+ soundoff = false;
+ on = true;
+ active = true;
+ started = true;
+ // volume = velocity;
+
+ noteOn_noteNumber = noteNumber;
+ noteOn_velocity = velocity;
+
+ lastMuteValue = 0;
+ lastSoloMuteValue = 0;
+
+ setNote(noteNumber);
+
+ if (performer.forcedKeynumber)
+ co_noteon_keynumber[0] = 0;
+ else
+ co_noteon_keynumber[0] = tunedKey * (1f / 128f);
+ if (performer.forcedVelocity)
+ co_noteon_velocity[0] = 0;
+ else
+ co_noteon_velocity[0] = velocity * (1f / 128f);
+ co_mixer_active[0] = 0;
+ co_mixer_gain[0] = 0;
+ co_mixer_pan[0] = 0;
+ co_mixer_balance[0] = 0;
+ co_mixer_reverb[0] = 0;
+ co_mixer_chorus[0] = 0;
+ co_osc_pitch[0] = 0;
+ co_filter_freq[0] = 0;
+ co_filter_q[0] = 0;
+ co_filter_type[0] = 0;
+ co_noteon_on[0] = 1;
+
+ eg.reset();
+ lfo.reset();
+ filter_left.reset();
+ filter_right.reset();
+
+ objects.put("master", synthesizer.getMainMixer().co_master);
+ objects.put("eg", eg);
+ objects.put("lfo", lfo);
+ objects.put("noteon", co_noteon);
+ objects.put("osc", co_osc);
+ objects.put("mixer", co_mixer);
+ objects.put("filter", co_filter);
+
+ connections = performer.connections;
+
+ if (connections_last == null
+ || connections_last.length < connections.length) {
+ connections_last = new double[connections.length];
+ }
+ if (connections_src == null
+ || connections_src.length < connections.length) {
+ connections_src = new double[connections.length][][];
+ connections_src_kc = new int[connections.length][];
+ }
+ if (connections_dst == null
+ || connections_dst.length < connections.length) {
+ connections_dst = new double[connections.length][];
+ }
+ for (int i = 0; i < connections.length; i++) {
+ ModelConnectionBlock conn = connections[i];
+ connections_last[i] = 0;
+ if (conn.getSources() != null) {
+ ModelSource[] srcs = conn.getSources();
+ if (connections_src[i] == null
+ || connections_src[i].length < srcs.length) {
+ connections_src[i] = new double[srcs.length][];
+ connections_src_kc[i] = new int[srcs.length];
+ }
+ double[][] src = connections_src[i];
+ int[] src_kc = connections_src_kc[i];
+ connections_src[i] = src;
+ for (int j = 0; j < srcs.length; j++) {
+ src_kc[j] = getValueKC(srcs[j].getIdentifier());
+ src[j] = getValue(srcs[j].getIdentifier());
+ }
+ }
+
+ if (conn.getDestination() != null)
+ connections_dst[i] = getValue(conn.getDestination()
+ .getIdentifier());
+ else
+ connections_dst[i] = null;
+ }
+
+ for (int i = 0; i < connections.length; i++)
+ processConnection(i);
+
+ if (extendedConnectionBlocks != null) {
+ for (ModelConnectionBlock connection: extendedConnectionBlocks) {
+ double value = 0;
+
+ if (softchannel.keybasedcontroller_active == null) {
+ for (ModelSource src: connection.getSources()) {
+ double x = getValue(src.getIdentifier())[0];
+ ModelTransform t = src.getTransform();
+ if (t == null)
+ value += x;
+ else
+ value += t.transform(x);
+ }
+ } else {
+ for (ModelSource src: connection.getSources()) {
+ double x = getValue(src.getIdentifier())[0];
+ x = processKeyBasedController(x,
+ getValueKC(src.getIdentifier()));
+ ModelTransform t = src.getTransform();
+ if (t == null)
+ value += x;
+ else
+ value += t.transform(x);
+ }
+ }
+
+ ModelDestination dest = connection.getDestination();
+ ModelTransform t = dest.getTransform();
+ if (t != null)
+ value = t.transform(value);
+ getValue(dest.getIdentifier())[0] += value;
+ }
+ }
+
+ eg.init(synthesizer);
+ lfo.init(synthesizer);
+
+ }
+
+ protected void setPolyPressure(int pressure) {
+ int[] c = performer.midi_connections[2];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void setChannelPressure(int pressure) {
+ int[] c = performer.midi_connections[1];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void controlChange(int controller, int value) {
+ int[] c = performer.midi_ctrl_connections[controller];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void nrpnChange(int controller, int value) {
+ int[] c = performer.midi_nrpn_connections.get(controller);
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void rpnChange(int controller, int value) {
+ int[] c = performer.midi_rpn_connections.get(controller);
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void setPitchBend(int bend) {
+ int[] c = performer.midi_connections[0];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void setMute(boolean mute) {
+ co_mixer_gain[0] -= lastMuteValue;
+ lastMuteValue = mute ? -960 : 0;
+ co_mixer_gain[0] += lastMuteValue;
+ }
+
+ protected void setSoloMute(boolean mute) {
+ co_mixer_gain[0] -= lastSoloMuteValue;
+ lastSoloMuteValue = mute ? -960 : 0;
+ co_mixer_gain[0] += lastSoloMuteValue;
+ }
+
+ protected void shutdown() {
+ if (co_noteon_on[0] < -0.5)
+ return;
+ on = false;
+
+ co_noteon_on[0] = -1;
+
+ int[] c = performer.midi_connections[3];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void soundOff() {
+ on = false;
+ soundoff = true;
+ }
+
+ protected void noteOff(int velocity) {
+ if (!on)
+ return;
+ on = false;
+
+ noteOff_velocity = velocity;
+
+ if (softchannel.sustain) {
+ sustain = true;
+ return;
+ }
+ if (sostenuto)
+ return;
+
+ co_noteon_on[0] = 0;
+
+ int[] c = performer.midi_connections[3];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void redamp() {
+ if (co_noteon_on[0] > 0.5)
+ return;
+ if (co_noteon_on[0] < -0.5)
+ return; // don't redamp notes in shutdown stage
+
+ sustain = true;
+ co_noteon_on[0] = 1;
+
+ int[] c = performer.midi_connections[3];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ protected void processControlLogic() {
+ if (stopping) {
+ active = false;
+ stopping = false;
+ audiostarted = false;
+ if (osc_stream != null)
+ try {
+ osc_stream.close();
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+
+ if (stealer_channel != null) {
+ stealer_channel.initVoice(this, stealer_performer,
+ stealer_voiceID, stealer_noteNumber, stealer_velocity,
+ stealer_extendedConnectionBlocks, stealer_channelmixer,
+ stealer_releaseTriggered);
+ stealer_releaseTriggered = false;
+ stealer_channel = null;
+ stealer_performer = null;
+ stealer_voiceID = -1;
+ stealer_noteNumber = 0;
+ stealer_velocity = 0;
+ stealer_extendedConnectionBlocks = null;
+ stealer_channelmixer = null;
+ }
+ }
+ if (started) {
+ audiostarted = true;
+
+ ModelOscillator osc = performer.oscillators[0];
+
+ osc_stream_off_transmitted = false;
+ if (osc instanceof ModelWavetable) {
+ try {
+ resampler.open((ModelWavetable)osc,
+ synthesizer.getFormat().getSampleRate());
+ osc_stream = resampler;
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+ } else {
+ osc_stream = osc.open(synthesizer.getFormat().getSampleRate());
+ }
+ osc_attenuation = osc.getAttenuation();
+ osc_stream_nrofchannels = osc.getChannels();
+ if (osc_buff == null || osc_buff.length < osc_stream_nrofchannels)
+ osc_buff = new float[osc_stream_nrofchannels][];
+
+ if (osc_stream != null)
+ osc_stream.noteOn(softchannel, this, noteOn_noteNumber,
+ noteOn_velocity);
+
+
+ }
+ if (audiostarted) {
+ if (portamento) {
+ double note_delta = tunedKey - (co_noteon_keynumber[0] * 128);
+ double note_delta_a = Math.abs(note_delta);
+ if (note_delta_a < 0.0000000001) {
+ co_noteon_keynumber[0] = tunedKey * (1.0 / 128.0);
+ portamento = false;
+ } else {
+ if (note_delta_a > softchannel.portamento_time)
+ note_delta = Math.signum(note_delta)
+ * softchannel.portamento_time;
+ co_noteon_keynumber[0] += note_delta * (1.0 / 128.0);
+ }
+
+ int[] c = performer.midi_connections[4];
+ if (c == null)
+ return;
+ for (int i = 0; i < c.length; i++)
+ processConnection(c[i]);
+ }
+
+ eg.processControlLogic();
+ lfo.processControlLogic();
+
+ for (int i = 0; i < performer.ctrl_connections.length; i++)
+ processConnection(performer.ctrl_connections[i]);
+
+ osc_stream.setPitch((float)co_osc_pitch[0]);
+
+ int filter_type = (int)co_filter_type[0];
+ double filter_freq;
+
+ if (co_filter_freq[0] == 13500.0)
+ filter_freq = 19912.126958213175;
+ else
+ filter_freq = 440.0 * Math.exp(
+ ((co_filter_freq[0]) - 6900.0) *
+ (Math.log(2.0) / 1200.0));
+ /*
+ filter_freq = 440.0 * Math.pow(2.0,
+ ((co_filter_freq[0]) - 6900.0) / 1200.0);*/
+ /*
+ * double velocity = co_noteon_velocity[0]; if(velocity < 0.5)
+ * filter_freq *= ((velocity * 2)*0.75 + 0.25);
+ */
+
+ double q = co_filter_q[0] / 10.0;
+ filter_left.setFilterType(filter_type);
+ filter_left.setFrequency(filter_freq);
+ filter_left.setResonance(q);
+ filter_right.setFilterType(filter_type);
+ filter_right.setFrequency(filter_freq);
+ filter_right.setResonance(q);
+ /*
+ float gain = (float) Math.pow(10,
+ (-osc_attenuation + co_mixer_gain[0]) / 200.0);
+ */
+ float gain = (float)Math.exp(
+ (-osc_attenuation + co_mixer_gain[0])*(Math.log(10) / 200.0));
+
+ if (co_mixer_gain[0] <= -960)
+ gain = 0;
+
+ if (soundoff) {
+ stopping = true;
+ gain = 0;
+ /*
+ * if(co_mixer_gain[0] > -960)
+ * co_mixer_gain[0] -= 960;
+ */
+ }
+
+ volume = (int)(Math.sqrt(gain) * 128);
+
+ // gain *= 0.2;
+
+ double pan = co_mixer_pan[0] * (1.0 / 1000.0);
+ // System.out.println("pan = " + pan);
+ if (pan < 0)
+ pan = 0;
+ else if (pan > 1)
+ pan = 1;
+
+ if (pan == 0.5) {
+ out_mixer_left = gain * 0.7071067811865476f;
+ out_mixer_right = out_mixer_left;
+ } else {
+ out_mixer_left = gain * (float)Math.cos(pan * Math.PI * 0.5);
+ out_mixer_right = gain * (float)Math.sin(pan * Math.PI * 0.5);
+ }
+
+ double balance = co_mixer_balance[0] * (1.0 / 1000.0);
+ if (balance != 0.5) {
+ if (balance > 0.5)
+ out_mixer_left *= (1 - balance) * 2;
+ else
+ out_mixer_right *= balance * 2;
+ }
+
+ if (synthesizer.reverb_on) {
+ out_mixer_effect1 = (float)(co_mixer_reverb[0] * (1.0 / 1000.0));
+ out_mixer_effect1 *= gain;
+ } else
+ out_mixer_effect1 = 0;
+ if (synthesizer.chorus_on) {
+ out_mixer_effect2 = (float)(co_mixer_chorus[0] * (1.0 / 1000.0));
+ out_mixer_effect2 *= gain;
+ } else
+ out_mixer_effect2 = 0;
+ out_mixer_end = co_mixer_active[0] < 0.5;
+
+ if (!on)
+ if (!osc_stream_off_transmitted) {
+ osc_stream_off_transmitted = true;
+ if (osc_stream != null)
+ osc_stream.noteOff(noteOff_velocity);
+ }
+
+ }
+ if (started) {
+ last_out_mixer_left = out_mixer_left;
+ last_out_mixer_right = out_mixer_right;
+ last_out_mixer_effect1 = out_mixer_effect1;
+ last_out_mixer_effect2 = out_mixer_effect2;
+ started = false;
+ }
+
+ }
+
+ protected void mixAudioStream(SoftAudioBuffer in, SoftAudioBuffer out,
+ float amp_from, float amp_to) {
+ int bufferlen = in.getSize();
+ if (amp_from < 0.000000001 && amp_to < 0.000000001)
+ return;
+ if (amp_from == amp_to) {
+ float[] fout = out.array();
+ float[] fin = in.array();
+ for (int i = 0; i < bufferlen; i++)
+ fout[i] += fin[i] * amp_to;
+ } else {
+ float amp = amp_from;
+ float amp_delta = (amp_to - amp_from) / bufferlen;
+ float[] fout = out.array();
+ float[] fin = in.array();
+ for (int i = 0; i < bufferlen; i++) {
+ amp += amp_delta;
+ fout[i] += fin[i] * amp;
+ }
+ }
+
+ }
+
+ protected void processAudioLogic(SoftAudioBuffer[] buffer) {
+ if (!audiostarted)
+ return;
+
+ int bufferlen = buffer[0].getSize();
+
+ try {
+ osc_buff[0] = buffer[SoftMainMixer.CHANNEL_LEFT_DRY].array();
+ if (nrofchannels != 1)
+ osc_buff[1] = buffer[SoftMainMixer.CHANNEL_RIGHT_DRY].array();
+ int ret = osc_stream.read(osc_buff, 0, bufferlen);
+ if (ret == -1) {
+ stopping = true;
+ return;
+ }
+ if (ret != bufferlen) {
+ Arrays.fill(osc_buff[0], ret, bufferlen, 0f);
+ if (nrofchannels != 1)
+ Arrays.fill(osc_buff[1], ret, bufferlen, 0f);
+ }
+
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+
+ SoftAudioBuffer left = buffer[SoftMainMixer.CHANNEL_LEFT];
+ SoftAudioBuffer right = buffer[SoftMainMixer.CHANNEL_RIGHT];
+ SoftAudioBuffer eff1 = buffer[SoftMainMixer.CHANNEL_EFFECT1];
+ SoftAudioBuffer eff2 = buffer[SoftMainMixer.CHANNEL_EFFECT2];
+ SoftAudioBuffer leftdry = buffer[SoftMainMixer.CHANNEL_LEFT_DRY];
+ SoftAudioBuffer rightdry = buffer[SoftMainMixer.CHANNEL_RIGHT_DRY];
+
+ if (osc_stream_nrofchannels == 1)
+ rightdry = null;
+
+ if (!Double.isInfinite(co_filter_freq[0])) {
+ filter_left.processAudio(leftdry);
+ if (rightdry != null)
+ filter_right.processAudio(rightdry);
+ }
+
+ if (nrofchannels == 1) {
+ out_mixer_left = (out_mixer_left + out_mixer_right) / 2;
+ mixAudioStream(leftdry, left, last_out_mixer_left, out_mixer_left);
+ if (rightdry != null)
+ mixAudioStream(rightdry, left, last_out_mixer_left,
+ out_mixer_left);
+ } else {
+ mixAudioStream(leftdry, left, last_out_mixer_left, out_mixer_left);
+ if (rightdry != null)
+ mixAudioStream(rightdry, right, last_out_mixer_right,
+ out_mixer_right);
+ else
+ mixAudioStream(leftdry, right, last_out_mixer_right,
+ out_mixer_right);
+ }
+
+ if (rightdry == null) {
+ mixAudioStream(leftdry, eff1, last_out_mixer_effect1,
+ out_mixer_effect1);
+ mixAudioStream(leftdry, eff2, last_out_mixer_effect2,
+ out_mixer_effect2);
+ } else {
+ mixAudioStream(leftdry, eff1, last_out_mixer_effect1 * 0.5f,
+ out_mixer_effect1 * 0.5f);
+ mixAudioStream(leftdry, eff2, last_out_mixer_effect2 * 0.5f,
+ out_mixer_effect2 * 0.5f);
+ mixAudioStream(rightdry, eff1, last_out_mixer_effect1 * 0.5f,
+ out_mixer_effect1 * 0.5f);
+ mixAudioStream(rightdry, eff2, last_out_mixer_effect2 * 0.5f,
+ out_mixer_effect2 * 0.5f);
+ }
+
+ last_out_mixer_left = out_mixer_left;
+ last_out_mixer_right = out_mixer_right;
+ last_out_mixer_effect1 = out_mixer_effect1;
+ last_out_mixer_effect2 = out_mixer_effect2;
+
+ if (out_mixer_end) {
+ stopping = true;
+ }
+
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java b/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java
new file mode 100644
index 000000000..8f9effee3
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.sound.sampled.AudioFormat.Encoding;
+import javax.sound.sampled.spi.AudioFileReader;
+
+/**
+ * WAVE file reader for files using format WAVE_FORMAT_EXTENSIBLE (0xFFFE).
+ *
+ * @author Karl Helgason
+ */
+public class WaveExtensibleFileReader extends AudioFileReader {
+
+ static private class GUID {
+ long i1;
+
+ int s1;
+
+ int s2;
+
+ int x1;
+
+ int x2;
+
+ int x3;
+
+ int x4;
+
+ int x5;
+
+ int x6;
+
+ int x7;
+
+ int x8;
+
+ private GUID() {
+ }
+
+ public GUID(long i1, int s1, int s2, int x1, int x2, int x3, int x4,
+ int x5, int x6, int x7, int x8) {
+ this.i1 = i1;
+ this.s1 = s1;
+ this.s2 = s2;
+ this.x1 = x1;
+ this.x2 = x2;
+ this.x3 = x3;
+ this.x4 = x4;
+ this.x5 = x5;
+ this.x6 = x6;
+ this.x7 = x7;
+ this.x8 = x8;
+ }
+
+ public static GUID read(RIFFReader riff) throws IOException {
+ GUID d = new GUID();
+ d.i1 = riff.readUnsignedInt();
+ d.s1 = riff.readUnsignedShort();
+ d.s2 = riff.readUnsignedShort();
+ d.x1 = riff.readUnsignedByte();
+ d.x2 = riff.readUnsignedByte();
+ d.x3 = riff.readUnsignedByte();
+ d.x4 = riff.readUnsignedByte();
+ d.x5 = riff.readUnsignedByte();
+ d.x6 = riff.readUnsignedByte();
+ d.x7 = riff.readUnsignedByte();
+ d.x8 = riff.readUnsignedByte();
+ return d;
+ }
+
+ public int hashCode() {
+ return (int) i1;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GUID))
+ return false;
+ GUID t = (GUID) obj;
+ if (i1 != t.i1)
+ return false;
+ if (s1 != t.s1)
+ return false;
+ if (s2 != t.s2)
+ return false;
+ if (x1 != t.x1)
+ return false;
+ if (x2 != t.x2)
+ return false;
+ if (x3 != t.x3)
+ return false;
+ if (x4 != t.x4)
+ return false;
+ if (x5 != t.x5)
+ return false;
+ if (x6 != t.x6)
+ return false;
+ if (x7 != t.x7)
+ return false;
+ if (x8 != t.x8)
+ return false;
+ return true;
+ }
+
+ }
+
+ private static String[] channelnames = { "FL", "FR", "FC", "LF",
+ "BL",
+ "BR", // 5.1
+ "FLC", "FLR", "BC", "SL", "SR", "TC", "TFL", "TFC", "TFR", "TBL",
+ "TBC", "TBR" };
+
+ private static String[] allchannelnames = { "w1", "w2", "w3", "w4", "w5",
+ "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
+ "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", "w24",
+ "w25", "w26", "w27", "w28", "w29", "w30", "w31", "w32", "w33",
+ "w34", "w35", "w36", "w37", "w38", "w39", "w40", "w41", "w42",
+ "w43", "w44", "w45", "w46", "w47", "w48", "w49", "w50", "w51",
+ "w52", "w53", "w54", "w55", "w56", "w57", "w58", "w59", "w60",
+ "w61", "w62", "w63", "w64" };
+
+ private static GUID SUBTYPE_PCM = new GUID(0x00000001, 0x0000, 0x0010,
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+ private static GUID SUBTYPE_IEEE_FLOAT = new GUID(0x00000003, 0x0000,
+ 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+ private String decodeChannelMask(long channelmask) {
+ StringBuffer sb = new StringBuffer();
+ long m = 1;
+ for (int i = 0; i < allchannelnames.length; i++) {
+ if ((channelmask & m) != 0L) {
+ if (i < channelnames.length) {
+ sb.append(channelnames[i] + " ");
+ } else {
+ sb.append(allchannelnames[i] + " ");
+ }
+ }
+ m *= 2L;
+ }
+ if (sb.length() == 0)
+ return null;
+ return sb.substring(0, sb.length() - 1);
+
+ }
+
+ public AudioFileFormat getAudioFileFormat(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+
+ stream.mark(200);
+ AudioFileFormat format;
+ try {
+ format = internal_getAudioFileFormat(stream);
+ } finally {
+ stream.reset();
+ }
+ return format;
+ }
+
+ private AudioFileFormat internal_getAudioFileFormat(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+
+ RIFFReader riffiterator = new RIFFReader(stream);
+ if (!riffiterator.getFormat().equals("RIFF"))
+ throw new UnsupportedAudioFileException();
+ if (!riffiterator.getType().equals("WAVE"))
+ throw new UnsupportedAudioFileException();
+
+ boolean fmt_found = false;
+ boolean data_found = false;
+
+ int channels = 1;
+ long samplerate = 1;
+ // long framerate = 1;
+ int framesize = 1;
+ int bits = 1;
+ int validBitsPerSample = 1;
+ long channelMask = 0;
+ GUID subFormat = null;
+
+ while (riffiterator.hasNextChunk()) {
+ RIFFReader chunk = riffiterator.nextChunk();
+
+ if (chunk.getFormat().equals("fmt ")) {
+ fmt_found = true;
+
+ int format = chunk.readUnsignedShort();
+ if (format != 0xFFFE)
+ throw new UnsupportedAudioFileException(); // WAVE_FORMAT_EXTENSIBLE
+ // only
+ channels = chunk.readUnsignedShort();
+ samplerate = chunk.readUnsignedInt();
+ /* framerate = */chunk.readUnsignedInt();
+ framesize = chunk.readUnsignedShort();
+ bits = chunk.readUnsignedShort();
+ int cbSize = chunk.readUnsignedShort();
+ if (cbSize != 22)
+ throw new UnsupportedAudioFileException();
+ validBitsPerSample = chunk.readUnsignedShort();
+ if (validBitsPerSample > bits)
+ throw new UnsupportedAudioFileException();
+ channelMask = chunk.readUnsignedInt();
+ subFormat = GUID.read(chunk);
+
+ }
+ if (chunk.getFormat().equals("data")) {
+ data_found = true;
+ break;
+ }
+ }
+
+ if (!fmt_found)
+ throw new UnsupportedAudioFileException();
+ if (!data_found)
+ throw new UnsupportedAudioFileException();
+
+ Map<String, Object> p = new HashMap<String, Object>();
+ String s_channelmask = decodeChannelMask(channelMask);
+ if (s_channelmask != null)
+ p.put("channelOrder", s_channelmask);
+ if (channelMask != 0)
+ p.put("channelMask", channelMask);
+ // validBitsPerSample is only informational for PCM data,
+ // data is still encode according to SampleSizeInBits.
+ p.put("validBitsPerSample", validBitsPerSample);
+
+ AudioFormat audioformat = null;
+ if (subFormat.equals(SUBTYPE_PCM)) {
+ if (bits == 8) {
+ audioformat = new AudioFormat(Encoding.PCM_UNSIGNED,
+ samplerate, bits, channels, framesize, samplerate,
+ false, p);
+ } else {
+ audioformat = new AudioFormat(Encoding.PCM_SIGNED, samplerate,
+ bits, channels, framesize, samplerate, false, p);
+ }
+ } else if (subFormat.equals(SUBTYPE_IEEE_FLOAT)) {
+ audioformat = new AudioFormat(AudioFloatConverter.PCM_FLOAT,
+ samplerate, bits, channels, framesize, samplerate, false, p);
+ } else
+ throw new UnsupportedAudioFileException();
+
+ AudioFileFormat fileformat = new AudioFileFormat(
+ AudioFileFormat.Type.WAVE, audioformat,
+ AudioSystem.NOT_SPECIFIED);
+ return fileformat;
+ }
+
+ public AudioInputStream getAudioInputStream(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+
+ AudioFileFormat format = getAudioFileFormat(stream);
+ RIFFReader riffiterator = new RIFFReader(stream);
+ if (!riffiterator.getFormat().equals("RIFF"))
+ throw new UnsupportedAudioFileException();
+ if (!riffiterator.getType().equals("WAVE"))
+ throw new UnsupportedAudioFileException();
+ while (riffiterator.hasNextChunk()) {
+ RIFFReader chunk = riffiterator.nextChunk();
+ if (chunk.getFormat().equals("data")) {
+ return new AudioInputStream(chunk, format.getFormat(), chunk
+ .getSize());
+ }
+ }
+ throw new UnsupportedAudioFileException();
+ }
+
+ public AudioFileFormat getAudioFileFormat(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ InputStream stream = url.openStream();
+ AudioFileFormat format;
+ try {
+ format = getAudioFileFormat(new BufferedInputStream(stream));
+ } finally {
+ stream.close();
+ }
+ return format;
+ }
+
+ public AudioFileFormat getAudioFileFormat(File file)
+ throws UnsupportedAudioFileException, IOException {
+ InputStream stream = new FileInputStream(file);
+ AudioFileFormat format;
+ try {
+ format = getAudioFileFormat(new BufferedInputStream(stream));
+ } finally {
+ stream.close();
+ }
+ return format;
+ }
+
+ public AudioInputStream getAudioInputStream(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ return getAudioInputStream(new BufferedInputStream(url.openStream()));
+ }
+
+ public AudioInputStream getAudioInputStream(File file)
+ throws UnsupportedAudioFileException, IOException {
+ return getAudioInputStream(new BufferedInputStream(new FileInputStream(
+ file)));
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java b/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java
new file mode 100644
index 000000000..b096e4a51
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.sound.sampled.spi.AudioFileReader;
+
+/**
+ * Floating-point encoded (format 3) WAVE file loader.
+ *
+ * @author Karl Helgason
+ */
+public class WaveFloatFileReader extends AudioFileReader {
+
+ public AudioFileFormat getAudioFileFormat(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+
+ stream.mark(200);
+ AudioFileFormat format;
+ try {
+ format = internal_getAudioFileFormat(stream);
+ } finally {
+ stream.reset();
+ }
+ return format;
+ }
+
+ private AudioFileFormat internal_getAudioFileFormat(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+
+ RIFFReader riffiterator = new RIFFReader(stream);
+ if (!riffiterator.getFormat().equals("RIFF"))
+ throw new UnsupportedAudioFileException();
+ if (!riffiterator.getType().equals("WAVE"))
+ throw new UnsupportedAudioFileException();
+
+ boolean fmt_found = false;
+ boolean data_found = false;
+
+ int channels = 1;
+ long samplerate = 1;
+ int framesize = 1;
+ int bits = 1;
+
+ while (riffiterator.hasNextChunk()) {
+ RIFFReader chunk = riffiterator.nextChunk();
+
+ if (chunk.getFormat().equals("fmt ")) {
+ fmt_found = true;
+
+ int format = chunk.readUnsignedShort();
+ if (format != 3) // WAVE_FORMAT_IEEE_FLOAT only
+ throw new UnsupportedAudioFileException();
+ channels = chunk.readUnsignedShort();
+ samplerate = chunk.readUnsignedInt();
+ /* framerate = */chunk.readUnsignedInt();
+ framesize = chunk.readUnsignedShort();
+ bits = chunk.readUnsignedShort();
+ }
+ if (chunk.getFormat().equals("data")) {
+ data_found = true;
+ break;
+ }
+ }
+
+ if (!fmt_found)
+ throw new UnsupportedAudioFileException();
+ if (!data_found)
+ throw new UnsupportedAudioFileException();
+
+ AudioFormat audioformat = new AudioFormat(
+ AudioFloatConverter.PCM_FLOAT, samplerate, bits, channels,
+ framesize, samplerate, false);
+ AudioFileFormat fileformat = new AudioFileFormat(
+ AudioFileFormat.Type.WAVE, audioformat,
+ AudioSystem.NOT_SPECIFIED);
+ return fileformat;
+ }
+
+ public AudioInputStream getAudioInputStream(InputStream stream)
+ throws UnsupportedAudioFileException, IOException {
+
+ AudioFileFormat format = getAudioFileFormat(stream);
+ RIFFReader riffiterator = new RIFFReader(stream);
+ if (!riffiterator.getFormat().equals("RIFF"))
+ throw new UnsupportedAudioFileException();
+ if (!riffiterator.getType().equals("WAVE"))
+ throw new UnsupportedAudioFileException();
+ while (riffiterator.hasNextChunk()) {
+ RIFFReader chunk = riffiterator.nextChunk();
+ if (chunk.getFormat().equals("data")) {
+ return new AudioInputStream(chunk, format.getFormat(),
+ chunk.getSize());
+ }
+ }
+ throw new UnsupportedAudioFileException();
+ }
+
+ public AudioFileFormat getAudioFileFormat(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ InputStream stream = url.openStream();
+ AudioFileFormat format;
+ try {
+ format = getAudioFileFormat(new BufferedInputStream(stream));
+ } finally {
+ stream.close();
+ }
+ return format;
+ }
+
+ public AudioFileFormat getAudioFileFormat(File file)
+ throws UnsupportedAudioFileException, IOException {
+ InputStream stream = new FileInputStream(file);
+ AudioFileFormat format;
+ try {
+ format = getAudioFileFormat(new BufferedInputStream(stream));
+ } finally {
+ stream.close();
+ }
+ return format;
+ }
+
+ public AudioInputStream getAudioInputStream(URL url)
+ throws UnsupportedAudioFileException, IOException {
+ return getAudioInputStream(new BufferedInputStream(url.openStream()));
+ }
+
+ public AudioInputStream getAudioInputStream(File file)
+ throws UnsupportedAudioFileException, IOException {
+ return getAudioInputStream(new BufferedInputStream(new FileInputStream(
+ file)));
+ }
+}
diff --git a/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java b/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java
new file mode 100644
index 000000000..ff11bd464
--- /dev/null
+++ b/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.sun.media.sound;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.AudioFileFormat.Type;
+import javax.sound.sampled.spi.AudioFileWriter;
+
+/**
+ * Floating-point encoded (format 3) WAVE file writer.
+ *
+ * @author Karl Helgason
+ */
+public class WaveFloatFileWriter extends AudioFileWriter {
+
+ public Type[] getAudioFileTypes() {
+ return new Type[] { Type.WAVE };
+ }
+
+ public Type[] getAudioFileTypes(AudioInputStream stream) {
+
+ if (!stream.getFormat().getEncoding().equals(
+ AudioFloatConverter.PCM_FLOAT))
+ return new Type[0];
+ return new Type[] { Type.WAVE };
+ }
+
+ private void checkFormat(AudioFileFormat.Type type, AudioInputStream stream) {
+ if (!Type.WAVE.equals(type))
+ throw new IllegalArgumentException("File type " + type
+ + " not supported.");
+ if (!stream.getFormat().getEncoding().equals(
+ AudioFloatConverter.PCM_FLOAT))
+ throw new IllegalArgumentException("File format "
+ + stream.getFormat() + " not supported.");
+ }
+
+ public void write(AudioInputStream stream, RIFFWriter writer)
+ throws IOException {
+
+ RIFFWriter fmt_chunk = writer.writeChunk("fmt ");
+
+ AudioFormat format = stream.getFormat();
+ fmt_chunk.writeUnsignedShort(3); // WAVE_FORMAT_IEEE_FLOAT
+ fmt_chunk.writeUnsignedShort(format.getChannels());
+ fmt_chunk.writeUnsignedInt((int) format.getSampleRate());
+ fmt_chunk.writeUnsignedInt(((int) format.getFrameRate())
+ * format.getFrameSize());
+ fmt_chunk.writeUnsignedShort(format.getFrameSize());
+ fmt_chunk.writeUnsignedShort(format.getSampleSizeInBits());
+ fmt_chunk.close();
+ RIFFWriter data_chunk = writer.writeChunk("data");
+ byte[] buff = new byte[1024];
+ int len;
+ while ((len = stream.read(buff, 0, buff.length)) != -1)
+ data_chunk.write(buff, 0, len);
+ data_chunk.close();
+ }
+
+ private static class NoCloseOutputStream extends OutputStream {
+ OutputStream out;
+
+ public NoCloseOutputStream(OutputStream out) {
+ this.out = out;
+ }
+
+ public void write(int b) throws IOException {
+ out.write(b);
+ }
+
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ }
+
+ public void write(byte[] b) throws IOException {
+ out.write(b);
+ }
+ }
+
+ private AudioInputStream toLittleEndian(AudioInputStream ais) {
+ AudioFormat format = ais.getFormat();
+ AudioFormat targetFormat = new AudioFormat(format.getEncoding(), format
+ .getSampleRate(), format.getSampleSizeInBits(), format
+ .getChannels(), format.getFrameSize(), format.getFrameRate(),
+ false);
+ return AudioSystem.getAudioInputStream(targetFormat, ais);
+ }
+
+ public int write(AudioInputStream stream, Type fileType, OutputStream out)
+ throws IOException {
+
+ checkFormat(fileType, stream);
+ if (stream.getFormat().isBigEndian())
+ stream = toLittleEndian(stream);
+ RIFFWriter writer = new RIFFWriter(new NoCloseOutputStream(out), "WAVE");
+ write(stream, writer);
+ int fpointer = (int) writer.getFilePointer();
+ writer.close();
+ return fpointer;
+ }
+
+ public int write(AudioInputStream stream, Type fileType, File out)
+ throws IOException {
+ checkFormat(fileType, stream);
+ if (stream.getFormat().isBigEndian())
+ stream = toLittleEndian(stream);
+ RIFFWriter writer = new RIFFWriter(out, "WAVE");
+ write(stream, writer);
+ int fpointer = (int) writer.getFilePointer();
+ writer.close();
+ return fpointer;
+ }
+
+}
diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiDeviceProvider b/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiDeviceProvider
index e0bbb1025..bffb952eb 100644
--- a/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiDeviceProvider
+++ b/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiDeviceProvider
@@ -1,5 +1,5 @@
# Providers for midi devices
-com.sun.media.sound.MixerSynthProvider
com.sun.media.sound.RealTimeSequencerProvider
com.sun.media.sound.MidiOutDeviceProvider
com.sun.media.sound.MidiInDeviceProvider
+com.sun.media.sound.SoftProvider
diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiFileReader b/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiFileReader
index 2daf82731..1d643a0e4 100644
--- a/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiFileReader
+++ b/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiFileReader
@@ -1,3 +1,2 @@
# Providers for midi sequences
com.sun.media.sound.StandardMidiFileReader
-com.sun.media.sound.RmfFileReader
diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.SoundbankReader b/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.SoundbankReader
index 85a8af274..03c3df820 100644
--- a/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.SoundbankReader
+++ b/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.SoundbankReader
@@ -1,2 +1,5 @@
# Providers for Soundbanks
-com.sun.media.sound.HsbParser
+com.sun.media.sound.SF2SoundbankReader
+com.sun.media.sound.DLSSoundbankReader
+com.sun.media.sound.AudioFileSoundbankReader
+com.sun.media.sound.JARSoundbankReader
diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.AudioFileReader b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.AudioFileReader
index db1cd3044..624dac1c0 100644
--- a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.AudioFileReader
+++ b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.AudioFileReader
@@ -2,3 +2,5 @@
com.sun.media.sound.AuFileReader
com.sun.media.sound.AiffFileReader
com.sun.media.sound.WaveFileReader
+com.sun.media.sound.WaveFloatFileReader
+com.sun.media.sound.SoftMidiAudioFileReader
diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider
index 7f452444c..2ea4f8b80 100644
--- a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider
+++ b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider
@@ -2,3 +2,4 @@
com.sun.media.sound.UlawCodec
com.sun.media.sound.AlawCodec
com.sun.media.sound.PCMtoPCMCodec
+com.sun.media.sound.AudioFloatFormatConverter
diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.MixerProvider b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.MixerProvider
index fc551ed39..5414bee18 100644
--- a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.MixerProvider
+++ b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.MixerProvider
@@ -1,5 +1,3 @@
# last mixer is default mixer
com.sun.media.sound.PortMixerProvider
-com.sun.media.sound.SimpleInputDeviceProvider
-com.sun.media.sound.HeadspaceMixerProvider
com.sun.media.sound.DirectAudioDeviceProvider
diff --git a/src/share/classes/com/sun/nio/file/ExtendedCopyOption.java b/src/share/classes/com/sun/nio/file/ExtendedCopyOption.java
new file mode 100644
index 000000000..b612c0e8d
--- /dev/null
+++ b/src/share/classes/com/sun/nio/file/ExtendedCopyOption.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.nio.file;
+
+import java.nio.file.CopyOption;
+
+/**
+ * Defines <em>extended</em> copy options supported on some platforms
+ * by Sun's provider implementation.
+ *
+ * @since 1.7
+ */
+
+public enum ExtendedCopyOption implements CopyOption {
+ /**
+ * The copy may be interrupted by the {@link Thread#interrupt interrupt}
+ * method.
+ */
+ INTERRUPTIBLE,
+}
diff --git a/src/windows/native/sun/windows/awt_Multimon.h b/src/share/classes/com/sun/nio/file/ExtendedOpenOption.java
index f2b371e34..25208d812 100644
--- a/src/windows/native/sun/windows/awt_Multimon.h
+++ b/src/share/classes/com/sun/nio/file/ExtendedOpenOption.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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,33 +22,29 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
-#ifndef _INC_MULTIMON_
-#define _INC_MULTIMON_
-#endif
-//
-// build defines that replace the regular APIs with our versions
-//
-#undef GetMonitorInfo
-#undef GetSystemMetrics
-#undef MonitorFromWindow
-#undef MonitorFromRect
-#undef MonitorFromPoint
-#undef EnumDisplayMonitors
-#undef EnumDisplayDevices
-#include "awt_MMStub.h"
+package com.sun.nio.file;
-#define GetSystemMetricsMM _getSystemMetrics
-#define MonitorFromWindow _monitorFromWindow
-#define MonitorFromRect _monitorFromRect
-#define MonitorFromPoint _monitorFromPoint
-#define GetMonitorInfo _getMonitorInfo
-#define EnumDisplayMonitors _enumDisplayMonitors
-#define EnumDisplayDevices _enumDisplayDevices
+import java.nio.file.OpenOption;
+/**
+ * Defines <em>extended</em> open options supported on some platforms
+ * by Sun's provider implementation.
+ *
+ * @since 1.7
+ */
-#define CountMonitors _countMonitors
-#define CollectMonitors _collectMonitors
-#define MonitorBounds _monitorBounds
-#define MakeDCFromMonitor _makeDCFromMonitor
-#define CreateWindowOnMonitor _createWindowOM
+public enum ExtendedOpenOption implements OpenOption {
+ /**
+ * Prevent operations on the file that request read access.
+ */
+ NOSHARE_READ,
+ /**
+ * Prevent operations on the file that request write access.
+ */
+ NOSHARE_WRITE,
+ /**
+ * Prevent operations on the file that request delete access.
+ */
+ NOSHARE_DELETE;
+}
diff --git a/src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java b/src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java
new file mode 100644
index 000000000..0f6ddc327
--- /dev/null
+++ b/src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.nio.file;
+
+import java.nio.file.WatchEvent.Modifier;
+
+/**
+ * Defines <em>extended</em> watch event modifiers supported on some platforms
+ * by Sun's provider implementation.
+ *
+ * @since 1.7
+ */
+
+public enum ExtendedWatchEventModifier implements Modifier {
+
+ /**
+ * Register a file tree instead of a single directory.
+ */
+ FILE_TREE,
+}
diff --git a/src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java b/src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java
new file mode 100644
index 000000000..57ab111b0
--- /dev/null
+++ b/src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.nio.file;
+
+import java.nio.file.WatchEvent.Modifier;
+
+/**
+ * Defines the <em>sensitivity levels</em> when registering objects with a
+ * watch service implementation that polls the file system.
+ *
+ * @since 1.7
+ */
+
+public enum SensitivityWatchEventModifier implements Modifier {
+ /**
+ * High sensitivity.
+ */
+ HIGH(2),
+ /**
+ * Medium sensitivity.
+ */
+ MEDIUM(10),
+ /**
+ * Low sensitivity.
+ */
+ LOW(30);
+
+ /**
+ * Returns the sensitivity in seconds.
+ */
+ public int sensitivityValueInSeconds() {
+ return sensitivity;
+ }
+
+ private final int sensitivity;
+ private SensitivityWatchEventModifier(int sensitivity) {
+ this.sensitivity = sensitivity;
+ }
+}
diff --git a/src/share/classes/java/awt/Choice.java b/src/share/classes/java/awt/Choice.java
index ea46b8d77..64c46b53d 100644
--- a/src/share/classes/java/awt/Choice.java
+++ b/src/share/classes/java/awt/Choice.java
@@ -228,7 +228,7 @@ public class Choice extends Component implements ItemSelectable, Accessible {
pItems.insertElementAt(item, index);
ChoicePeer peer = (ChoicePeer)this.peer;
if (peer != null) {
- peer.addItem(item, index);
+ peer.add(item, index);
}
// no selection or selection shifted up
if (selectedIndex < 0 || selectedIndex >= index) {
diff --git a/src/share/classes/java/awt/Component.java b/src/share/classes/java/awt/Component.java
index 8896ec248..afe091dcc 100644
--- a/src/share/classes/java/awt/Component.java
+++ b/src/share/classes/java/awt/Component.java
@@ -65,8 +65,10 @@ import java.applet.Applet;
import sun.security.action.GetPropertyAction;
import sun.awt.AppContext;
+import sun.awt.AWTAccessor;
import sun.awt.ConstrainableGraphics;
import sun.awt.SubRegionShowable;
+import sun.awt.SunToolkit;
import sun.awt.WindowClosingListener;
import sun.awt.CausedFocusEvent;
import sun.awt.EmbeddedFrame;
@@ -758,22 +760,26 @@ public abstract class Component implements ImageObserver, MenuContainer,
* The shape set with the applyCompoundShape() method. It uncludes the result
* of the HW/LW mixing related shape computation. It may also include
* the user-specified shape of the component.
+ * The 'null' value means the component has normal shape (or has no shape at all)
+ * and applyCompoundShape() will skip the following shape identical to normal.
*/
private transient Region compoundShape = null;
/*
+ * Represents the shape of this lightweight component to be cut out from
+ * heavyweight components should they intersect. Possible values:
+ * 1. null - consider the shape rectangular
+ * 2. EMPTY_REGION - nothing gets cut out (children still get cut out)
+ * 3. non-empty - this shape gets cut out.
+ */
+ private transient Region mixingCutoutRegion = null;
+
+ /*
* Indicates whether addNotify() is complete
* (i.e. the peer is created).
*/
private transient boolean isAddNotifyComplete = false;
- private static final PropertyChangeListener opaquePropertyChangeListener =
- new PropertyChangeListener() {
- public void propertyChange(java.beans.PropertyChangeEvent evt) {
- ((Component)evt.getSource()).mixOnOpaqueChanging();
- }
- };
-
/**
* Should only be used in subclass getBounds to check that part of bounds
* is actualy changing
@@ -793,6 +799,39 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
}
+ static {
+ AWTAccessor.setComponentAccessor(new AWTAccessor.ComponentAccessor() {
+ public void setMixingCutoutShape(Component comp, Shape shape) {
+ Region region = shape == null ? null :
+ Region.getInstance(shape, null);
+
+ synchronized (comp.getTreeLock()) {
+ boolean needShowing = false;
+ boolean needHiding = false;
+
+ if (!comp.isNonOpaqueForMixing()) {
+ needHiding = true;
+ }
+
+ comp.mixingCutoutRegion = region;
+
+ if (!comp.isNonOpaqueForMixing()) {
+ needShowing = true;
+ }
+
+ if (comp.isMixingNeeded()) {
+ if (needHiding) {
+ comp.mixOnHiding(comp.isLightweight());
+ }
+ if (needShowing) {
+ comp.mixOnShowing();
+ }
+ }
+ }
+ }
+ });
+ }
+
/**
* Constructs a new component. Class <code>Component</code> can be
* extended directly to create a lightweight component that does not
@@ -1306,7 +1345,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
enabled = true;
ComponentPeer peer = this.peer;
if (peer != null) {
- peer.enable();
+ peer.setEnabled(true);
if (visible) {
updateCursorImmediately();
}
@@ -1355,7 +1394,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
ComponentPeer peer = this.peer;
if (peer != null) {
- peer.disable();
+ peer.setEnabled(false);
if (visible) {
updateCursorImmediately();
}
@@ -1447,7 +1486,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
mixOnShowing();
ComponentPeer peer = this.peer;
if (peer != null) {
- peer.show();
+ peer.setVisible(true);
createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
this, parent,
HierarchyEvent.SHOWING_CHANGED,
@@ -1517,7 +1556,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
ComponentPeer peer = this.peer;
if (peer != null) {
- peer.hide();
+ peer.setVisible(false);
createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
this, parent,
HierarchyEvent.SHOWING_CHANGED,
@@ -2414,7 +2453,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
if (dim == null || !(isPreferredSizeSet() || isValid())) {
synchronized (getTreeLock()) {
prefSize = (peer != null) ?
- peer.preferredSize() :
+ peer.getPreferredSize() :
getMinimumSize();
dim = prefSize;
}
@@ -2484,7 +2523,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
if (dim == null || !(isMinimumSizeSet() || isValid())) {
synchronized (getTreeLock()) {
minSize = (peer != null) ?
- peer.minimumSize() :
+ peer.getMinimumSize() :
size();
dim = minSize;
}
@@ -3171,7 +3210,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
private Insets getInsets_NoClientCode() {
ComponentPeer peer = this.peer;
if (peer instanceof ContainerPeer) {
- return (Insets)((ContainerPeer)peer).insets().clone();
+ return (Insets)((ContainerPeer)peer).getInsets().clone();
}
return new Insets(0, 0, 0, 0);
}
@@ -6643,7 +6682,6 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
if (!isAddNotifyComplete) {
- addPropertyChangeListener("opaque", opaquePropertyChangeListener);
mixOnShowing();
}
@@ -6722,7 +6760,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
// Hide peer first to stop system events such as cursor moves.
if (visible) {
- p.hide();
+ p.setVisible(false);
}
peer = null; // Stop peer updates.
@@ -6735,9 +6773,11 @@ public abstract class Component implements ImageObserver, MenuContainer,
p.dispose();
mixOnHiding(isLightweight);
- removePropertyChangeListener("opaque", opaquePropertyChangeListener);
isAddNotifyComplete = false;
+ // Nullifying compoundShape means that the component has normal shape
+ // (or has no shape at all).
+ this.compoundShape = null;
}
if (hierarchyListener != null ||
@@ -9401,10 +9441,9 @@ public abstract class Component implements ImageObserver, MenuContainer,
* Null-layout of the container or absence of the container mean
* the bounds of the component are final and can be trusted.
*/
- private boolean areBoundsValid() {
+ final boolean areBoundsValid() {
Container cont = getContainer();
- return cont == null || cont.isValid()
- || cont.getLayout() == null;
+ return cont == null || cont.isValid() || cont.getLayout() == null;
}
/**
@@ -9413,6 +9452,14 @@ public abstract class Component implements ImageObserver, MenuContainer,
*/
void applyCompoundShape(Region shape) {
checkTreeLock();
+
+ if (!areBoundsValid()) {
+ if (mixingLog.isLoggable(Level.FINE)) {
+ mixingLog.fine("this = " + this + "; areBoundsValid = " + areBoundsValid());
+ }
+ return;
+ }
+
if (!isLightweight()) {
ComponentPeer peer = getPeer();
if (peer != null) {
@@ -9422,22 +9469,31 @@ public abstract class Component implements ImageObserver, MenuContainer,
// with some incorrect Region object with loX being
// greater than the hiX for instance.
if (shape.isEmpty()) {
- shape = Region.getInstanceXYWH(0, 0, 0, 0);
+ shape = Region.EMPTY_REGION;
}
+
// Note: the shape is not really copied/cloned. We create
// the Region object ourselves, so there's no any possibility
// to modify the object outside of the mixing code.
- this.compoundShape = shape;
-
- if (areBoundsValid()) {
+ // Nullifying compoundShape means that the component has normal shape
+ // (or has no shape at all).
+ if (shape.equals(getNormalShape())) {
+ if (this.compoundShape == null) {
+ return;
+ }
+ this.compoundShape = null;
+ peer.applyShape(null);
+ } else {
+ if (shape.equals(getAppliedShape())) {
+ return;
+ }
+ this.compoundShape = shape;
Point compAbsolute = getLocationOnWindow();
-
if (mixingLog.isLoggable(Level.FINER)) {
mixingLog.fine("this = " + this +
- "; compAbsolute=" + compAbsolute + "; shape=" + shape);
+ "; compAbsolute=" + compAbsolute + "; shape=" + shape);
}
-
peer.applyShape(shape.getTranslatedRegion(-compAbsolute.x, -compAbsolute.y));
}
}
@@ -9460,7 +9516,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
Point curLocation = getLocation();
for (Container parent = getContainer();
- parent != null;
+ parent != null && !(parent instanceof Window);
parent = parent.getContainer())
{
curLocation.x += parent.getX();
@@ -9486,7 +9542,28 @@ public abstract class Component implements ImageObserver, MenuContainer,
);
}
- private int getSiblingIndexAbove() {
+ /**
+ * Returns the "opaque shape" of the component.
+ *
+ * The opaque shape of a lightweight components is the actual shape that
+ * needs to be cut off of the heavyweight components in order to mix this
+ * lightweight component correctly with them.
+ *
+ * The method is overriden in the java.awt.Container to handle non-opaque
+ * containers containing opaque children.
+ *
+ * See 6637655 for details.
+ */
+ Region getOpaqueShape() {
+ checkTreeLock();
+ if (mixingCutoutRegion != null) {
+ return mixingCutoutRegion;
+ } else {
+ return getNormalShape();
+ }
+ }
+
+ final int getSiblingIndexAbove() {
checkTreeLock();
Container parent = getContainer();
if (parent == null) {
@@ -9498,7 +9575,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
return nextAbove < 0 ? -1 : nextAbove;
}
- private int getSiblingIndexBelow() {
+ final int getSiblingIndexBelow() {
checkTreeLock();
Container parent = getContainer();
if (parent == null) {
@@ -9510,6 +9587,11 @@ public abstract class Component implements ImageObserver, MenuContainer,
return nextBelow >= parent.getComponentCount() ? -1 : nextBelow;
}
+ final boolean isNonOpaqueForMixing() {
+ return mixingCutoutRegion != null &&
+ mixingCutoutRegion.isEmpty();
+ }
+
private Region calculateCurrentShape() {
checkTreeLock();
Region s = getNormalShape();
@@ -9532,8 +9614,8 @@ public abstract class Component implements ImageObserver, MenuContainer,
* implementation of the Container class.
*/
Component c = cont.getComponent(index);
- if (c.isLightweight() && c.isShowing() && c.isOpaque()) {
- s = s.getDifference(c.getNormalShape());
+ if (c.isLightweight() && c.isShowing()) {
+ s = s.getDifference(c.getOpaqueShape());
}
}
@@ -9558,6 +9640,9 @@ public abstract class Component implements ImageObserver, MenuContainer,
void applyCurrentShape() {
checkTreeLock();
if (!areBoundsValid()) {
+ if (mixingLog.isLoggable(Level.FINE)) {
+ mixingLog.fine("this = " + this + "; areBoundsValid = " + areBoundsValid());
+ }
return; // Because applyCompoundShape() ignores such components anyway
}
if (mixingLog.isLoggable(Level.FINE)) {
@@ -9576,16 +9661,54 @@ public abstract class Component implements ImageObserver, MenuContainer,
applyCompoundShape(getAppliedShape().getDifference(s));
}
+ private final void applyCurrentShapeBelowMe() {
+ checkTreeLock();
+ Container parent = getContainer();
+ if (parent != null && parent.isShowing()) {
+ // First, reapply shapes of my siblings
+ parent.recursiveApplyCurrentShape(getSiblingIndexBelow());
+
+ // Second, if my container is non-opaque, reapply shapes of siblings of my container
+ Container parent2 = parent.getContainer();
+ while (!parent.isOpaque() && parent2 != null) {
+ parent2.recursiveApplyCurrentShape(parent.getSiblingIndexBelow());
+
+ parent = parent2;
+ parent2 = parent.getContainer();
+ }
+ }
+ }
+
+ final void subtractAndApplyShapeBelowMe() {
+ checkTreeLock();
+ Container parent = getContainer();
+ if (parent != null && isShowing()) {
+ Region opaqueShape = getOpaqueShape();
+
+ // First, cut my siblings
+ parent.recursiveSubtractAndApplyShape(opaqueShape, getSiblingIndexBelow());
+
+ // Second, if my container is non-opaque, cut siblings of my container
+ Container parent2 = parent.getContainer();
+ while (!parent.isOpaque() && parent2 != null) {
+ parent2.recursiveSubtractAndApplyShape(opaqueShape, parent.getSiblingIndexBelow());
+
+ parent = parent2;
+ parent2 = parent.getContainer();
+ }
+ }
+ }
+
void mixOnShowing() {
synchronized (getTreeLock()) {
if (mixingLog.isLoggable(Level.FINE)) {
mixingLog.fine("this = " + this);
}
+ if (!isMixingNeeded()) {
+ return;
+ }
if (isLightweight()) {
- Container parent = getContainer();
- if (parent != null && isShowing() && isOpaque()) {
- parent.recursiveSubtractAndApplyShape(getNormalShape(), getSiblingIndexBelow());
- }
+ subtractAndApplyShapeBelowMe();
} else {
applyCurrentShape();
}
@@ -9599,12 +9722,12 @@ public abstract class Component implements ImageObserver, MenuContainer,
if (mixingLog.isLoggable(Level.FINE)) {
mixingLog.fine("this = " + this + "; isLightweight = " + isLightweight);
}
+ if (!isMixingNeeded()) {
+ return;
+ }
if (isLightweight) {
- Container parent = getContainer();
- if (parent != null) {
- parent.recursiveApplyCurrentShape(getSiblingIndexBelow());
- }
- } //XXX: else applyNormalShape() ???
+ applyCurrentShapeBelowMe();
+ }
}
}
@@ -9613,11 +9736,11 @@ public abstract class Component implements ImageObserver, MenuContainer,
if (mixingLog.isLoggable(Level.FINE)) {
mixingLog.fine("this = " + this);
}
+ if (!isMixingNeeded()) {
+ return;
+ }
if (isLightweight()) {
- Container parent = getContainer();
- if (parent != null) {
- parent.recursiveApplyCurrentShape(parent.getComponentZOrder(this));
- }
+ applyCurrentShapeBelowMe();
} else {
applyCurrentShape();
}
@@ -9633,11 +9756,13 @@ public abstract class Component implements ImageObserver, MenuContainer,
mixingLog.fine("this = " + this +
"; oldZorder=" + oldZorder + "; newZorder=" + newZorder + "; parent=" + parent);
}
-
+ if (!isMixingNeeded()) {
+ return;
+ }
if (isLightweight()) {
if (becameHigher) {
- if (parent != null && isShowing() && isOpaque()) {
- parent.recursiveSubtractAndApplyShape(getNormalShape(), getSiblingIndexBelow(), oldZorder);
+ if (parent != null && isShowing()) {
+ parent.recursiveSubtractAndApplyShape(getOpaqueShape(), getSiblingIndexBelow(), oldZorder);
}
} else {
if (parent != null) {
@@ -9653,8 +9778,8 @@ public abstract class Component implements ImageObserver, MenuContainer,
for (int index = oldZorder; index < newZorder; index++) {
Component c = parent.getComponent(index);
- if (c.isLightweight() && c.isShowing() && c.isOpaque()) {
- shape = shape.getDifference(c.getNormalShape());
+ if (c.isLightweight() && c.isShowing()) {
+ shape = shape.getDifference(c.getOpaqueShape());
}
}
applyCompoundShape(shape);
@@ -9664,21 +9789,42 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
}
- void mixOnOpaqueChanging() {
- if (mixingLog.isLoggable(Level.FINE)) {
- mixingLog.fine("this = " + this);
- }
- if (isOpaque()) {
- mixOnShowing();
- } else {
- mixOnHiding(isLightweight());
- }
- }
-
void mixOnValidating() {
// This method gets overriden in the Container. Obviously, a plain
// non-container components don't need to handle validation.
}
+ final boolean isMixingNeeded() {
+ if (SunToolkit.getSunAwtDisableMixing()) {
+ if (mixingLog.isLoggable(Level.FINEST)) {
+ mixingLog.finest("this = " + this + "; Mixing disabled via sun.awt.disableMixing");
+ }
+ return false;
+ }
+ if (!areBoundsValid()) {
+ if (mixingLog.isLoggable(Level.FINE)) {
+ mixingLog.fine("this = " + this + "; areBoundsValid = " + areBoundsValid());
+ }
+ return false;
+ }
+ Window window = getContainingWindow();
+ if (window != null) {
+ if (!window.hasHeavyweightDescendants() || !window.hasLightweightDescendants()) {
+ if (mixingLog.isLoggable(Level.FINE)) {
+ mixingLog.fine("containing window = " + window +
+ "; has h/w descendants = " + window.hasHeavyweightDescendants() +
+ "; has l/w descendants = " + window.hasLightweightDescendants());
+ }
+ return false;
+ }
+ } else {
+ if (mixingLog.isLoggable(Level.FINE)) {
+ mixingLog.finest("this = " + this + "; containing window is null");
+ }
+ return false;
+ }
+ return true;
+ }
+
// ****************** END OF MIXING CODE ********************************
}
diff --git a/src/share/classes/java/awt/Container.java b/src/share/classes/java/awt/Container.java
index c302e4b0e..04425d5a4 100644
--- a/src/share/classes/java/awt/Container.java
+++ b/src/share/classes/java/awt/Container.java
@@ -343,7 +343,7 @@ public class Container extends Component {
ComponentPeer peer = this.peer;
if (peer instanceof ContainerPeer) {
ContainerPeer cpeer = (ContainerPeer)peer;
- return (Insets)cpeer.insets().clone();
+ return (Insets)cpeer.getInsets().clone();
}
return new Insets(0, 0, 0, 0);
}
@@ -569,7 +569,7 @@ public class Container extends Component {
* @return true if there is at least one heavyweight children in a container, false otherwise
* @since 1.5
*/
- private boolean hasHeavyweightDescendants() {
+ final boolean hasHeavyweightDescendants() {
checkTreeLock();
return numOfHWComponents > 0;
}
@@ -580,7 +580,7 @@ public class Container extends Component {
* @return true if there is at least one lightweight children in a container, false otherwise
* @since 1.7
*/
- private boolean hasLightweightDescendants() {
+ final boolean hasLightweightDescendants() {
checkTreeLock();
return numOfLWComponents > 0;
}
@@ -3861,6 +3861,28 @@ public class Container extends Component {
return -1;
}
+ /*
+ * This method is overriden to handle opaque children in non-opaque
+ * containers.
+ */
+ @Override
+ final Region getOpaqueShape() {
+ checkTreeLock();
+ if (isLightweight() && isNonOpaqueForMixing()
+ && hasLightweightDescendants())
+ {
+ Region s = Region.EMPTY_REGION;
+ for (int index = 0; index < getComponentCount(); index++) {
+ Component c = getComponent(index);
+ if (c.isLightweight() && c.isShowing()) {
+ s = s.getUnion(c.getOpaqueShape());
+ }
+ }
+ return s.getIntersection(getNormalShape());
+ }
+ return super.getOpaqueShape();
+ }
+
final void recursiveSubtractAndApplyShape(Region shape) {
recursiveSubtractAndApplyShape(shape, getTopmostComponentIndex(), getBottommostComponentIndex());
}
@@ -3878,6 +3900,15 @@ public class Container extends Component {
if (fromZorder == -1) {
return;
}
+ if (shape.isEmpty()) {
+ return;
+ }
+ // An invalid container with not-null layout should be ignored
+ // by the mixing code, the container will be validated later
+ // and the mixing code will be executed later.
+ if (getLayout() != null && !isValid()) {
+ return;
+ }
for (int index = fromZorder; index <= toZorder; index++) {
Component comp = getComponent(index);
if (!comp.isLightweight()) {
@@ -3906,10 +3937,19 @@ public class Container extends Component {
if (fromZorder == -1) {
return;
}
+ // An invalid container with not-null layout should be ignored
+ // by the mixing code, the container will be validated later
+ // and the mixing code will be executed later.
+ if (getLayout() != null && !isValid()) {
+ return;
+ }
for (int index = fromZorder; index <= toZorder; index++) {
Component comp = getComponent(index);
if (!comp.isLightweight()) {
comp.applyCurrentShape();
+ if (comp instanceof Container && ((Container)comp).getLayout() == null) {
+ ((Container)comp).recursiveApplyCurrentShape();
+ }
} else if (comp instanceof Container &&
((Container)comp).hasHeavyweightDescendants()) {
((Container)comp).recursiveApplyCurrentShape();
@@ -3931,7 +3971,7 @@ public class Container extends Component {
if (comp.isVisible()) {
ComponentPeer peer = comp.getPeer();
if (peer != null) {
- peer.show();
+ peer.setVisible(true);
}
}
}
@@ -3952,7 +3992,7 @@ public class Container extends Component {
if (comp.isVisible()) {
ComponentPeer peer = comp.getPeer();
if (peer != null) {
- peer.hide();
+ peer.setVisible(false);
}
}
}
@@ -4000,6 +4040,10 @@ public class Container extends Component {
mixingLog.fine("this = " + this);
}
+ if (!isMixingNeeded()) {
+ return;
+ }
+
boolean isLightweight = isLightweight();
if (isLightweight && isRecursivelyVisibleUpToHeavyweightContainer()) {
@@ -4034,6 +4078,9 @@ public class Container extends Component {
if (mixingLog.isLoggable(Level.FINE)) {
mixingLog.fine("this = " + this);
}
+
+ boolean isMixingNeeded = isMixingNeeded();
+
if (isLightweight() && hasHeavyweightDescendants()) {
final Point origin = new Point(getX(), getY());
for (Container cont = getContainer();
@@ -4044,7 +4091,18 @@ public class Container extends Component {
}
recursiveRelocateHeavyweightChildren(origin);
+
+ if (!isMixingNeeded) {
+ return;
+ }
+
+ recursiveApplyCurrentShape();
+ }
+
+ if (!isMixingNeeded) {
+ return;
}
+
super.mixOnReshaping();
}
}
@@ -4057,6 +4115,10 @@ public class Container extends Component {
"; oldZ=" + oldZorder + "; newZ=" + newZorder);
}
+ if (!isMixingNeeded()) {
+ return;
+ }
+
boolean becameHigher = newZorder < oldZorder;
if (becameHigher && isLightweight() && hasHeavyweightDescendants()) {
@@ -4073,10 +4135,18 @@ public class Container extends Component {
mixingLog.fine("this = " + this);
}
+ if (!isMixingNeeded()) {
+ return;
+ }
+
if (hasHeavyweightDescendants()) {
recursiveApplyCurrentShape();
}
+ if (isLightweight() && isNonOpaqueForMixing()) {
+ subtractAndApplyShapeBelowMe();
+ }
+
super.mixOnValidating();
}
}
diff --git a/src/share/classes/java/awt/Dialog.java b/src/share/classes/java/awt/Dialog.java
index 2104a64b7..310e52350 100644
--- a/src/share/classes/java/awt/Dialog.java
+++ b/src/share/classes/java/awt/Dialog.java
@@ -941,7 +941,7 @@ public class Dialog extends Window {
// does not invoke the super.show(). So wried... :(
mixOnShowing();
- peer.show(); // now guaranteed never to block
+ peer.setVisible(true); // now guaranteed never to block
if (isModalBlocked()) {
modalBlocker.toFront();
}
diff --git a/src/share/classes/java/awt/EventDispatchThread.java b/src/share/classes/java/awt/EventDispatchThread.java
index c9b77b0b2..f35ff913a 100644
--- a/src/share/classes/java/awt/EventDispatchThread.java
+++ b/src/share/classes/java/awt/EventDispatchThread.java
@@ -300,119 +300,19 @@ class EventDispatchThread extends Thread {
}
// Can get and throw only unchecked exceptions
catch (RuntimeException e) {
- processException(e, modalFiltersCount > 0);
+ processException(e);
} catch (Error e) {
- processException(e, modalFiltersCount > 0);
+ processException(e);
}
return true;
}
- private void processException(Throwable e, boolean isModal) {
+ private void processException(Throwable e) {
if (eventLog.isLoggable(Level.FINE)) {
- eventLog.log(Level.FINE, "Processing exception: " + e +
- ", isModal = " + isModal);
+ eventLog.log(Level.FINE, "Processing exception: " + e);
}
- if (!handleException(e)) {
- // See bug ID 4499199.
- // If we are in a modal dialog, we cannot throw
- // an exception for the ThreadGroup to handle (as added
- // in RFE 4063022). If we did, the message pump of
- // the modal dialog would be interrupted.
- // We instead choose to handle the exception ourselves.
- // It may be useful to add either a runtime flag or API
- // later if someone would like to instead dispose the
- // dialog and allow the thread group to handle it.
- if (isModal) {
- System.err.println(
- "Exception occurred during event dispatching:");
- e.printStackTrace();
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException)e;
- } else if (e instanceof Error) {
- throw (Error)e;
- }
- }
- }
-
- private static final String handlerPropName = "sun.awt.exception.handler";
- private static String handlerClassName = null;
- private static String NO_HANDLER = new String();
-
- /**
- * Handles an exception thrown in the event-dispatch thread.
- *
- * <p> If the system property "sun.awt.exception.handler" is defined, then
- * when this method is invoked it will attempt to do the following:
- *
- * <ol>
- * <li> Load the class named by the value of that property, using the
- * current thread's context class loader,
- * <li> Instantiate that class using its zero-argument constructor,
- * <li> Find the resulting handler object's <tt>public void handle</tt>
- * method, which should take a single argument of type
- * <tt>Throwable</tt>, and
- * <li> Invoke the handler's <tt>handle</tt> method, passing it the
- * <tt>thrown</tt> argument that was passed to this method.
- * </ol>
- *
- * If any of the first three steps fail then this method will return
- * <tt>false</tt> and all following invocations of this method will return
- * <tt>false</tt> immediately. An exception thrown by the handler object's
- * <tt>handle</tt> will be caught, and will cause this method to return
- * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully
- * invoked, then this method will return <tt>true</tt>. This method will
- * never throw any sort of exception.
- *
- * <p> <i>Note:</i> This method is a temporary hack to work around the
- * absence of a real API that provides the ability to replace the
- * event-dispatch thread. The magic "sun.awt.exception.handler" property
- * <i>will be removed</i> in a future release.
- *
- * @param thrown The Throwable that was thrown in the event-dispatch
- * thread
- *
- * @return <tt>false</tt> if any of the above steps failed, otherwise
- * <tt>true</tt>
- */
- private boolean handleException(Throwable thrown) {
-
- try {
-
- if (handlerClassName == NO_HANDLER) {
- return false; /* Already tried, and failed */
- }
-
- /* Look up the class name */
- if (handlerClassName == null) {
- handlerClassName = ((String) AccessController.doPrivileged(
- new GetPropertyAction(handlerPropName)));
- if (handlerClassName == null) {
- handlerClassName = NO_HANDLER; /* Do not try this again */
- return false;
- }
- }
-
- /* Load the class, instantiate it, and find its handle method */
- Method m;
- Object h;
- try {
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- Class c = Class.forName(handlerClassName, true, cl);
- m = c.getMethod("handle", new Class[] { Throwable.class });
- h = c.newInstance();
- } catch (Throwable x) {
- handlerClassName = NO_HANDLER; /* Do not try this again */
- return false;
- }
-
- /* Finally, invoke the handler */
- m.invoke(h, new Object[] { thrown });
-
- } catch (Throwable x) {
- return false;
- }
-
- return true;
+ getUncaughtExceptionHandler().uncaughtException(this, e);
+ // don't rethrow the exception to avoid EDT recreation
}
boolean isDispatching(EventQueue eq) {
diff --git a/src/share/classes/java/awt/List.java b/src/share/classes/java/awt/List.java
index 2ce9af6c0..5fd4e4631 100644
--- a/src/share/classes/java/awt/List.java
+++ b/src/share/classes/java/awt/List.java
@@ -378,7 +378,7 @@ public class List extends Component implements ItemSelectable, Accessible {
ListPeer peer = (ListPeer)this.peer;
if (peer != null) {
- peer.addItem(item, index);
+ peer.add(item, index);
}
}
@@ -413,7 +413,7 @@ public class List extends Component implements ItemSelectable, Accessible {
public synchronized void clear() {
ListPeer peer = (ListPeer)this.peer;
if (peer != null) {
- peer.clear();
+ peer.removeAll();
}
items = new Vector();
selected = new int[0];
@@ -718,7 +718,7 @@ public class List extends Component implements ItemSelectable, Accessible {
multipleMode = b;
ListPeer peer = (ListPeer)this.peer;
if (peer != null) {
- peer.setMultipleSelections(b);
+ peer.setMultipleMode(b);
}
}
}
@@ -768,7 +768,7 @@ public class List extends Component implements ItemSelectable, Accessible {
synchronized (getTreeLock()) {
ListPeer peer = (ListPeer)this.peer;
return (peer != null) ?
- peer.preferredSize(rows) :
+ peer.getPreferredSize(rows) :
super.preferredSize();
}
}
@@ -818,7 +818,7 @@ public class List extends Component implements ItemSelectable, Accessible {
synchronized (getTreeLock()) {
ListPeer peer = (ListPeer)this.peer;
return (peer != null) ?
- peer.minimumSize(rows) :
+ peer.getMinimumSize(rows) :
super.minimumSize();
}
}
diff --git a/src/share/classes/java/awt/MenuItem.java b/src/share/classes/java/awt/MenuItem.java
index 99ddac75d..71dd760a9 100644
--- a/src/share/classes/java/awt/MenuItem.java
+++ b/src/share/classes/java/awt/MenuItem.java
@@ -268,7 +268,7 @@ public class MenuItem extends MenuComponent implements Accessible {
enabled = true;
MenuItemPeer peer = (MenuItemPeer)this.peer;
if (peer != null) {
- peer.enable();
+ peer.setEnabled(true);
}
}
@@ -294,7 +294,7 @@ public class MenuItem extends MenuComponent implements Accessible {
enabled = false;
MenuItemPeer peer = (MenuItemPeer)this.peer;
if (peer != null) {
- peer.disable();
+ peer.setEnabled(false);
}
}
diff --git a/src/share/classes/java/awt/Robot.java b/src/share/classes/java/awt/Robot.java
index 2ce6a2686..94c71283c 100644
--- a/src/share/classes/java/awt/Robot.java
+++ b/src/share/classes/java/awt/Robot.java
@@ -70,10 +70,7 @@ public class Robot {
private RobotPeer peer;
private boolean isAutoWaitForIdle = false;
private int autoDelay = 0;
- private static final int LEGAL_BUTTON_MASK =
- InputEvent.BUTTON1_MASK|
- InputEvent.BUTTON2_MASK|
- InputEvent.BUTTON3_MASK;
+ private static int LEGAL_BUTTON_MASK;
// location of robot's GC, used in mouseMove(), getPixelColor() and captureScreenImage()
private Point gdLoc;
@@ -98,6 +95,19 @@ public class Robot {
}
init(GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice());
+ int tmpMask = 0;
+ if (Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()){
+ for (int i = 0; i < peer.getNumberOfButtons(); i++){
+ tmpMask |= InputEvent.getMaskForButton(i+1);
+ }
+ }
+ tmpMask |= InputEvent.BUTTON1_MASK|
+ InputEvent.BUTTON2_MASK|
+ InputEvent.BUTTON3_MASK|
+ InputEvent.BUTTON1_DOWN_MASK|
+ InputEvent.BUTTON2_DOWN_MASK|
+ InputEvent.BUTTON3_DOWN_MASK;
+ LEGAL_BUTTON_MASK = tmpMask;
}
/**
@@ -187,18 +197,55 @@ public class Robot {
/**
* Presses one or more mouse buttons. The mouse buttons should
- * be released using the <code>mouseRelease</code> method.
+ * be released using the {@link #mouseRelease(int)} method.
+ *
+ * @param buttons the Button mask; a combination of one or more
+ * mouse button masks.
+ * <p>
+ * It is allowed to use only a combination of valid values as a {@code buttons} parameter.
+ * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK},
+ * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK}
+ * and values returned by the
+ * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method.
*
- * @param buttons the Button mask; a combination of one or more
- * of these flags:
+ * The valid combination also depends on a
+ * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows:
+ * <ul>
+ * <li> If support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * then it is allowed to use only the following standard button masks:
+ * {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK},
+ * {@code InputEvent.BUTTON3_DOWN_MASK}.
+ * <li> If support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
+ * then it is allowed to use the standard button masks
+ * and masks for existing extended mouse buttons, if the mouse has more then three buttons.
+ * In that way, it is allowed to use the button masks corresponding to the buttons
+ * in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}.
+ * <br>
+ * It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)}
+ * method to obtain the mask for any mouse button by its number.
+ * </ul>
+ * <p>
+ * The following standard button masks are also accepted:
* <ul>
- * <li><code>InputEvent.BUTTON1_MASK</code>
- * <li><code>InputEvent.BUTTON2_MASK</code>
- * <li><code>InputEvent.BUTTON3_MASK</code>
+ * <li>{@code InputEvent.BUTTON1_MASK}
+ * <li>{@code InputEvent.BUTTON2_MASK}
+ * <li>{@code InputEvent.BUTTON3_MASK}
* </ul>
- * @throws IllegalArgumentException if the button mask is not a
- * valid combination
+ * However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK},
+ * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead.
+ * Either extended {@code _DOWN_MASK} or old {@code _MASK} values
+ * should be used, but both those models should not be mixed.
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
+ * and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
+ * that does not exist on the mouse and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
* @see #mouseRelease(int)
+ * @see InputEvent#getMaskForButton(int)
+ * @see Toolkit#areExtraMouseButtonsEnabled()
+ * @see java.awt.MouseInfo#getNumberOfButtons()
+ * @see java.awt.event.MouseEvent
*/
public synchronized void mousePress(int buttons) {
checkButtonsArgument(buttons);
@@ -209,16 +256,53 @@ public class Robot {
/**
* Releases one or more mouse buttons.
*
- * @param buttons the Button mask; a combination of one or more
- * of these flags:
+ * @param buttons the Button mask; a combination of one or more
+ * mouse button masks.
+ * <p>
+ * It is allowed to use only a combination of valid values as a {@code buttons} parameter.
+ * A valid combination consists of {@code InputEvent.BUTTON1_DOWN_MASK},
+ * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK}
+ * and values returned by the
+ * {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)} method.
+ *
+ * The valid combination also depends on a
+ * {@link Toolkit#areExtraMouseButtonsEnabled() Toolkit.areExtraMouseButtonsEnabled()} value as follows:
+ * <ul>
+ * <li> If the support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * then it is allowed to use only the following standard button masks:
+ * {@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK},
+ * {@code InputEvent.BUTTON3_DOWN_MASK}.
+ * <li> If the support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
+ * then it is allowed to use the standard button masks
+ * and masks for existing extended mouse buttons, if the mouse has more then three buttons.
+ * In that way, it is allowed to use the button masks corresponding to the buttons
+ * in the range from 1 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}.
+ * <br>
+ * It is recommended to use the {@link InputEvent#getMaskForButton(int) InputEvent.getMaskForButton(button)}
+ * method to obtain the mask for any mouse button by its number.
+ * </ul>
+ * <p>
+ * The following standard button masks are also accepted:
* <ul>
- * <li><code>InputEvent.BUTTON1_MASK</code>
- * <li><code>InputEvent.BUTTON2_MASK</code>
- * <li><code>InputEvent.BUTTON3_MASK</code>
+ * <li>{@code InputEvent.BUTTON1_MASK}
+ * <li>{@code InputEvent.BUTTON2_MASK}
+ * <li>{@code InputEvent.BUTTON3_MASK}
* </ul>
+ * However, it is recommended to use {@code InputEvent.BUTTON1_DOWN_MASK},
+ * {@code InputEvent.BUTTON2_DOWN_MASK}, {@code InputEvent.BUTTON3_DOWN_MASK} instead.
+ * Either extended {@code _DOWN_MASK} or old {@code _MASK} values
+ * should be used, but both those models should not be mixed.
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
+ * and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * @throws IllegalArgumentException if the {@code buttons} mask contains the mask for extra mouse button
+ * that does not exist on the mouse and support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
* @see #mousePress(int)
- * @throws IllegalArgumentException if the button mask is not a valid
- * combination
+ * @see InputEvent#getMaskForButton(int)
+ * @see Toolkit#areExtraMouseButtonsEnabled()
+ * @see java.awt.MouseInfo#getNumberOfButtons()
+ * @see java.awt.event.MouseEvent
*/
public synchronized void mouseRelease(int buttons) {
checkButtonsArgument(buttons);
diff --git a/src/share/classes/java/awt/TextArea.java b/src/share/classes/java/awt/TextArea.java
index 6d2f8ca0c..8cf1c2ced 100644
--- a/src/share/classes/java/awt/TextArea.java
+++ b/src/share/classes/java/awt/TextArea.java
@@ -321,7 +321,7 @@ public class TextArea extends TextComponent {
public synchronized void insertText(String str, int pos) {
TextAreaPeer peer = (TextAreaPeer)this.peer;
if (peer != null) {
- peer.insertText(str, pos);
+ peer.insert(str, pos);
} else {
text = text.substring(0, pos) + str + text.substring(pos);
}
@@ -385,7 +385,7 @@ public class TextArea extends TextComponent {
public synchronized void replaceText(String str, int start, int end) {
TextAreaPeer peer = (TextAreaPeer)this.peer;
if (peer != null) {
- peer.replaceText(str, start, end);
+ peer.replaceRange(str, start, end);
} else {
text = text.substring(0, start) + str + text.substring(end);
}
@@ -500,7 +500,7 @@ public class TextArea extends TextComponent {
synchronized (getTreeLock()) {
TextAreaPeer peer = (TextAreaPeer)this.peer;
return (peer != null) ?
- peer.preferredSize(rows, columns) :
+ peer.getPreferredSize(rows, columns) :
super.preferredSize();
}
}
@@ -552,7 +552,7 @@ public class TextArea extends TextComponent {
synchronized (getTreeLock()) {
TextAreaPeer peer = (TextAreaPeer)this.peer;
return (peer != null) ?
- peer.minimumSize(rows, columns) :
+ peer.getMinimumSize(rows, columns) :
super.minimumSize();
}
}
diff --git a/src/share/classes/java/awt/TextField.java b/src/share/classes/java/awt/TextField.java
index b2659aa99..e5e809e17 100644
--- a/src/share/classes/java/awt/TextField.java
+++ b/src/share/classes/java/awt/TextField.java
@@ -281,7 +281,7 @@ public class TextField extends TextComponent {
echoChar = c;
TextFieldPeer peer = (TextFieldPeer)this.peer;
if (peer != null) {
- peer.setEchoCharacter(c);
+ peer.setEchoChar(c);
}
}
}
@@ -376,7 +376,7 @@ public class TextField extends TextComponent {
synchronized (getTreeLock()) {
TextFieldPeer peer = (TextFieldPeer)this.peer;
return (peer != null) ?
- peer.preferredSize(columns) :
+ peer.getPreferredSize(columns) :
super.preferredSize();
}
}
@@ -424,7 +424,7 @@ public class TextField extends TextComponent {
synchronized (getTreeLock()) {
TextFieldPeer peer = (TextFieldPeer)this.peer;
return (peer != null) ?
- peer.minimumSize(columns) :
+ peer.getMinimumSize(columns) :
super.minimumSize();
}
}
diff --git a/src/share/classes/java/awt/Toolkit.java b/src/share/classes/java/awt/Toolkit.java
index 5ac97013d..aaa6afdd5 100644
--- a/src/share/classes/java/awt/Toolkit.java
+++ b/src/share/classes/java/awt/Toolkit.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1995-2008 Sun Microsystems, Inc. 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
@@ -2550,4 +2550,37 @@ public abstract class Toolkit {
}
}
}
+
+ /**
+ * Reports whether events from extra mouse buttons are allowed to be processed and posted into
+ * {@code EventQueue}.
+ * <br>
+ * To change the returned value it is necessary to set the {@code sun.awt.enableExtraMouseButtons}
+ * property before the {@code Toolkit} class initialization. This setting could be done on the application
+ * startup by the following command:
+ * <pre>
+ * java -Dsun.awt.enableExtraMouseButtons=false Application
+ * </pre>
+ * Alternatively, the property could be set in the application by using the following code:
+ * <pre>
+ * System.setProperty("sun.awt.enableExtraMouseButtons", "true");
+ * </pre>
+ * before the {@code Toolkit} class initialization.
+ * If not set by the time of the {@code Toolkit} class initialization, this property will be
+ * initialized with {@code true}.
+ * Changing this value after the {@code Toolkit} class initialization will have no effect.
+ * <p>
+ * The current value could be queried by using the
+ * {@code System.getProperty("sun.awt.enableExtraMouseButtons")} method.
+ * @exception HeadlessException if GraphicsEnvironment.isHeadless() returns true
+ * @return {@code true} if events from extra mouse buttons are allowed to be processed and posted;
+ * {@code false} otherwise
+ * @see System#getProperty(String propertyName)
+ * @see System#setProperty(String propertyName, String value)
+ * @see java.awt.EventQueue
+ * @since 1.7
+ */
+ public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
+ return Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled();
+ }
}
diff --git a/src/share/classes/java/awt/Window.java b/src/share/classes/java/awt/Window.java
index 94a8ff2ed..2651729f2 100644
--- a/src/share/classes/java/awt/Window.java
+++ b/src/share/classes/java/awt/Window.java
@@ -53,6 +53,7 @@ import sun.awt.AppContext;
import sun.awt.CausedFocusEvent;
import sun.awt.SunToolkit;
import sun.awt.util.IdentityArrayList;
+import sun.java2d.Disposer;
import sun.java2d.pipe.Region;
import sun.security.action.GetPropertyAction;
import sun.security.util.SecurityConstants;
@@ -409,8 +410,6 @@ public class Window extends Container implements Accessible {
}
modalExclusionType = Dialog.ModalExclusionType.NO_EXCLUDE;
-
- sun.java2d.Disposer.addRecord(anchor, new WindowDisposerRecord(appContext, this));
}
/**
@@ -540,6 +539,10 @@ public class Window extends Container implements Accessible {
if (owner != null) {
owner.addOwnedWindow(weakThis);
}
+
+ // Fix for 6758673: this call is moved here from init(gc), because
+ // WindowDisposerRecord requires a proper value of parent field.
+ Disposer.addRecord(anchor, new WindowDisposerRecord(appContext, this));
}
/**
diff --git a/src/share/classes/java/awt/doc-files/DesktopProperties.html b/src/share/classes/java/awt/doc-files/DesktopProperties.html
index f699ba2fa..5f3afb374 100644
--- a/src/share/classes/java/awt/doc-files/DesktopProperties.html
+++ b/src/share/classes/java/awt/doc-files/DesktopProperties.html
@@ -1,5 +1,5 @@
<!--
- Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ Copyright 2005-2008 Sun Microsystems, Inc. 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
@@ -64,6 +64,11 @@ here, and their value types.
<td valign="TOP"><a href="../../util/Map.html">java.util.Map<a/></td>
<td valign="TOP">Font smoothing (text antialiasing) settings.<a/></td>
</tr>
+<tr>
+<td valign="TOP"><A href=#"sun.awt.enableExtraMouseButtons">sun.awt.enableExtraMouseButtons</A</td>
+<td valign="TOP"><a href="../../lang/Boolean.html">java.lang.Boolean<a/></td>
+<td valign="TOP">Controls if mouse events from extra buttons are to be generated or not<a/></td>
+</tr>
</table>
<p>
<h2>Desktop Font Rendering Hints</h2>
@@ -219,5 +224,50 @@ So to determine if there are per-device settings it is sufficient to
determine that there is a non-null return for any screen device using
the per-device property name.
</ul>
+<h2>Mouse Functionality</h2>
+<b>Desktop Property: <A name="sun.awt.enableExtraMouseButtons">"sun.awt.enableExtraMouseButtons"</A></b>
+<p>
+This property determines if events from extra mouse buttons (if they are exist and are
+enabled by the underlying operating system) are allowed to be processed and posted into
+{@code EventQueue}.
+<br>
+The value could be changed by passing "sun.awt.enableExtraMouseButtons"
+property value into java before application starts. This could be done with the following command:
+<pre>
+java -Dsun.awt.enableExtraMouseButtons=false Application
+</pre>
+Once set on application startup, it is impossible to change this value after.
+<br>
+Current value could also be queried using getDesktopProperty("sun.awt.enableExtraMouseButtons")
+method.
+<br>
+If the property is set to {@code true} then
+<ul>
+<li> it is still legal to create {@code MouseEvent} objects with
+standard buttons and, if the mouse has more
+then three buttons, it is also legal to use buttons from the range started
+from 0 up to {@link java.awt.MouseInfo#getNumberOfButtons() getNumberOfButtons()}.
+
+<li> it is legal to use standard button masks when using {@code Robot.mousePress()}
+and {@code Robot.mouseRelease()} methods and, if the mouse has more then three buttons,
+it is also legal to use masks for existing extended mouse buttons.
+That way, if there are more then three buttons on the mouse then it is allowed to
+use button masks corresponding to the buttons
+in the range from 1 up to {@link java.awt.MouseInfo#getNumberOfButtons() getNumberOfButtons()}
+</ul>
+<br>
+If the property is set to {@code false} then
+<ul>
+<li> it is legal to create {@code MouseEvent} objects with standard buttons
+only: {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2} and
+{@code BUTTON3}
+<li> it is legal to use standard button masks only:
+{@code InputEvent.BUTTON1_DOWN_MASK}, {@code InputEvent.BUTTON2_DOWN_MASK},
+{@code InputEvent.BUTTON3_DOWN_MASK}
+</ul>
+
+This property should be used when there is no need in listening mouse events fired as a result of
+activity with extra mouse button.
+By default this property is set to {@code true}.
</body>
</html>
diff --git a/src/share/classes/java/awt/event/InputEvent.java b/src/share/classes/java/awt/event/InputEvent.java
index e36f3fed3..5f892464a 100644
--- a/src/share/classes/java/awt/event/InputEvent.java
+++ b/src/share/classes/java/awt/event/InputEvent.java
@@ -31,6 +31,7 @@ import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.util.logging.Logger;
import java.util.logging.Level;
+import java.util.Arrays;
/**
* The root event class for all component-level input events.
@@ -153,13 +154,93 @@ public abstract class InputEvent extends ComponentEvent {
*/
public static final int ALT_GRAPH_DOWN_MASK = 1 << 13;
+ /**
+ * An array of extended modifiers for additional buttons.
+ * @see getButtonDownMasks
+ * @since 7.0
+ */
+ private static final int [] BUTTON_DOWN_MASK = new int [] { BUTTON1_DOWN_MASK,
+ BUTTON2_DOWN_MASK,
+ BUTTON3_DOWN_MASK,
+ 1<<14, //4th phisical button (this is not a wheel!)
+ 1<<15, //(this is not a wheel!)
+ 1<<16,
+ 1<<17,
+ 1<<18,
+ 1<<19,
+ 1<<20,
+ 1<<21 };
+
+ /**
+ * A method to access an array of extended modifiers for additional buttons.
+ * @since 7.0
+ */
+ private static int [] getButtonDownMasks(){
+ return Arrays.copyOf(BUTTON_DOWN_MASK, BUTTON_DOWN_MASK.length);
+ }
+
+
+ /**
+ * A method to obtain a mask for any existing mouse button.
+ * The returned mask may be used for different purposes. Following are some of them:
+ * <ul>
+ * <li> {@link java.awt.Robot#mousePress(int) mousePress(buttons)} and
+ * {@link java.awt.Robot#mouseRelease(int) mouseRelease(buttons)}
+ * <li> as a {@code modifiers} parameter when creating a new {@link MouseEvent} instance
+ * <li> to check {@link MouseEvent#getModifiersEx() modifiersEx} of existing {@code MouseEvent}
+ * </ul>
+ * @param button is a number to represent a button starting from 1.
+ * For example,
+ * <pre>
+ * int button = InputEvent.getMaskForButton(1);
+ * </pre>
+ * will have the same meaning as
+ * <pre>
+ * int button = InputEvent.getMaskForButton(MouseEvent.BUTTON1);
+ * </pre>
+ * because {@link MouseEvent#BUTTON1 MouseEvent.BUTTON1} equals to 1.
+ * If a mouse has three enabled buttons(see {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()})
+ * then the values from the left column passed into the method will return
+ * corresponding values from the right column:
+ * <PRE>
+ * <b>button </b> <b>returned mask</b>
+ * {@link MouseEvent#BUTTON1 BUTTON1} {@link MouseEvent#BUTTON1_DOWN_MASK BUTTON1_DOWN_MASK}
+ * {@link MouseEvent#BUTTON2 BUTTON2} {@link MouseEvent#BUTTON2_DOWN_MASK BUTTON2_DOWN_MASK}
+ * {@link MouseEvent#BUTTON3 BUTTON3} {@link MouseEvent#BUTTON3_DOWN_MASK BUTTON3_DOWN_MASK}
+ * </PRE>
+ * If a mouse has more than three enabled buttons then more values
+ * are admissible (4, 5, etc.). There is no assigned constants for these extended buttons.
+ * The button masks for the extra buttons returned by this method have no assigned names like the
+ * first three button masks.
+ * <p>
+ * This method has the following implementation restriction.
+ * It returns masks for a limited number of buttons only. The maximum number is
+ * implementation dependent and may vary.
+ * This limit is defined by the relevant number
+ * of buttons that may hypothetically exist on the mouse but it is greater than the
+ * {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}.
+ * <p>
+ * @throws IllegalArgumentException if {@code button} is less than zero or greater than the number
+ * of button masks reserved for buttons
+ * @since 7.0
+ * @see java.awt.MouseInfo#getNumberOfButtons()
+ * @see Toolkit#areExtraMouseButtonsEnabled()
+ * @see MouseEvent#getModifiers()
+ * @see MouseEvent#getModifiersEx()
+ */
+ public static int getMaskForButton(int button) {
+ if (button <= 0 || button > BUTTON_DOWN_MASK.length) {
+ throw new IllegalArgumentException("button doesn\'t exist " + button);
+ }
+ return BUTTON_DOWN_MASK[button - 1];
+ }
+
// the constant below MUST be updated if any extra modifier
// bits are to be added!
// in fact, it is undesirable to add modifier bits
// to the same field as this may break applications
// see bug# 5066958
-
- static final int FIRST_HIGH_BIT = 1 << 14;
+ static final int FIRST_HIGH_BIT = 1 << 22;
static final int JDK_1_3_MODIFIERS = SHIFT_DOWN_MASK - 1;
static final int HIGH_MODIFIERS = ~( FIRST_HIGH_BIT - 1 );
@@ -382,7 +463,7 @@ public abstract class InputEvent extends ComponentEvent {
* cause the returning an empty string.
*
* @param modifiers a modifier mask describing the extended
- * modifier keys and mouse buttons for the event
+ * modifier keys and mouse buttons for the event
* @return a text description of the combination of extended
* modifier keys and mouse buttons that were held down
* during the event.
@@ -410,17 +491,14 @@ public abstract class InputEvent extends ComponentEvent {
buf.append(Toolkit.getProperty("AWT.altGraph", "Alt Graph"));
buf.append("+");
}
- if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0) {
- buf.append(Toolkit.getProperty("AWT.button1", "Button1"));
- buf.append("+");
- }
- if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0) {
- buf.append(Toolkit.getProperty("AWT.button2", "Button2"));
- buf.append("+");
- }
- if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0) {
- buf.append(Toolkit.getProperty("AWT.button3", "Button3"));
- buf.append("+");
+
+ int buttonNumber = 1;
+ for (int mask : InputEvent.BUTTON_DOWN_MASK){
+ if ((modifiers & mask) != 0) {
+ buf.append(Toolkit.getProperty("AWT.button"+buttonNumber, "Button"+buttonNumber));
+ buf.append("+");
+ }
+ buttonNumber++;
}
if (buf.length() > 0) {
buf.setLength(buf.length()-1); // remove trailing '+'
diff --git a/src/share/classes/java/awt/event/MouseEvent.java b/src/share/classes/java/awt/event/MouseEvent.java
index ad46a95d7..c530b1ebd 100644
--- a/src/share/classes/java/awt/event/MouseEvent.java
+++ b/src/share/classes/java/awt/event/MouseEvent.java
@@ -32,6 +32,7 @@ import java.awt.Toolkit;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.awt.IllegalComponentStateException;
+import java.awt.MouseInfo;
/**
* An event which indicates that a mouse action occurred in a component.
@@ -135,7 +136,15 @@ import java.awt.IllegalComponentStateException;
* for <code>BUTTON2_MASK</code> arrives first,
* followed by the pair for <code>BUTTON1_MASK</code>.
* <p>
- *
+ * Some extra mouse buttons are added to extend the standard set of buttons
+ * represented by the following constants:{@code BUTTON1}, {@code BUTTON2}, and {@code BUTTON3}.
+ * Extra buttons have no assigned {@code BUTTONx}
+ * constants as well as their button masks have no assigned {@code BUTTONx_DOWN_MASK}
+ * constants. Nevertheless, ordinal numbers starting from 4 may be
+ * used as button numbers (button ids). Values obtained by the
+ * {@link InputEvent#getMaskForButton(int) getMaskForButton(button)} method may be used
+ * as button masks.
+ * <p>
* <code>MOUSE_DRAGGED</code> events are delivered to the <code>Component</code>
* in which the mouse button was pressed until the mouse button is released
* (regardless of whether the mouse position is within the bounds of the
@@ -324,13 +333,31 @@ public class MouseEvent extends InputEvent {
/**
* Indicates which, if any, of the mouse buttons has changed state.
*
- * The only legal values are the following constants:
- * <code>NOBUTTON</code>,
- * <code>BUTTON1</code>,
- * <code>BUTTON2</code> or
- * <code>BUTTON3</code>.
+ * The valid values are ranged from 0 to the value returned by the
+ * {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()} method.
+ * This range already includes constants {@code NOBUTTON}, {@code BUTTON1},
+ * {@code BUTTON2}, and {@code BUTTON3}
+ * if these buttons are present. So it is allowed to use these constants too.
+ * For example, for a mouse with two buttons this field may contain the following values:
+ * <ul>
+ * <li> 0 ({@code NOBUTTON})
+ * <li> 1 ({@code BUTTON1})
+ * <li> 2 ({@code BUTTON2})
+ * </ul>
+ * If a mouse has 5 buttons, this field may contain the following values:
+ * <ul>
+ * <li> 0 ({@code NOBUTTON})
+ * <li> 1 ({@code BUTTON1})
+ * <li> 2 ({@code BUTTON2})
+ * <li> 3 ({@code BUTTON3})
+ * <li> 4
+ * <li> 5
+ * </ul>
+ * If support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled()} disabled by Java
+ * then the field may not contain the value larger than {@code BUTTON3}.
* @serial
- * @see #getButton().
+ * @see #getButton()
+ * @see java.awt.Toolkit#areExtraMouseButtonsEnabled()
*/
int button;
@@ -385,6 +412,15 @@ public class MouseEvent extends InputEvent {
}
/**
+ * A number of buttons available on the mouse at the {@code Toolkit} machinery startup.
+ */
+ private static int cachedNumberOfButtons;
+
+ static {
+ cachedNumberOfButtons = MouseInfo.getNumberOfButtons();
+ }
+
+ /**
* Returns the absolute horizontal x position of the event.
* In a virtual device multi-screen environment in which the
* desktop area could span multiple physical screen devices,
@@ -421,7 +457,8 @@ public class MouseEvent extends InputEvent {
/**
* Constructs a <code>MouseEvent</code> object with the
* specified source component,
- * type, modifiers, coordinates, and click count.
+ * type, time, modifiers, coordinates, click count, popupTrigger flag,
+ * and button number.
* <p>
* Creating an invalid event (such
* as by using more than one of the old _MASKs, or modifier/button
@@ -464,7 +501,33 @@ public class MouseEvent extends InputEvent {
* @param popupTrigger A boolean that equals {@code true} if this event
* is a trigger for a popup menu
* @param button An integer that indicates, which of the mouse buttons has
- * changed its state
+ * changed its state.
+ * The following rules are applied to this parameter:
+ * <ul>
+ * <li>If support for the extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * then it is allowed to create {@code MouseEvent} objects only with the standard buttons:
+ * {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2}, and
+ * {@code BUTTON3}.
+ * <li> If support for the extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
+ * then it is allowed to create {@code MouseEvent} objects with
+ * the standard buttons.
+ * In case the support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java, then
+ * in addition to the standard buttons, {@code MouseEvent} objects can be created
+ * using buttons from the range starting from 4 to
+ * {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}
+ * if the mouse has more than three buttons.
+ * </ul>
+ * @throws IllegalArgumentException if {@code button} is less then zero
+ * @throws IllegalArgumentException if <code>source</code> is null
+ * @throws IllegalArgumentException if {@code button} is greater then BUTTON3 and the support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * @throws IllegalArgumentException if {@code button} is greater then the
+ * {@link java.awt.MouseInfo#getNumberOfButtons() current number of buttons} and the support
+ * for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled}
+ * by Java
* @throws IllegalArgumentException if an invalid <code>button</code>
* value is passed in
* @throws IllegalArgumentException if <code>source</code> is null
@@ -498,7 +561,7 @@ public class MouseEvent extends InputEvent {
/**
* Constructs a <code>MouseEvent</code> object with the
* specified source component,
- * type, modifiers, coordinates, and click count.
+ * type, modifiers, coordinates, click count, and popupTrigger flag.
* An invocation of the form
* <tt>MouseEvent(source, id, when, modifiers, x, y, clickCount, popupTrigger)</tt>
* behaves in exactly the same way as the invocation
@@ -551,10 +614,26 @@ public class MouseEvent extends InputEvent {
}
+ /* if the button is an extra button and it is released or clicked then in Xsystem its state
+ is not modified. Exclude this button number from ExtModifiers mask.*/
+ transient private boolean shouldExcludeButtonFromExtModifiers = false;
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getModifiersEx() {
+ int tmpModifiers = modifiers;
+ if (shouldExcludeButtonFromExtModifiers) {
+ tmpModifiers &= ~(InputEvent.getMaskForButton(getButton()));
+ }
+ return tmpModifiers & ~JDK_1_3_MODIFIERS;
+ }
+
/**
* Constructs a <code>MouseEvent</code> object with the
* specified source component,
- * type, modifiers, coordinates, absolute coordinates, and click count.
+ * type, time, modifiers, coordinates, absolute coordinates, click count, popupTrigger flag,
+ * and button number.
* <p>
* Creating an invalid event (such
* as by using more than one of the old _MASKs, or modifier/button
@@ -595,7 +674,33 @@ public class MouseEvent extends InputEvent {
* @param popupTrigger A boolean that equals {@code true} if this event
* is a trigger for a popup menu
* @param button An integer that indicates, which of the mouse buttons has
- * changed its state
+ * changed its state.
+ * The following rules are applied to this parameter:
+ * <ul>
+ * <li>If support for the extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * then it is allowed to create {@code MouseEvent} objects only with the standard buttons:
+ * {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2}, and
+ * {@code BUTTON3}.
+ * <li> If support for the extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
+ * then it is allowed to create {@code MouseEvent} objects with
+ * the standard buttons.
+ * In case the support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java, then
+ * in addition to the standard buttons, {@code MouseEvent} objects can be created
+ * using buttons from the range starting from 4 to
+ * {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}
+ * if the mouse has more than three buttons.
+ * </ul>
+ * @throws IllegalArgumentException if {@code button} is less then zero
+ * @throws IllegalArgumentException if <code>source</code> is null
+ * @throws IllegalArgumentException if {@code button} is greater then BUTTON3 and the support for extended mouse buttons is
+ * {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * @throws IllegalArgumentException if {@code button} is greater then the
+ * {@link java.awt.MouseInfo#getNumberOfButtons() current number of buttons} and the support
+ * for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled}
+ * by Java
* @throws IllegalArgumentException if an invalid <code>button</code>
* value is passed in
* @throws IllegalArgumentException if <code>source</code> is null
@@ -610,6 +715,10 @@ public class MouseEvent extends InputEvent {
* @see #getClickCount()
* @see #isPopupTrigger()
* @see #getButton()
+ * @see #button
+ * @see Toolkit#areExtraMouseButtonsEnabled()
+ * @see java.awt.MouseInfo#getNumberOfButtons()
+ * @see InputEvent#getMaskForButton(int)
* @since 1.6
*/
public MouseEvent(Component source, int id, long when, int modifiers,
@@ -623,14 +732,41 @@ public class MouseEvent extends InputEvent {
this.yAbs = yAbs;
this.clickCount = clickCount;
this.popupTrigger = popupTrigger;
- if (button < NOBUTTON || button >BUTTON3) {
- throw new IllegalArgumentException("Invalid button value");
+ if (button < NOBUTTON){
+ throw new IllegalArgumentException("Invalid button value :" + button);
}
+ //TODO: initialize MouseInfo.cachedNumber on toolkit creation.
+ if (button > BUTTON3) {
+ if (!Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()){
+ throw new IllegalArgumentException("Extra mouse events are disabled " + button);
+ } else {
+ if (button > cachedNumberOfButtons) {
+ throw new IllegalArgumentException("Nonexistent button " + button);
+ }
+ }
+ // XToolkit: extra buttons are not reporting about their state correctly.
+ // Being pressed they report the state=0 both on the press and on the release.
+ // For 1-3 buttons the state value equals zero on press and non-zero on release.
+ // Other modifiers like Shift, ALT etc seem report well with extra buttons.
+ // The problem reveals as follows: one button is pressed and then another button is pressed and released.
+ // So, the getModifiersEx() would not be zero due to a first button and we will skip this modifier.
+ // This may have to be moved into the peer code instead if possible.
+
+ if (getModifiersEx() != 0) { //There is at least one more button in a pressed state.
+ if (id == MouseEvent.MOUSE_RELEASED || id == MouseEvent.MOUSE_CLICKED){
+ System.out.println("MEvent. CASE!");
+ shouldExcludeButtonFromExtModifiers = true;
+ }
+ }
+ }
+
this.button = button;
+
if ((getModifiers() != 0) && (getModifiersEx() == 0)) {
setNewModifiers();
} else if ((getModifiers() == 0) &&
- (getModifiersEx() != 0 || button != NOBUTTON))
+ (getModifiersEx() != 0 || button != NOBUTTON) &&
+ (button <= BUTTON3))
{
setOldModifiers();
}
@@ -701,13 +837,55 @@ public class MouseEvent extends InputEvent {
/**
* Returns which, if any, of the mouse buttons has changed state.
+ * The returned value is ranged
+ * from 0 to the {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}
+ * value.
+ * The returned value includes at least the following constants:
+ * <ul>
+ * <li> {@code NOBUTTON}
+ * <li> {@code BUTTON1}
+ * <li> {@code BUTTON2}
+ * <li> {@code BUTTON3}
+ * </ul>
+ * It is allowed to use those constants to compare with the returned button number in the application.
+ * For example,
+ * <pre>
+ * if (anEvent.getButton() == MouseEvent.BUTTON1) {
+ * </pre>
+ * In particular, for a mouse with one, two, or three buttons this method may return the following values:
+ * <ul>
+ * <li> 0 ({@code NOBUTTON})
+ * <li> 1 ({@code BUTTON1})
+ * <li> 2 ({@code BUTTON2})
+ * <li> 3 ({@code BUTTON3})
+ * </ul>
+ * Button numbers greater then {@code BUTTON3} have no constant identifier. So if a mouse with five buttons is
+ * installed, this method may return the following values:
+ * <ul>
+ * <li> 0 ({@code NOBUTTON})
+ * <li> 1 ({@code BUTTON1})
+ * <li> 2 ({@code BUTTON2})
+ * <li> 3 ({@code BUTTON3})
+ * <li> 4
+ * <li> 5
+ * </ul>
+ * <p>
+ * Note: If support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
+ * then the AWT event subsystem does not produce mouse events for the extended mouse
+ * buttons. So it is not expected that this method returns anything except {@code NOBUTTON}, {@code BUTTON1},
+ * {@code BUTTON2}, {@code BUTTON3}.
*
- * @return one of the following constants:
- * <code>NOBUTTON</code>,
- * <code>BUTTON1</code>,
- * <code>BUTTON2</code> or
- * <code>BUTTON3</code>.
+ * @return one of the values from 0 to {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}
+ * if support for the extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java.
+ * That range includes {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2}, {@code BUTTON3};
+ * <br>
+ * {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2} or {@code BUTTON3}
+ * if support for the extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
* @since 1.4
+ * @see Toolkit#areExtraMouseButtonsEnabled()
+ * @see java.awt.MouseInfo#getNumberOfButtons()
+ * @see #MouseEvent(Component, int, long, int, int, int, int, int, int, boolean, int)
+ * @see InputEvent#getMaskForButton(int)
*/
public int getButton() {
return button;
@@ -746,7 +924,7 @@ public class MouseEvent extends InputEvent {
* and will cause the returning an unspecified string.
* Zero parameter means that no modifiers were passed and will
* cause the returning an empty string.
- *
+ * <p>
* @param modifiers A modifier mask describing the modifier keys and
* mouse buttons that were down during the event
* @return string string text description of the combination of modifier
@@ -788,6 +966,24 @@ public class MouseEvent extends InputEvent {
buf.append(Toolkit.getProperty("AWT.button3", "Button3"));
buf.append("+");
}
+
+ int mask;
+
+ // TODO: add a toolkit field that holds a number of button on the mouse.
+ // As the method getMouseModifiersText() is static and obtain
+ // an integer as a parameter then we may not restrict this with the number
+ // of buttons installed on the mouse.
+ // It's a temporary solution. We need to somehow hold the number of buttons somewhere else.
+ for (int i = 1; i <= cachedNumberOfButtons; i++){
+ mask = InputEvent.getMaskForButton(i);
+ if ((modifiers & mask) != 0 &&
+ buf.indexOf(Toolkit.getProperty("AWT.button"+i, "Button"+i)) == -1) //1,2,3 buttons may already be there; so don't duplicate it.
+ {
+ buf.append(Toolkit.getProperty("AWT.button"+i, "Button"+i));
+ buf.append("+");
+ }
+ }
+
if (buf.length() > 0) {
buf.setLength(buf.length()-1); // remove trailing '+'
}
@@ -836,14 +1032,18 @@ public class MouseEvent extends InputEvent {
str.append(",(").append(x).append(",").append(y).append(")");
str.append(",absolute(").append(xAbs).append(",").append(yAbs).append(")");
- str.append(",button=").append(getButton());
+ if (id != MOUSE_DRAGGED && id != MOUSE_MOVED){
+ str.append(",button=").append(getButton());
+ }
if (getModifiers() != 0) {
str.append(",modifiers=").append(getMouseModifiersText(modifiers));
}
if (getModifiersEx() != 0) {
- str.append(",extModifiers=").append(getModifiersExText(modifiers));
+ //Using plain "modifiers" here does show an excluded extended buttons in the string event representation.
+ //getModifiersEx() solves the problem.
+ str.append(",extModifiers=").append(getModifiersExText(getModifiersEx()));
}
str.append(",clickCount=").append(clickCount);
diff --git a/src/share/classes/java/awt/peer/ButtonPeer.java b/src/share/classes/java/awt/peer/ButtonPeer.java
index 5b6f43b1d..8b2190060 100644
--- a/src/share/classes/java/awt/peer/ButtonPeer.java
+++ b/src/share/classes/java/awt/peer/ButtonPeer.java
@@ -25,7 +25,11 @@
package java.awt.peer;
+import java.awt.Button;
+
/**
+ * The peer interface for {@link Button}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -33,5 +37,14 @@ package java.awt.peer;
* instances.
*/
public interface ButtonPeer extends ComponentPeer {
+
+ /**
+ * Sets the label that is displayed on the button. Can be {@code null}
+ * when the button should not display a label.
+ *
+ * @param label the label string to set
+ *
+ * @see Button#setLabel
+ */
void setLabel(String label);
}
diff --git a/src/share/classes/java/awt/peer/CanvasPeer.java b/src/share/classes/java/awt/peer/CanvasPeer.java
index 020a931a7..bbf6a1110 100644
--- a/src/share/classes/java/awt/peer/CanvasPeer.java
+++ b/src/share/classes/java/awt/peer/CanvasPeer.java
@@ -24,7 +24,11 @@
*/
package java.awt.peer;
+import java.awt.Canvas;
+
/**
+ * The peer interface for {@link Canvas}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
diff --git a/src/share/classes/java/awt/peer/CheckboxMenuItemPeer.java b/src/share/classes/java/awt/peer/CheckboxMenuItemPeer.java
index d7e5a60de..34b9f85c3 100644
--- a/src/share/classes/java/awt/peer/CheckboxMenuItemPeer.java
+++ b/src/share/classes/java/awt/peer/CheckboxMenuItemPeer.java
@@ -24,7 +24,11 @@
*/
package java.awt.peer;
+import java.awt.CheckboxMenuItem;
+
/**
+ * The peer interface for {@link CheckboxMenuItem}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -32,5 +36,14 @@ package java.awt.peer;
* instances.
*/
public interface CheckboxMenuItemPeer extends MenuItemPeer {
+
+ /**
+ * Sets the state of the checkbox to be checked ({@code true}) or
+ * unchecked ({@code false}).
+ *
+ * @param t the state to set on the checkbox
+ *
+ * @see CheckboxMenuItemPeer#setState(boolean)
+ */
void setState(boolean t);
}
diff --git a/src/share/classes/java/awt/peer/CheckboxPeer.java b/src/share/classes/java/awt/peer/CheckboxPeer.java
index 64ecf3a91..f2126e20f 100644
--- a/src/share/classes/java/awt/peer/CheckboxPeer.java
+++ b/src/share/classes/java/awt/peer/CheckboxPeer.java
@@ -27,6 +27,8 @@ package java.awt.peer;
import java.awt.*;
/**
+ * The peer interface for {@link Checkbox}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,7 +36,36 @@ import java.awt.*;
* instances.
*/
public interface CheckboxPeer extends ComponentPeer {
+
+ /**
+ * Sets the state of the checkbox to be checked ({@code true}) or
+ * unchecked ({@code false}).
+ *
+ * @param t the state to set on the checkbox
+ *
+ * @see Checkbox#setState(boolean)
+ */
void setState(boolean state);
+
+ /**
+ * Sets the checkbox group for this checkbox. Checkboxes in one checkbox
+ * group can only be selected exclusively (like radio buttons). A value
+ * of {@code null} removes this checkbox from any checkbox group.
+ *
+ * @param g the checkbox group to set, or {@code null} when this
+ * checkbox should not be placed in any group
+ *
+ * @see Checkbox#setCheckboxGroup(CheckboxGroup)
+ */
void setCheckboxGroup(CheckboxGroup g);
+
+ /**
+ * Sets the label that should be displayed on the ckeckbox. A value of
+ * {@code null} means that no label should be displayed.
+ *
+ * @param label the label to be displayed on the checkbox, or
+ * {@code null} when no label should be displayed.
+ */
void setLabel(String label);
+
}
diff --git a/src/share/classes/java/awt/peer/ChoicePeer.java b/src/share/classes/java/awt/peer/ChoicePeer.java
index fdeaabaa7..7efebdae3 100644
--- a/src/share/classes/java/awt/peer/ChoicePeer.java
+++ b/src/share/classes/java/awt/peer/ChoicePeer.java
@@ -24,7 +24,11 @@
*/
package java.awt.peer;
+import java.awt.Choice;
+
/**
+ * The peer interface for {@link Choice}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -32,13 +36,41 @@ package java.awt.peer;
* instances.
*/
public interface ChoicePeer extends ComponentPeer {
+
+ /**
+ * Adds an item with the string {@code item} to the combo box list
+ * at index {@code index}.
+ *
+ * @param item the label to be added to the list
+ * @param index the index where to add the item
+ *
+ * @see Choice#add(String)
+ */
void add(String item, int index);
+
+ /**
+ * Removes the item at index {@code index} from the combo box list.
+ *
+ * @param index the index where to remove the item
+ *
+ * @see Choice#remove(int)
+ */
void remove(int index);
+
+ /**
+ * Removes all items from the combo box list.
+ *
+ * @see Choice#removeAll()
+ */
void removeAll();
- void select(int index);
- /*
- * DEPRECATED: Replaced by add(String, int).
+ /**
+ * Selects the item at index {@code index}.
+ *
+ * @param index the index which should be selected
+ *
+ * @see Choice#select(int)
*/
- void addItem(String item, int index);
+ void select(int index);
+
}
diff --git a/src/share/classes/java/awt/peer/ComponentPeer.java b/src/share/classes/java/awt/peer/ComponentPeer.java
index 783076b06..e71ab27f7 100644
--- a/src/share/classes/java/awt/peer/ComponentPeer.java
+++ b/src/share/classes/java/awt/peer/ComponentPeer.java
@@ -37,6 +37,12 @@ import sun.java2d.pipe.Region;
/**
+ * The peer interface for {@link Component}. This is the top level peer
+ * interface for widgets and defines the bulk of methods for AWT component
+ * peers. Most component peers have to implement this interface (via one
+ * of the subinterfaces), except menu components, which implement
+ * {@link MenuComponentPeer}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -44,115 +50,493 @@ import sun.java2d.pipe.Region;
* instances.
*/
public interface ComponentPeer {
- public static final int SET_LOCATION = 1,
- SET_SIZE = 2,
- SET_BOUNDS = 3,
- SET_CLIENT_SIZE = 4,
- RESET_OPERATION = 5,
- NO_EMBEDDED_CHECK = (1 << 14),
- DEFAULT_OPERATION = SET_BOUNDS;
+
+ /**
+ * Operation for {@link #setBounds(int, int, int, int, int)}, indicating
+ * a change in the component location only.
+ *
+ * @see #setBounds(int, int, int, int, int)
+ */
+ public static final int SET_LOCATION = 1;
+
+ /**
+ * Operation for {@link #setBounds(int, int, int, int, int)}, indicating
+ * a change in the component size only.
+ *
+ * @see #setBounds(int, int, int, int, int)
+ */
+ public static final int SET_SIZE = 2;
+
+ /**
+ * Operation for {@link #setBounds(int, int, int, int, int)}, indicating
+ * a change in the component size and location.
+ *
+ * @see #setBounds(int, int, int, int, int)
+ */
+ public static final int SET_BOUNDS = 3;
+
+ /**
+ * Operation for {@link #setBounds(int, int, int, int, int)}, indicating
+ * a change in the component client size. This is used for setting
+ * the 'inside' size of windows, without the border insets.
+ *
+ * @see #setBounds(int, int, int, int, int)
+ */
+ public static final int SET_CLIENT_SIZE = 4;
+
+ /**
+ * Resets the setBounds() operation to DEFAULT_OPERATION. This is not
+ * passed into {@link #setBounds(int, int, int, int, int)}.
+ *
+ * TODO: This is only used internally and should probably be moved outside
+ * the peer interface.
+ *
+ * @see Component#setBoundsOp
+ */
+ public static final int RESET_OPERATION = 5;
+
+ /**
+ * A flag that is used to suppress checks for embedded frames.
+ *
+ * TODO: This is only used internally and should probably be moved outside
+ * the peer interface.
+ */
+ public static final int NO_EMBEDDED_CHECK = (1 << 14);
+
+ /**
+ * The default operation, which is to set size and location.
+ *
+ * TODO: This is only used internally and should probably be moved outside
+ * the peer interface.
+ *
+ * @see Component#setBoundsOp
+ */
+ public static final int DEFAULT_OPERATION = SET_BOUNDS;
+
+ /**
+ * Determines if a component has been obscured, i.e. by an overlapping
+ * window or similar. This is used by JViewport for optimizing performance.
+ * This doesn't have to be implemented, when
+ * {@link #canDetermineObscurity()} returns {@code false}.
+ *
+ * @return {@code true} when the component has been obscured,
+ * {@code false} otherwise
+ *
+ * @see #canDetermineObscurity()
+ * @see javax.swing.JViewport#needsRepaintAfterBlit
+ */
boolean isObscured();
+
+ /**
+ * Returns {@code true} when the peer can determine if a component
+ * has been obscured, {@code false} false otherwise.
+ *
+ * @return {@code true} when the peer can determine if a component
+ * has been obscured, {@code false} false otherwise
+ *
+ * @see #isObscured()
+ * @see javax.swing.JViewport#needsRepaintAfterBlit
+ */
boolean canDetermineObscurity();
- void setVisible(boolean b);
- void setEnabled(boolean b);
- void paint(Graphics g);
- void repaint(long tm, int x, int y, int width, int height);
- void print(Graphics g);
- void setBounds(int x, int y, int width, int height, int op);
- void handleEvent(AWTEvent e);
- void coalescePaintEvent(PaintEvent e);
- Point getLocationOnScreen();
- Dimension getPreferredSize();
- Dimension getMinimumSize();
- ColorModel getColorModel();
- Toolkit getToolkit();
- Graphics getGraphics();
- FontMetrics getFontMetrics(Font font);
- void dispose();
- void setForeground(Color c);
- void setBackground(Color c);
- void setFont(Font f);
- void updateCursorImmediately();
- boolean requestFocus(Component lightweightChild,
- boolean temporary,
- boolean focusedWindowChangeAllowed,
- long time, CausedFocusEvent.Cause cause);
- boolean isFocusable();
-
- Image createImage(ImageProducer producer);
- Image createImage(int width, int height);
- VolatileImage createVolatileImage(int width, int height);
- boolean prepareImage(Image img, int w, int h, ImageObserver o);
- int checkImage(Image img, int w, int h, ImageObserver o);
- GraphicsConfiguration getGraphicsConfiguration();
- boolean handlesWheelScrolling();
- void createBuffers(int numBuffers, BufferCapabilities caps) throws AWTException;
- Image getBackBuffer();
- void flip(int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction);
- void destroyBuffers();
/**
- * Reparents this peer to the new parent referenced by <code>newContainer</code> peer
- * Implementation depends on toolkit and container.
- * @param newContainer peer of the new parent container
- * @since 1.5
+ * Makes a component visible or invisible.
+ *
+ * @param v {@code true} to make a component visible,
+ * {@code false} to make it invisible
+ *
+ * @see Component#setVisible(boolean)
*/
- void reparent(ContainerPeer newContainer);
+ void setVisible(boolean v);
+
/**
- * Returns whether this peer supports reparenting to another parent withour destroying the peer
- * @return true if appropriate reparent is supported, false otherwise
- * @since 1.5
+ * Enables or disables a component. Disabled components are usually grayed
+ * out and cannot be activated.
+ *
+ * @param e {@code true} to enable the component, {@code false}
+ * to disable it
+ *
+ * @see Component#setEnabled(boolean)
*/
- boolean isReparentSupported();
+ void setEnabled(boolean e);
/**
- * Used by lightweight implementations to tell a ComponentPeer to layout
- * its sub-elements. For instance, a lightweight Checkbox needs to layout
- * the box, as well as the text label.
+ * Paints the component to the specified graphics context. This is called
+ * by {@link Component#paintAll(Graphics)} to paint the component.
+ *
+ * @param g the graphics context to paint to
+ *
+ * @see Component#paintAll(Graphics)
*/
- void layout();
+ void paint(Graphics g);
+ /**
+ * Prints the component to the specified graphics context. This is called
+ * by {@link Component#printAll(Graphics)} to print the component.
+ *
+ * @param g the graphics context to print to
+ *
+ * @see Component#printAll(Graphics)
+ */
+ void print(Graphics g);
- Rectangle getBounds();
+ /**
+ * Sets the location or size or both of the component. The location is
+ * specified relative to the component's parent. The {@code op}
+ * parameter specifies which properties change. If it is
+ * {@link #SET_LOCATION}, then only the location changes (and the size
+ * parameters can be ignored). If {@code op} is {@link #SET_SIZE},
+ * then only the size changes (and the location can be ignored). If
+ * {@code op} is {@link #SET_BOUNDS}, then both change. There is a
+ * special value {@link #SET_CLIENT_SIZE}, which is used only for
+ * window-like components to set the size of the client (i.e. the 'inner'
+ * size, without the insets of the window borders).
+ *
+ * @param x the X location of the component
+ * @param y the Y location of the component
+ * @param width the width of the component
+ * @param height the height of the component
+ * @param op the operation flag
+ *
+ * @see #SET_BOUNDS
+ * @see #SET_LOCATION
+ * @see #SET_SIZE
+ * @see #SET_CLIENT_SIZE
+ */
+ void setBounds(int x, int y, int width, int height, int op);
/**
- * Applies the shape to the native component window.
- * @since 1.7
+ * Called to let the component peer handle events.
+ *
+ * @param e the AWT event to handle
+ *
+ * @see Component#dispatchEvent(AWTEvent)
*/
- void applyShape(Region shape);
+ void handleEvent(AWTEvent e);
+
+ /**
+ * Called to coalesce paint events.
+ *
+ * @param e the paint event to consider to coalesce
+ *
+ * @see EventQueue#coalescePaintEvent
+ */
+ void coalescePaintEvent(PaintEvent e);
/**
- * DEPRECATED: Replaced by getPreferredSize().
+ * Determines the location of the component on the screen.
+ *
+ * @return the location of the component on the screen
+ *
+ * @see Component#getLocationOnScreen()
*/
- Dimension preferredSize();
+ Point getLocationOnScreen();
/**
- * DEPRECATED: Replaced by getMinimumSize().
+ * Determines the preferred size of the component.
+ *
+ * @return the preferred size of the component
+ *
+ * @see Component#getPreferredSize()
*/
- Dimension minimumSize();
+ Dimension getPreferredSize();
/**
- * DEPRECATED: Replaced by setVisible(boolean).
+ * Determines the minimum size of the component.
+ *
+ * @return the minimum size of the component
+ *
+ * @see Component#getMinimumSize()
*/
- void show();
+ Dimension getMinimumSize();
/**
- * DEPRECATED: Replaced by setVisible(boolean).
+ * Returns the color model used by the component.
+ *
+ * @return the color model used by the component
+ *
+ * @see Component#getColorModel()
*/
- void hide();
+ ColorModel getColorModel();
/**
- * DEPRECATED: Replaced by setEnabled(boolean).
+ * Returns the toolkit that is responsible for the component.
+ *
+ * @return the toolkit that is responsible for the component
+ *
+ * @see Component#getToolkit()
*/
- void enable();
+ Toolkit getToolkit();
/**
- * DEPRECATED: Replaced by setEnabled(boolean).
+ * Returns a graphics object to paint on the component.
+ *
+ * @return a graphics object to paint on the component
+ *
+ * @see Component#getGraphics()
*/
- void disable();
+ // TODO: Maybe change this to force Graphics2D, since many things will
+ // break with plain Graphics nowadays.
+ Graphics getGraphics();
/**
- * DEPRECATED: Replaced by setBounds(int, int, int, int).
+ * Returns a font metrics object to determine the metrics properties of
+ * the specified font.
+ *
+ * @param font the font to determine the metrics for
+ *
+ * @return a font metrics object to determine the metrics properties of
+ * the specified font
+ *
+ * @see Component#getFontMetrics(Font)
*/
- void reshape(int x, int y, int width, int height);
+ FontMetrics getFontMetrics(Font font);
+
+ /**
+ * Disposes all resources held by the component peer. This is called
+ * when the component has been disconnected from the component hierarchy
+ * and is about to be garbage collected.
+ *
+ * @see Component#removeNotify()
+ */
+ void dispose();
+
+ /**
+ * Sets the foreground color of this component.
+ *
+ * @param c the foreground color to set
+ *
+ * @see Component#setForeground(Color)
+ */
+ void setForeground(Color c);
+
+ /**
+ * Sets the background color of this component.
+ *
+ * @param c the background color to set
+ *
+ * @see Component#setBackground(Color)
+ */
+ void setBackground(Color c);
+
+ /**
+ * Sets the font of this component.
+ *
+ * @param f the font of this component
+ *
+ * @see Component#setFont(Font)
+ */
+ void setFont(Font f);
+
+ /**
+ * Updates the cursor of the component.
+ *
+ * @see Component#updateCursorImmediately
+ */
+ void updateCursorImmediately();
+
+ /**
+ * Requests focus on this component.
+ *
+ * @param lightweightChild the actual lightweight child that requests the
+ * focus
+ * @param temporary {@code true} if the focus change is temporary,
+ * {@code false} otherwise
+ * @param focusedWindowChangeAllowed {@code true} if changing the
+ * focus of the containing window is allowed or not
+ * @param time the time of the focus change request
+ * @param cause the cause of the focus change request
+ *
+ * @return {@code true} if the focus change is guaranteed to be
+ * granted, {@code false} otherwise
+ */
+ boolean requestFocus(Component lightweightChild, boolean temporary,
+ boolean focusedWindowChangeAllowed, long time,
+ CausedFocusEvent.Cause cause);
+
+ /**
+ * Returns {@code true} when the component takes part in the focus
+ * traversal, {@code false} otherwise.
+ *
+ * @return {@code true} when the component takes part in the focus
+ * traversal, {@code false} otherwise
+ */
+ boolean isFocusable();
+
+ /**
+ * Creates an image using the specified image producer.
+ *
+ * @param producer the image producer from which the image pixels will be
+ * produced
+ *
+ * @return the created image
+ *
+ * @see Component#createImage(ImageProducer)
+ */
+ Image createImage(ImageProducer producer);
+
+ /**
+ * Creates an empty image with the specified width and height. This is
+ * generally used as a non-accelerated backbuffer for drawing onto the
+ * component (e.g. by Swing).
+ *
+ * @param width the width of the image
+ * @param height the height of the image
+ *
+ * @return the created image
+ *
+ * @see Component#createImage(int, int)
+ */
+ // TODO: Maybe make that return a BufferedImage, because some stuff will
+ // break if a different kind of image is returned.
+ Image createImage(int width, int height);
+
+ /**
+ * Creates an empty volatile image with the specified width and height.
+ * This is generally used as an accelerated backbuffer for drawing onto
+ * the component (e.g. by Swing).
+ *
+ * @param width the width of the image
+ * @param height the height of the image
+ *
+ * @return the created volatile image
+ *
+ * @see Component#createVolatileImage(int, int)
+ */
+ // TODO: Include capabilities here and fix Component#createVolatileImage
+ VolatileImage createVolatileImage(int width, int height);
+
+ /**
+ * Prepare the specified image for rendering on this component. This should
+ * start loading the image (if not already loaded) and create an
+ * appropriate screen representation.
+ *
+ * @param img the image to prepare
+ * @param w the width of the screen representation
+ * @param h the height of the screen representation
+ * @param o an image observer to observe the progress
+ *
+ * @return {@code true} if the image is already fully prepared,
+ * {@code false} otherwise
+ *
+ * @see Component#prepareImage(Image, int, int, ImageObserver)
+ */
+ boolean prepareImage(Image img, int w, int h, ImageObserver o);
+
+ /**
+ * Determines the status of the construction of the screen representaion
+ * of the specified image.
+ *
+ * @param img the image to check
+ * @param w the target width
+ * @param h the target height
+ * @param o the image observer to notify
+ *
+ * @return the status as bitwise ORed ImageObserver flags
+ *
+ * @see Component#checkImage(Image, int, int, ImageObserver)
+ */
+ int checkImage(Image img, int w, int h, ImageObserver o);
+
+ /**
+ * Returns the graphics configuration that corresponds to this component.
+ *
+ * @return the graphics configuration that corresponds to this component
+ *
+ * @see Component#getGraphicsConfiguration()
+ */
+ GraphicsConfiguration getGraphicsConfiguration();
+
+ /**
+ * Determines if the component handles wheel scrolling itself. Otherwise
+ * it is delegated to the component's parent.
+ *
+ * @return {@code true} if the component handles wheel scrolling,
+ * {@code false} otherwise
+ *
+ * @see Component#dispatchEventImpl(AWTEvent)
+ */
+ boolean handlesWheelScrolling();
+
+ /**
+ * Create {@code numBuffers} flipping buffers with the specified
+ * buffer capabilities.
+ *
+ * @param numBuffers the number of buffers to create
+ * @param caps the buffer capabilities
+ *
+ * @throws AWTException if flip buffering is not supported
+ *
+ * @see Component.FlipBufferStrategy#createBuffers
+ */
+ void createBuffers(int numBuffers, BufferCapabilities caps)
+ throws AWTException;
+
+ /**
+ * Returns the back buffer as image.
+ *
+ * @return the back buffer as image
+ *
+ * @see Component.FlipBufferStrategy#getBackBuffer
+ */
+ Image getBackBuffer();
+
+ /**
+ * Move the back buffer to the front buffer.
+ *
+ * @param x1 the area to be flipped, upper left X coordinate
+ * @param y1 the area to be flipped, upper left Y coordinate
+ * @param x2 the area to be flipped, lower right X coordinate
+ * @param y2 the area to be flipped, lower right Y coordinate
+ * @param flipAction the flip action to perform
+ *
+ * @see Component.FlipBufferStrategy#flip
+ */
+ void flip(int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction);
+
+ /**
+ * Destroys all created buffers.
+ *
+ * @see Component.FlipBufferStrategy#destroyBuffers
+ */
+ void destroyBuffers();
+
+ /**
+ * Reparents this peer to the new parent referenced by
+ * {@code newContainer} peer. Implementation depends on toolkit and
+ * container.
+ *
+ * @param newContainer peer of the new parent container
+ *
+ * @since 1.5
+ */
+ void reparent(ContainerPeer newContainer);
+
+ /**
+ * Returns whether this peer supports reparenting to another parent without
+ * destroying the peer.
+ *
+ * @return true if appropriate reparent is supported, false otherwise
+ *
+ * @since 1.5
+ */
+ boolean isReparentSupported();
+
+ /**
+ * Used by lightweight implementations to tell a ComponentPeer to layout
+ * its sub-elements. For instance, a lightweight Checkbox needs to layout
+ * the box, as well as the text label.
+ *
+ * @see Component#validate()
+ */
+ void layout();
+
+ /**
+ * Applies the shape to the native component window.
+ * @since 1.7
+ *
+ * @see Component#applyCompoundShape
+ */
+ void applyShape(Region shape);
+
}
diff --git a/src/share/classes/java/awt/peer/ContainerPeer.java b/src/share/classes/java/awt/peer/ContainerPeer.java
index a4f332c66..092a54f2a 100644
--- a/src/share/classes/java/awt/peer/ContainerPeer.java
+++ b/src/share/classes/java/awt/peer/ContainerPeer.java
@@ -27,6 +27,9 @@ package java.awt.peer;
import java.awt.*;
/**
+ * The peer interface for {@link Container}. This is the parent interface
+ * for all container like widgets.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,30 +37,60 @@ import java.awt.*;
* instances.
*/
public interface ContainerPeer extends ComponentPeer {
+
+ /**
+ * Returns the insets of this container. Insets usually is the space that
+ * is occupied by things like borders.
+ *
+ * @return the insets of this container
+ */
Insets getInsets();
+
+ /**
+ * Notifies the peer that validation of the component tree is about to
+ * begin.
+ *
+ * @see Container#validate()
+ */
void beginValidate();
+
+ /**
+ * Notifies the peer that validation of the component tree is finished.
+ *
+ * @see Container#validate()
+ */
void endValidate();
+
+ /**
+ * Notifies the peer that layout is about to begin. This is called
+ * before the container itself and its children are laid out.
+ *
+ * @see Container#validateTree()
+ */
void beginLayout();
+
+ /**
+ * Notifies the peer that layout is finished. This is called after the
+ * container and its children have been laid out.
+ *
+ * @see Container#validateTree()
+ */
void endLayout();
- boolean isPaintPending();
/**
- * Restacks native windows - children of this native window - according to Java container order
+ * Restacks native windows - children of this native window - according to
+ * Java container order.
+ *
* @since 1.5
*/
void restack();
/**
- * Indicates availabiltity of restacking operation in this container.
+ * Indicates availability of restacking operation in this container.
+ *
* @return Returns true if restack is supported, false otherwise
+ *
* @since 1.5
*/
boolean isRestackSupported();
- /**
-
-
-
- * DEPRECATED: Replaced by getInsets().
- */
- Insets insets();
}
diff --git a/src/share/classes/java/awt/peer/DesktopPeer.java b/src/share/classes/java/awt/peer/DesktopPeer.java
index e0e5ad2ad..1b2d1bdb8 100644
--- a/src/share/classes/java/awt/peer/DesktopPeer.java
+++ b/src/share/classes/java/awt/peer/DesktopPeer.java
@@ -32,7 +32,7 @@ import java.net.URI;
import java.awt.Desktop.Action;
/**
- * The <code>DesktopPeer</code> interface provides methods for the operation
+ * The {@code DesktopPeer} interface provides methods for the operation
* of open, edit, print, browse and mail with the given URL or file, by
* launching the associated application.
* <p>
@@ -40,14 +40,15 @@ import java.awt.Desktop.Action;
*
*/
public interface DesktopPeer {
+
/**
* Returns whether the given action is supported on the current platform.
* @param action the action type to be tested if it's supported on the
* current platform.
- * @return <code>true</code> if the given action is supported on
- * the current platform; <code>false</code> otherwise.
+ * @return {@code true} if the given action is supported on
+ * the current platform; {@code false} otherwise.
*/
- public boolean isSupported(Action action);
+ boolean isSupported(Action action);
/**
* Launches the associated application to open the given file. The
@@ -58,7 +59,7 @@ public interface DesktopPeer {
* @throws IOException If the given file has no associated application,
* or the associated application fails to be launched.
*/
- public void open(File file) throws IOException;
+ void open(File file) throws IOException;
/**
* Launches the associated editor and opens the given file for editing. The
@@ -69,7 +70,7 @@ public interface DesktopPeer {
* @throws IOException If the given file has no associated editor, or
* the associated application fails to be launched.
*/
- public void edit(File file) throws IOException;
+ void edit(File file) throws IOException;
/**
* Prints the given file with the native desktop printing facility, using
@@ -79,7 +80,7 @@ public interface DesktopPeer {
* @throws IOException If the given file has no associated application
* that can be used to print it.
*/
- public void print(File file) throws IOException;
+ void print(File file) throws IOException;
/**
* Launches the mail composing window of the user default mail client,
@@ -93,7 +94,7 @@ public interface DesktopPeer {
* @throws IOException If the user default mail client is not found,
* or it fails to be launched.
*/
- public void mail(URI mailtoURL) throws IOException;
+ void mail(URI mailtoURL) throws IOException;
/**
* Launches the user default browser to display the given URI.
@@ -102,5 +103,5 @@ public interface DesktopPeer {
* @throws IOException If the user default browser is not found,
* or it fails to be launched.
*/
- public void browse(URI url) throws IOException;
+ void browse(URI url) throws IOException;
}
diff --git a/src/share/classes/java/awt/peer/DialogPeer.java b/src/share/classes/java/awt/peer/DialogPeer.java
index 7c08206d4..27ac26fff 100644
--- a/src/share/classes/java/awt/peer/DialogPeer.java
+++ b/src/share/classes/java/awt/peer/DialogPeer.java
@@ -28,6 +28,9 @@ package java.awt.peer;
import java.awt.*;
/**
+ * The peer interface for {@link Dialog}. This adds a couple of dialog specific
+ * features to the {@link WindowPeer} interface.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -35,7 +38,33 @@ import java.awt.*;
* instances.
*/
public interface DialogPeer extends WindowPeer {
+
+ /**
+ * Sets the title on the dialog window.
+ *
+ * @param title the title to set
+ *
+ * @see Dialog#setTitle(String)
+ */
void setTitle(String title);
+
+ /**
+ * Sets if the dialog should be resizable or not.
+ *
+ * @param resizeable {@code true} when the dialog should be resizable,
+ * {@code false} if not
+ *
+ * @see Dialog#setResizable(boolean)
+ */
void setResizable(boolean resizeable);
+
+ /**
+ * Block the specified windows. This is used for modal dialogs.
+ *
+ * @param windows the windows to block
+ *
+ * @see Dialog#modalShow()
+ * @see Dialog#blockWindows()
+ */
void blockWindows(java.util.List<Window> windows);
}
diff --git a/src/share/classes/java/awt/peer/FileDialogPeer.java b/src/share/classes/java/awt/peer/FileDialogPeer.java
index d4a326a4e..29fc4fdb5 100644
--- a/src/share/classes/java/awt/peer/FileDialogPeer.java
+++ b/src/share/classes/java/awt/peer/FileDialogPeer.java
@@ -25,9 +25,12 @@
package java.awt.peer;
+import java.awt.FileDialog;
import java.io.FilenameFilter;
/**
+ * The peer interface for {@link FileDialog}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -35,7 +38,32 @@ import java.io.FilenameFilter;
* instances.
*/
public interface FileDialogPeer extends DialogPeer {
+
+ /**
+ * Sets the selected file for this file dialog.
+ *
+ * @param file the file to set as selected file, or {@code null} for
+ * no selected file
+ *
+ * @see FileDialog#setFile(String)
+ */
void setFile(String file);
+
+ /**
+ * Sets the current directory for this file dialog.
+ *
+ * @param dir the directory to set
+ *
+ * @see FileDialog#setDirectory(String)
+ */
void setDirectory(String dir);
+
+ /**
+ * Sets the filename filter for filtering the displayed files.
+ *
+ * @param filter the filter to set
+ *
+ * @see FileDialog#setFilenameFilter(FilenameFilter)
+ */
void setFilenameFilter(FilenameFilter filter);
}
diff --git a/src/share/classes/java/awt/peer/FontPeer.java b/src/share/classes/java/awt/peer/FontPeer.java
index 471c3fd1f..63a592692 100644
--- a/src/share/classes/java/awt/peer/FontPeer.java
+++ b/src/share/classes/java/awt/peer/FontPeer.java
@@ -26,6 +26,9 @@
package java.awt.peer;
/**
+ * The peer interface for fonts. This is only a marker interface and not
+ * used by AWT itself.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
diff --git a/src/share/classes/java/awt/peer/FramePeer.java b/src/share/classes/java/awt/peer/FramePeer.java
index 76f0080f7..942b92cc8 100644
--- a/src/share/classes/java/awt/peer/FramePeer.java
+++ b/src/share/classes/java/awt/peer/FramePeer.java
@@ -27,7 +27,12 @@ package java.awt.peer;
import java.awt.*;
+import sun.awt.EmbeddedFrame;
+
/**
+ * The peer interface for {@link Frame}. This adds a couple of frame specific
+ * methods to the {@link WindowPeer} interface.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -35,12 +40,89 @@ import java.awt.*;
* instances.
*/
public interface FramePeer extends WindowPeer {
+
+ /**
+ * Sets the title on the frame.
+ *
+ * @param title the title to set
+ *
+ * @see Frame#setTitle(String)
+ */
void setTitle(String title);
+
+ /**
+ * Sets the menu bar for the frame.
+ *
+ * @param mb the menu bar to set
+ *
+ * @see Frame#setMenuBar(MenuBar)
+ */
void setMenuBar(MenuBar mb);
+
+ /**
+ * Sets if the frame should be resizable or not.
+ *
+ * @param resizeable {@code true} when the frame should be resizable,
+ * {@code false} if not
+ *
+ * @see Frame#setResizable(boolean)
+ */
void setResizable(boolean resizeable);
+
+ /**
+ * Changes the state of the frame.
+ *
+ * @param state the new state
+ *
+ * @see Frame#setExtendedState(int)
+ */
void setState(int state);
- int getState();
- void setMaximizedBounds(Rectangle bounds); // XXX
+
+ /**
+ * Returns the current state of the frame.
+ *
+ * @return the current state of the frame
+ *
+ * @see Frame#getExtendedState()
+ */
+ int getState();
+
+ /**
+ * Sets the bounds of the frame when it becomes maximized.
+ *
+ * @param bounds the maximized bounds of the frame
+ *
+ * @see Frame#setMaximizedBounds(Rectangle)
+ */
+ void setMaximizedBounds(Rectangle bounds);
+
+ /**
+ * Sets the size and location for embedded frames. (On embedded frames,
+ * setLocation() and setBounds() always set the frame to (0,0) for
+ * backwards compatibility.
+ *
+ * @param x the X location
+ * @param y the Y location
+ * @param width the width of the frame
+ * @param height the height of the frame
+ *
+ * @see EmbeddedFrame#setBoundsPrivate(int, int, int, int)
+ */
+ // TODO: This is only used in EmbeddedFrame, and should probably be moved
+ // into an EmbeddedFramePeer which would extend FramePeer
void setBoundsPrivate(int x, int y, int width, int height);
+
+ /**
+ * Returns the size and location for embedded frames. (On embedded frames,
+ * setLocation() and setBounds() always set the frame to (0,0) for
+ * backwards compatibility.
+ *
+ * @return the bounds of an embedded frame
+ *
+ * @see EmbeddedFrame#getBoundsPrivate()
+ */
+ // TODO: This is only used in EmbeddedFrame, and should probably be moved
+ // into an EmbeddedFramePeer which would extend FramePeer
Rectangle getBoundsPrivate();
+
}
diff --git a/src/share/classes/java/awt/peer/KeyboardFocusManagerPeer.java b/src/share/classes/java/awt/peer/KeyboardFocusManagerPeer.java
index f00db9341..e465de83e 100644
--- a/src/share/classes/java/awt/peer/KeyboardFocusManagerPeer.java
+++ b/src/share/classes/java/awt/peer/KeyboardFocusManagerPeer.java
@@ -28,11 +28,45 @@ package java.awt.peer;
import java.awt.Component;
import java.awt.Window;
+/**
+ * The native peer interface for {@link KeyboardFocusManager}.
+ */
public interface KeyboardFocusManagerPeer {
+
+ /**
+ * Returns the currently focused window.
+ *
+ * @return the currently focused window
+ *
+ * @see KeyboardFocusManager#getNativeFocusedWindow()
+ */
Window getCurrentFocusedWindow();
+ /**
+ * Sets the component that should become the focus owner.
+ *
+ * @param comp the component to become the focus owner
+ *
+ * @see KeyboardFocusManager#setNativeFocusOwner(Component)
+ */
void setCurrentFocusOwner(Component comp);
+
+ /**
+ * Returns the component that currently owns the input focus.
+ *
+ * @return the component that currently owns the input focus
+ *
+ * @see KeyboardFocusManager#getNativeFocusOwner()
+ */
Component getCurrentFocusOwner();
+ /**
+ * Clears the current global focus owner.
+ *
+ * @param activeWindow
+ *
+ * @see KeyboardFocusManager#clearGlobalFocusOwner()
+ */
void clearGlobalFocusOwner(Window activeWindow);
+
}
diff --git a/src/share/classes/java/awt/peer/LabelPeer.java b/src/share/classes/java/awt/peer/LabelPeer.java
index 395260558..5421d56e4 100644
--- a/src/share/classes/java/awt/peer/LabelPeer.java
+++ b/src/share/classes/java/awt/peer/LabelPeer.java
@@ -24,7 +24,11 @@
*/
package java.awt.peer;
+import java.awt.Label;
+
/**
+ * The peer interface for {@link Label}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -32,6 +36,25 @@ package java.awt.peer;
* instances.
*/
public interface LabelPeer extends ComponentPeer {
+
+ /**
+ * Sets the text to be displayed on the label.
+ *
+ * @param label the text to be displayed on the label
+ *
+ * @see Label#setText
+ */
void setText(String label);
+
+ /**
+ * Sets the alignment of the label text.
+ *
+ * @param alignment the alignment of the label text
+ *
+ * @see Label#setAlignment(int)
+ * @see Label#CENTER
+ * @see Label#RIGHT
+ * @see Label#LEFT
+ */
void setAlignment(int alignment);
}
diff --git a/src/share/classes/java/awt/peer/ListPeer.java b/src/share/classes/java/awt/peer/ListPeer.java
index 643f47ae9..87ce55b1c 100644
--- a/src/share/classes/java/awt/peer/ListPeer.java
+++ b/src/share/classes/java/awt/peer/ListPeer.java
@@ -25,8 +25,11 @@
package java.awt.peer;
import java.awt.Dimension;
+import java.awt.List;
/**
+ * The peer interface for {@link List}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,39 +37,102 @@ import java.awt.Dimension;
* instances.
*/
public interface ListPeer extends ComponentPeer {
+
+ /**
+ * Returns the indices of the list items that are currently selected.
+ * The returned array is not required to be a copy, the callers of this
+ * method already make sure it is not modified.
+ *
+ * @return the indices of the list items that are currently selected
+ *
+ * @see List#getSelectedIndexes()
+ */
int[] getSelectedIndexes();
+
+ /**
+ * Adds an item to the list at the specified index.
+ *
+ * @param item the item to add to the list
+ * @param index the index where to add the item into the list
+ *
+ * @see List#add(String, int)
+ */
void add(String item, int index);
+
+ /**
+ * Deletes items from the list. All items from start to end should are
+ * deleted, including the item at the start and end indices.
+ *
+ * @param start the first item to be deleted
+ * @param end the last item to be deleted
+ */
void delItems(int start, int end);
+
+ /**
+ * Removes all items from the list.
+ *
+ * @see List#removeAll()
+ */
void removeAll();
+
+ /**
+ * Selects the item at the specified {@code index}.
+ *
+ * @param index the index of the item to select
+ *
+ * @see List#select(int)
+ */
void select(int index);
- void deselect(int index);
- void makeVisible(int index);
- void setMultipleMode(boolean b);
- Dimension getPreferredSize(int rows);
- Dimension getMinimumSize(int rows);
/**
- * DEPRECATED: Replaced by add(String, int).
+ * De-selects the item at the specified {@code index}.
+ *
+ * @param index the index of the item to de-select
+ *
+ * @see List#deselect(int)
*/
- void addItem(String item, int index);
+ void deselect(int index);
/**
- * DEPRECATED: Replaced by removeAll().
+ * Makes sure that the item at the specified {@code index} is visible,
+ * by scrolling the list or similar.
+ *
+ * @param index the index of the item to make visible
+ *
+ * @see List#makeVisible(int)
*/
- void clear();
+ void makeVisible(int index);
/**
- * DEPRECATED: Replaced by setMultipleMode(boolean).
+ * Toggles multiple selection mode on or off.
+ *
+ * @param m {@code true} for multiple selection mode,
+ * {@code false} for single selection mode
+ *
+ * @see List#setMultipleMode(boolean)
*/
- void setMultipleSelections(boolean v);
+ void setMultipleMode(boolean m);
/**
- * DEPRECATED: Replaced by getPreferredSize(int).
+ * Returns the preferred size for a list with the specified number of rows.
+ *
+ * @param rows the number of rows
+ *
+ * @return the preferred size of the list
+ *
+ * @see List#getPreferredSize(int)
*/
- Dimension preferredSize(int v);
+ Dimension getPreferredSize(int rows);
/**
- * DEPRECATED: Replaced by getMinimumSize(int).
+ * Returns the minimum size for a list with the specified number of rows.
+ *
+ * @param rows the number of rows
+ *
+ * @return the minimum size of the list
+ *
+ * @see List#getMinimumSize(int)
*/
- Dimension minimumSize(int v);
+ Dimension getMinimumSize(int rows);
+
}
diff --git a/src/share/classes/java/awt/peer/MenuBarPeer.java b/src/share/classes/java/awt/peer/MenuBarPeer.java
index 41b6e30b6..fd3a73fd5 100644
--- a/src/share/classes/java/awt/peer/MenuBarPeer.java
+++ b/src/share/classes/java/awt/peer/MenuBarPeer.java
@@ -25,8 +25,11 @@
package java.awt.peer;
import java.awt.Menu;
+import java.awt.MenuBar;
/**
+ * The peer interface for {@link MenuBar}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,7 +37,31 @@ import java.awt.Menu;
* instances.
*/
public interface MenuBarPeer extends MenuComponentPeer {
+
+ /**
+ * Adds a menu to the menu bar.
+ *
+ * @param m the menu to add
+ *
+ * @see MenuBar#add(Menu)
+ */
void addMenu(Menu m);
+
+ /**
+ * Deletes a menu from the menu bar.
+ *
+ * @param index the index of the menu to remove
+ *
+ * @see MenuBar#remove(int)
+ */
void delMenu(int index);
+
+ /**
+ * Adds a help menu to the menu bar.
+ *
+ * @param m the help menu to add
+ *
+ * @see MenuBar#setHelpMenu(Menu)
+ */
void addHelpMenu(Menu m);
}
diff --git a/src/share/classes/java/awt/peer/MenuComponentPeer.java b/src/share/classes/java/awt/peer/MenuComponentPeer.java
index 83e43e771..fa20a4d49 100644
--- a/src/share/classes/java/awt/peer/MenuComponentPeer.java
+++ b/src/share/classes/java/awt/peer/MenuComponentPeer.java
@@ -25,8 +25,12 @@
package java.awt.peer;
import java.awt.Font;
+import java.awt.MenuComponent;
/**
+ * The base interface for all kinds of menu components. This is used by
+ * {@link MenuComponent}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,6 +38,20 @@ import java.awt.Font;
* instances.
*/
public interface MenuComponentPeer {
+
+ /**
+ * Disposes the menu component.
+ *
+ * @see MenuComponent#removeNotify()
+ */
void dispose();
+
+ /**
+ * Sets the font for the menu component.
+ *
+ * @param f the font to use for the menu component
+ *
+ * @see MenuComponent#setFont(Font)
+ */
void setFont(Font f);
}
diff --git a/src/share/classes/java/awt/peer/MenuItemPeer.java b/src/share/classes/java/awt/peer/MenuItemPeer.java
index 9aa8b4e50..7551bd552 100644
--- a/src/share/classes/java/awt/peer/MenuItemPeer.java
+++ b/src/share/classes/java/awt/peer/MenuItemPeer.java
@@ -24,7 +24,11 @@
*/
package java.awt.peer;
+import java.awt.MenuItem;
+
/**
+ * The peer interface for menu items. This is used by {@link MenuItem}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -32,16 +36,20 @@ package java.awt.peer;
* instances.
*/
public interface MenuItemPeer extends MenuComponentPeer {
- void setLabel(String label);
- void setEnabled(boolean b);
/**
- * DEPRECATED: Replaced by setEnabled(boolean).
+ * Sets the label to be displayed in this menu item.
+ *
+ * @param label the label to be displayed
*/
- void enable();
+ void setLabel(String label);
/**
- * DEPRECATED: Replaced by setEnabled(boolean).
+ * Enables or disables the menu item.
+ *
+ * @param e {@code true} to enable the menu item, {@code false}
+ * to disable it
*/
- void disable();
+ void setEnabled(boolean e);
+
}
diff --git a/src/share/classes/java/awt/peer/MenuPeer.java b/src/share/classes/java/awt/peer/MenuPeer.java
index 9e941aabe..2b399ef6a 100644
--- a/src/share/classes/java/awt/peer/MenuPeer.java
+++ b/src/share/classes/java/awt/peer/MenuPeer.java
@@ -24,9 +24,12 @@
*/
package java.awt.peer;
+import java.awt.Menu;
import java.awt.MenuItem;
/**
+ * The peer interface for menus. This is used by {@link Menu}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,7 +37,29 @@ import java.awt.MenuItem;
* instances.
*/
public interface MenuPeer extends MenuItemPeer {
+
+ /**
+ * Adds a separator (e.g. a horizontal line or similar) to the menu.
+ *
+ * @see Menu#addSeparator()
+ */
void addSeparator();
+
+ /**
+ * Adds the specified menu item to the menu.
+ *
+ * @param item the menu item to add
+ *
+ * @see Menu#add(MenuItem)
+ */
void addItem(MenuItem item);
+
+ /**
+ * Removes the menu item at the specified index.
+ *
+ * @param index the index of the item to remove
+ *
+ * @see Menu#remove(int)
+ */
void delItem(int index);
}
diff --git a/src/share/classes/java/awt/peer/MouseInfoPeer.java b/src/share/classes/java/awt/peer/MouseInfoPeer.java
index 3e1cd5360..c3297943e 100644
--- a/src/share/classes/java/awt/peer/MouseInfoPeer.java
+++ b/src/share/classes/java/awt/peer/MouseInfoPeer.java
@@ -29,6 +29,9 @@ import java.awt.Window;
import java.awt.Point;
/**
+ * Peer interface for {@link MouseInfo}. This is used to get some additional
+ * information about the mouse.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
diff --git a/src/share/classes/java/awt/peer/PanelPeer.java b/src/share/classes/java/awt/peer/PanelPeer.java
index bb123adc9..1b443bcb7 100644
--- a/src/share/classes/java/awt/peer/PanelPeer.java
+++ b/src/share/classes/java/awt/peer/PanelPeer.java
@@ -25,6 +25,10 @@
package java.awt.peer;
/**
+ * The peer interface for {@link Panel}. This is a subinterface of
+ * ContainerPeer and does not declare any additional methods because a Panel
+ * is just that, a concrete Container.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
diff --git a/src/share/classes/java/awt/peer/PopupMenuPeer.java b/src/share/classes/java/awt/peer/PopupMenuPeer.java
index 1bad6bc09..16eeb1fb2 100644
--- a/src/share/classes/java/awt/peer/PopupMenuPeer.java
+++ b/src/share/classes/java/awt/peer/PopupMenuPeer.java
@@ -25,8 +25,11 @@
package java.awt.peer;
import java.awt.Event;
+import java.awt.PopupMenu;
/**
+ * The peer interface for {@link PopupMenu}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,5 +37,14 @@ import java.awt.Event;
* instances.
*/
public interface PopupMenuPeer extends MenuPeer {
+
+ /**
+ * Shows the popup menu.
+ *
+ * @param e a synthetic event describing the origin and location of the
+ * popup menu
+ *
+ * @see PopupMenu#show(java.awt.Component, int, int)
+ */
void show(Event e);
}
diff --git a/src/share/classes/java/awt/peer/RobotPeer.java b/src/share/classes/java/awt/peer/RobotPeer.java
index dfd4ca78d..ce50ed587 100644
--- a/src/share/classes/java/awt/peer/RobotPeer.java
+++ b/src/share/classes/java/awt/peer/RobotPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -39,17 +39,93 @@ import java.awt.*;
*/
public interface RobotPeer
{
- public void mouseMove(int x, int y);
- public void mousePress(int buttons);
- public void mouseRelease(int buttons);
+ /**
+ * Moves the mouse pointer to the specified screen location.
+ *
+ * @param x the X location on screen
+ * @param y the Y location on screen
+ *
+ * @see Robot#mouseMove(int, int)
+ */
+ void mouseMove(int x, int y);
- public void mouseWheel(int wheelAmt);
+ /**
+ * Simulates a mouse press with the specified button(s).
+ *
+ * @param buttons the button mask
+ *
+ * @see Robot#mousePress(int)
+ */
+ void mousePress(int buttons);
- public void keyPress(int keycode);
- public void keyRelease(int keycode);
+ /**
+ * Simulates a mouse release with the specified button(s).
+ *
+ * @param buttons the button mask
+ *
+ * @see Robot#mouseRelease(int)
+ */
+ void mouseRelease(int buttons);
- public int getRGBPixel(int x, int y);
- public int [] getRGBPixels(Rectangle bounds);
+ /**
+ * Simulates mouse wheel action.
+ *
+ * @param wheelAmt number of notches to move the mouse wheel
+ *
+ * @see Robot#mouseWheel(int)
+ */
+ void mouseWheel(int wheelAmt);
- public void dispose();
+ /**
+ * Simulates a key press of the specified key.
+ *
+ * @param keycode the key code to press
+ *
+ * @see Robot#keyPress(int)
+ */
+ void keyPress(int keycode);
+
+ /**
+ * Simulates a key release of the specified key.
+ *
+ * @param keycode the key code to release
+ *
+ * @see Robot#keyRelease(int)
+ */
+ void keyRelease(int keycode);
+
+ /**
+ * Gets the RGB value of the specified pixel on screen.
+ *
+ * @param x the X screen coordinate
+ * @param y the Y screen coordinate
+ *
+ * @return the RGB value of the specified pixel on screen
+ *
+ * @see Robot#getPixelColor(int, int)
+ */
+ int getRGBPixel(int x, int y);
+
+ /**
+ * Gets the RGB values of the specified screen area as an array.
+ *
+ * @param bounds the screen area to capture the RGB values from
+ *
+ * @return the RGB values of the specified screen area
+ *
+ * @see Robot#createScreenCapture(Rectangle)
+ */
+ int[] getRGBPixels(Rectangle bounds);
+
+ /**
+ * Disposes the robot peer when it is not needed anymore.
+ */
+ void dispose();
+
+ /**
+ * Returns the number of buttons that the robot simulates.
+ *
+ * @return the number of buttons that the robot simulates
+ */
+ int getNumberOfButtons();
}
diff --git a/src/share/classes/java/awt/peer/ScrollPanePeer.java b/src/share/classes/java/awt/peer/ScrollPanePeer.java
index 0097bdeb3..b6529afe6 100644
--- a/src/share/classes/java/awt/peer/ScrollPanePeer.java
+++ b/src/share/classes/java/awt/peer/ScrollPanePeer.java
@@ -25,8 +25,12 @@
package java.awt.peer;
import java.awt.Adjustable;
+import java.awt.ScrollPane;
+import java.awt.ScrollPaneAdjustable;
/**
+ * The peer interface for {@link ScrollPane}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,10 +38,60 @@ import java.awt.Adjustable;
* instances.
*/
public interface ScrollPanePeer extends ContainerPeer {
+
+ /**
+ * Returns the height of the horizontal scroll bar.
+ *
+ * @return the height of the horizontal scroll bar
+ *
+ * @see ScrollPane#getHScrollbarHeight()
+ */
int getHScrollbarHeight();
+
+ /**
+ * Returns the width of the vertical scroll bar.
+ *
+ * @return the width of the vertical scroll bar
+ *
+ * @see ScrollPane#getVScrollbarWidth()
+ */
int getVScrollbarWidth();
+
+ /**
+ * Sets the scroll position of the child.
+ *
+ * @param x the X coordinate of the scroll position
+ * @param y the Y coordinate of the scroll position
+ *
+ * @see ScrollPane#setScrollPosition(int, int)
+ */
void setScrollPosition(int x, int y);
+
+ /**
+ * Called when the child component changes its size.
+ *
+ * @param w the new width of the child component
+ * @param h the new height of the child component
+ *
+ * @see ScrollPane#layout()
+ */
void childResized(int w, int h);
+
+ /**
+ * Sets the unit increment of one of the scroll pane's adjustables.
+ *
+ * @param adj the scroll pane adjustable object
+ * @param u the unit increment
+ *
+ * @see ScrollPaneAdjustable#setUnitIncrement(int)
+ */
void setUnitIncrement(Adjustable adj, int u);
+
+ /**
+ * Sets the value for one of the scroll pane's adjustables.
+ *
+ * @param adj the scroll pane adjustable object
+ * @param v the value to set
+ */
void setValue(Adjustable adj, int v);
}
diff --git a/src/share/classes/java/awt/peer/ScrollbarPeer.java b/src/share/classes/java/awt/peer/ScrollbarPeer.java
index e3b11139e..c9e1b49dd 100644
--- a/src/share/classes/java/awt/peer/ScrollbarPeer.java
+++ b/src/share/classes/java/awt/peer/ScrollbarPeer.java
@@ -24,7 +24,11 @@
*/
package java.awt.peer;
+import java.awt.Scrollbar;
+
/**
+ * The peer interface for {@link Scrollbar}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -32,7 +36,34 @@ package java.awt.peer;
* instances.
*/
public interface ScrollbarPeer extends ComponentPeer {
+
+ /**
+ * Sets the parameters for the scrollbar.
+ *
+ * @param value the current value
+ * @param visible how much of the whole scale is visible
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ *
+ * @see Scrollbar#setValues(int, int, int, int)
+ */
void setValues(int value, int visible, int minimum, int maximum);
+
+ /**
+ * Sets the line increment of the scrollbar.
+ *
+ * @param l the line increment
+ *
+ * @see Scrollbar#setLineIncrement(int)
+ */
void setLineIncrement(int l);
+
+ /**
+ * Sets the page increment of the scrollbar.
+ *
+ * @param l the page increment
+ *
+ * @see Scrollbar#setPageIncrement(int)
+ */
void setPageIncrement(int l);
}
diff --git a/src/share/classes/java/awt/peer/SystemTrayPeer.java b/src/share/classes/java/awt/peer/SystemTrayPeer.java
index 883e24590..30ce90ac4 100644
--- a/src/share/classes/java/awt/peer/SystemTrayPeer.java
+++ b/src/share/classes/java/awt/peer/SystemTrayPeer.java
@@ -26,7 +26,20 @@
package java.awt.peer;
import java.awt.Dimension;
+import java.awt.SystemTray;
+/**
+ * The peer interface for {@link SystemTray}. This doesn't need to be
+ * implemented if {@link SystemTray#isSupported()} returns false.
+ */
public interface SystemTrayPeer {
+
+ /**
+ * Returns the size of the system tray icon.
+ *
+ * @return the size of the system tray icon
+ *
+ * @see SystemTray#getTrayIconSize()
+ */
Dimension getTrayIconSize();
}
diff --git a/src/share/classes/java/awt/peer/TextAreaPeer.java b/src/share/classes/java/awt/peer/TextAreaPeer.java
index 1e65e3240..0dcafb589 100644
--- a/src/share/classes/java/awt/peer/TextAreaPeer.java
+++ b/src/share/classes/java/awt/peer/TextAreaPeer.java
@@ -25,8 +25,11 @@
package java.awt.peer;
import java.awt.Dimension;
+import java.awt.TextArea;
/**
+ * The peer interface for {@link TexTArea}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,28 +37,52 @@ import java.awt.Dimension;
* instances.
*/
public interface TextAreaPeer extends TextComponentPeer {
- void insert(String text, int pos);
- void replaceRange(String text, int start, int end);
- Dimension getPreferredSize(int rows, int columns);
- Dimension getMinimumSize(int rows, int columns);
/**
- * DEPRECATED: Replaced by insert(String, int).
+ * Inserts the specified text at the specified position in the document.
+ *
+ * @param text the text to insert
+ * @param pos the position to insert
+ *
+ * @see TextArea#insert(String, int)
*/
- void insertText(String txt, int pos);
+ void insert(String text, int pos);
/**
- * DEPRECATED: Replaced by ReplaceRange(String, int, int).
+ * Replaces a range of text by the specified string
+ *
+ * @param text the replacement string
+ * @param start the begin of the range to replace
+ * @param end the end of the range to replace
+ *
+ * @see TextArea#replaceRange(String, int, int)
*/
- void replaceText(String txt, int start, int end);
+ void replaceRange(String text, int start, int end);
/**
- * DEPRECATED: Replaced by getPreferredSize(int, int).
+ * Returns the preferred size of a textarea with the specified number of
+ * columns and rows.
+ *
+ * @param rows the number of rows
+ * @param columns the number of columns
+ *
+ * @return the preferred size of a textarea
+ *
+ * @see TextArea#getPreferredSize(int, int)
*/
- Dimension preferredSize(int rows, int cols);
+ Dimension getPreferredSize(int rows, int columns);
/**
- * DEPRECATED: Replaced by getMinimumSize(int, int).
+ * Returns the minimum size of a textarea with the specified number of
+ * columns and rows.
+ *
+ * @param rows the number of rows
+ * @param columns the number of columns
+ *
+ * @return the minimum size of a textarea
+ *
+ * @see TextArea#getMinimumSize(int, int)
*/
- Dimension minimumSize(int rows, int cols);
+ Dimension getMinimumSize(int rows, int columns);
+
}
diff --git a/src/share/classes/java/awt/peer/TextComponentPeer.java b/src/share/classes/java/awt/peer/TextComponentPeer.java
index db4bd9096..35228e575 100644
--- a/src/share/classes/java/awt/peer/TextComponentPeer.java
+++ b/src/share/classes/java/awt/peer/TextComponentPeer.java
@@ -24,10 +24,12 @@
*/
package java.awt.peer;
-import java.awt.Rectangle;
+import java.awt.TextComponent;
import java.awt.im.InputMethodRequests;
/**
+ * The peer interface for {@link TextComponent}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -35,16 +37,85 @@ import java.awt.im.InputMethodRequests;
* instances.
*/
public interface TextComponentPeer extends ComponentPeer {
+
+ /**
+ * Sets if the text component should be editable or not.
+ *
+ * @param editable {@code true} for editable text components,
+ * {@code false} for non-editable text components
+ *
+ * @see TextComponent#setEditable(boolean)
+ */
void setEditable(boolean editable);
+
+ /**
+ * Returns the current content of the text component.
+ *
+ * @return the current content of the text component
+ *
+ * @see TextComponent#getText()
+ */
String getText();
+
+ /**
+ * Sets the content for the text component.
+ *
+ * @param l the content to set
+ *
+ * @see TextComponent#setText(String)
+ */
void setText(String l);
+
+ /**
+ * Returns the start index of the current selection.
+ *
+ * @return the start index of the current selection
+ *
+ * @see TextComponent#getSelectionStart()
+ */
int getSelectionStart();
+
+ /**
+ * Returns the end index of the current selection.
+ *
+ * @return the end index of the current selection
+ *
+ * @see TextComponent#getSelectionEnd()
+ */
int getSelectionEnd();
+
+ /**
+ * Selects an area of the text component.
+ *
+ * @param selStart the start index of the new selection
+ * @param selEnd the end index of the new selection
+ *
+ * @see TextComponent#select(int, int)
+ */
void select(int selStart, int selEnd);
+
+ /**
+ * Sets the caret position of the text component.
+ *
+ * @param pos the caret position to set
+ *
+ * @see TextComponent#setCaretPosition(int)
+ */
void setCaretPosition(int pos);
+
+ /**
+ * Returns the current caret position.
+ *
+ * @return the current caret position
+ *
+ * @see TextComponent#getCaretPosition()
+ */
int getCaretPosition();
- int getIndexAtPoint(int x, int y);
- Rectangle getCharacterBounds(int i);
- long filterEvents(long mask);
+
+ /**
+ * Returns the input method requests.
+ *
+ * @return the input method requests
+ */
InputMethodRequests getInputMethodRequests();
}
diff --git a/src/share/classes/java/awt/peer/TextFieldPeer.java b/src/share/classes/java/awt/peer/TextFieldPeer.java
index 1a7f3f77b..4c6c6cea3 100644
--- a/src/share/classes/java/awt/peer/TextFieldPeer.java
+++ b/src/share/classes/java/awt/peer/TextFieldPeer.java
@@ -25,8 +25,11 @@
package java.awt.peer;
import java.awt.Dimension;
+import java.awt.TextField;
/**
+ * The peer interface for {@link TextField}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -34,22 +37,38 @@ import java.awt.Dimension;
* instances.
*/
public interface TextFieldPeer extends TextComponentPeer {
- void setEchoChar(char echoChar);
- Dimension getPreferredSize(int columns);
- Dimension getMinimumSize(int columns);
/**
- * DEPRECATED: Replaced by setEchoChar(char echoChar).
+ * Sets the echo character.
+ *
+ * @param echoChar the echo character to set
+ *
+ * @see TextField#getEchoChar()
*/
- void setEchoCharacter(char c);
+ void setEchoChar(char echoChar);
/**
- * DEPRECATED: Replaced by getPreferredSize(int).
+ * Returns the preferred size of the text field with the specified number
+ * of columns.
+ *
+ * @param columns the number of columns
+ *
+ * @return the preferred size of the text field
+ *
+ * @see TextField#getPreferredSize(int)
*/
- Dimension preferredSize(int cols);
+ Dimension getPreferredSize(int columns);
/**
- * DEPRECATED: Replaced by getMinimumSize(int).
+ * Returns the minimum size of the text field with the specified number
+ * of columns.
+ *
+ * @param columns the number of columns
+ *
+ * @return the minimum size of the text field
+ *
+ * @see TextField#getMinimumSize(int)
*/
- Dimension minimumSize(int cols);
+ Dimension getMinimumSize(int columns);
+
}
diff --git a/src/share/classes/java/awt/peer/TrayIconPeer.java b/src/share/classes/java/awt/peer/TrayIconPeer.java
index 6e7c23fad..6b1a583b4 100644
--- a/src/share/classes/java/awt/peer/TrayIconPeer.java
+++ b/src/share/classes/java/awt/peer/TrayIconPeer.java
@@ -25,10 +25,56 @@
package java.awt.peer;
+import java.awt.SystemTray;
+import java.awt.TrayIcon;
+
+/**
+ * The peer interface for the {@link TrayIcon}. This doesn't need to be
+ * implemented if {@link SystemTray#isSupported()} returns false.
+ */
public interface TrayIconPeer {
+
+ /**
+ * Disposes the tray icon and releases and resources held by it.
+ *
+ * @see TrayIcon#removeNotify()
+ */
void dispose();
+
+ /**
+ * Sets the tool tip for the tray icon.
+ *
+ * @param tooltip the tooltip to set
+ *
+ * @see TrayIcon#setToolTip(String)
+ */
void setToolTip(String tooltip);
+
+ /**
+ * Updates the icon image. This is supposed to display the current icon
+ * from the TrayIcon component in the actual tray icon.
+ *
+ * @see TrayIcon#setImage(java.awt.Image)
+ * @see TrayIcon#setImageAutoSize(boolean)
+ */
void updateImage();
+
+ /**
+ * Displays a message at the tray icon.
+ *
+ * @param caption the message caption
+ * @param text the actual message text
+ * @param messageType the message type
+ *
+ * @see TrayIcon#displayMessage(String, String, java.awt.TrayIcon.MessageType)
+ */
void displayMessage(String caption, String text, String messageType);
+
+ /**
+ * Shows the popup menu of this tray icon at the specified position.
+ *
+ * @param x the X location for the popup menu
+ * @param y the Y location for the popup menu
+ */
void showPopupMenu(int x, int y);
}
diff --git a/src/share/classes/java/awt/peer/WindowPeer.java b/src/share/classes/java/awt/peer/WindowPeer.java
index 2ecd4b4f3..7b5da857f 100644
--- a/src/share/classes/java/awt/peer/WindowPeer.java
+++ b/src/share/classes/java/awt/peer/WindowPeer.java
@@ -28,6 +28,8 @@ package java.awt.peer;
import java.awt.*;
/**
+ * The peer interface for {@link Window}.
+ *
* The peer interfaces are intended only for use in porting
* the AWT. They are not intended for use by application
* developers, and developers should not implement peers
@@ -35,12 +37,59 @@ import java.awt.*;
* instances.
*/
public interface WindowPeer extends ContainerPeer {
+
+ /**
+ * Makes this window the topmost window on the desktop.
+ *
+ * @see Window#toFront()
+ */
void toFront();
+
+ /**
+ * Makes this window the bottommost window on the desktop.
+ *
+ * @see Window#toBack()
+ */
void toBack();
+
+ /**
+ * Sets if the window should always stay on top of all other windows or
+ * not.
+ *
+ * @param alwaysOnTop if the window should always stay on top of all other
+ * windows or not
+ *
+ * @see Window#setAlwaysOnTop(boolean)
+ */
void setAlwaysOnTop(boolean alwaysOnTop);
+
+ /**
+ * Updates the window's focusable state.
+ *
+ * @see Window#setFocusableWindowState(boolean)
+ */
void updateFocusableWindowState();
- boolean requestWindowFocus();
+
+ /**
+ * Sets if this window is blocked by a modal dialog or not.
+ *
+ * @param blocker the blocking modal dialog
+ * @param blocked {@code true} to block the window, {@code false}
+ * to unblock it
+ */
void setModalBlocked(Dialog blocker, boolean blocked);
+
+ /**
+ * Updates the minimum size on the peer.
+ *
+ * @see Window#setMinimumSize(Dimension)
+ */
void updateMinimumSize();
+
+ /**
+ * Updates the icons for the window.
+ *
+ * @see Window#setIconImages(java.util.List)
+ */
void updateIconImages();
}
diff --git a/src/share/classes/java/beans/EventHandler.java b/src/share/classes/java/beans/EventHandler.java
index 2cc005c79..55144ca27 100644
--- a/src/share/classes/java/beans/EventHandler.java
+++ b/src/share/classes/java/beans/EventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -385,16 +385,16 @@ public class EventHandler implements InvocationHandler {
try {
Method getter = null;
if (target != null) {
- getter = ReflectionUtils.getMethod(target.getClass(),
+ getter = Statement.getMethod(target.getClass(),
"get" + NameGenerator.capitalize(first),
new Class[]{});
if (getter == null) {
- getter = ReflectionUtils.getMethod(target.getClass(),
+ getter = Statement.getMethod(target.getClass(),
"is" + NameGenerator.capitalize(first),
new Class[]{});
}
if (getter == null) {
- getter = ReflectionUtils.getMethod(target.getClass(), first, new Class[]{});
+ getter = Statement.getMethod(target.getClass(), first, new Class[]{});
}
}
if (getter == null) {
@@ -462,10 +462,10 @@ public class EventHandler implements InvocationHandler {
target = applyGetters(target, action.substring(0, lastDot));
action = action.substring(lastDot + 1);
}
- Method targetMethod = ReflectionUtils.getMethod(
+ Method targetMethod = Statement.getMethod(
target.getClass(), action, argTypes);
if (targetMethod == null) {
- targetMethod = ReflectionUtils.getMethod(target.getClass(),
+ targetMethod = Statement.getMethod(target.getClass(),
"set" + NameGenerator.capitalize(action), argTypes);
}
if (targetMethod == null) {
diff --git a/src/share/classes/java/beans/MetaData.java b/src/share/classes/java/beans/MetaData.java
index 5d8fd6979..defd19645 100644
--- a/src/share/classes/java/beans/MetaData.java
+++ b/src/share/classes/java/beans/MetaData.java
@@ -24,6 +24,8 @@
*/
package java.beans;
+import com.sun.beans.finder.PrimitiveWrapperMap;
+
import java.awt.AWTKeyStroke;
import java.awt.BorderLayout;
import java.awt.Dimension;
@@ -204,7 +206,7 @@ class java_lang_Class_PersistenceDelegate extends PersistenceDelegate {
if (c.isPrimitive()) {
Field field = null;
try {
- field = ReflectionUtils.typeToClass(c).getDeclaredField("TYPE");
+ field = PrimitiveWrapperMap.getType(c.getName()).getDeclaredField("TYPE");
} catch (NoSuchFieldException ex) {
System.err.println("Unknown primitive type: " + c);
}
diff --git a/src/share/classes/java/beans/ReflectionUtils.java b/src/share/classes/java/beans/ReflectionUtils.java
index 167b02995..3c3e869aa 100644
--- a/src/share/classes/java/beans/ReflectionUtils.java
+++ b/src/share/classes/java/beans/ReflectionUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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
@@ -24,20 +24,7 @@
*/
package java.beans;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-
-import java.util.*;
-
-import com.sun.beans.ObjectHandler;
-import sun.reflect.misc.MethodUtil;
-import sun.reflect.misc.ConstructorUtil;
-import sun.reflect.misc.ReflectUtil;
/**
* A utility class for reflectively finding methods, constuctors and fields
@@ -45,12 +32,6 @@ import sun.reflect.misc.ReflectUtil;
*/
class ReflectionUtils {
- private static Reference methodCacheRef;
-
- public static Class typeToClass(Class type) {
- return type.isPrimitive() ? ObjectHandler.typeNameToClass(type.getName()) : type;
- }
-
public static boolean isPrimitive(Class type) {
return primitiveTypeFor(type) != null;
}
@@ -69,346 +50,6 @@ class ReflectionUtils {
}
/**
- * Tests each element on the class arrays for assignability.
- *
- * @param argClasses arguments to be tested
- * @param argTypes arguments from Method
- * @return true if each class in argTypes is assignable from the
- * corresponding class in argClasses.
- */
- private static boolean matchArguments(Class[] argClasses, Class[] argTypes) {
- return matchArguments(argClasses, argTypes, false);
- }
-
- /**
- * Tests each element on the class arrays for equality.
- *
- * @param argClasses arguments to be tested
- * @param argTypes arguments from Method
- * @return true if each class in argTypes is equal to the
- * corresponding class in argClasses.
- */
- private static boolean matchExplicitArguments(Class[] argClasses, Class[] argTypes) {
- return matchArguments(argClasses, argTypes, true);
- }
-
- private static boolean matchArguments(Class[] argClasses,
- Class[] argTypes, boolean explicit) {
-
- boolean match = (argClasses.length == argTypes.length);
- for(int j = 0; j < argClasses.length && match; j++) {
- Class argType = argTypes[j];
- if (argType.isPrimitive()) {
- argType = typeToClass(argType);
- }
- if (explicit) {
- // Test each element for equality
- if (argClasses[j] != argType) {
- match = false;
- }
- } else {
- // Consider null an instance of all classes.
- if (argClasses[j] != null &&
- !(argType.isAssignableFrom(argClasses[j]))) {
- match = false;
- }
- }
- }
- return match;
- }
-
- /**
- * @return the method which best matches the signature or throw an exception
- * if it can't be found or the method is ambiguous.
- */
- static Method getPublicMethod(Class declaringClass, String methodName,
- Class[] argClasses) throws NoSuchMethodException {
- Method m;
-
- m = findPublicMethod(declaringClass, methodName, argClasses);
- if (m == null)
- throw new NoSuchMethodException(declaringClass.getName() + "." + methodName);
- return m;
- }
-
- /**
- * @return the method which best matches the signature or null if it cant be found or
- * the method is ambiguous.
- */
- public static Method findPublicMethod(Class declaringClass, String methodName,
- Class[] argClasses) {
- // Many methods are "getters" which take no arguments.
- // This permits the following optimisation which
- // avoids the expensive call to getMethods().
- if (argClasses.length == 0) {
- try {
- return MethodUtil.getMethod(declaringClass, methodName, argClasses);
- }
- catch (NoSuchMethodException e) {
- return null;
- } catch (SecurityException se) {
- // fall through
- }
- }
- Method[] methods = MethodUtil.getPublicMethods(declaringClass);
- List list = new ArrayList();
- for(int i = 0; i < methods.length; i++) {
- // Collect all the methods which match the signature.
- Method method = methods[i];
- if (method.getName().equals(methodName)) {
- if (matchArguments(argClasses, method.getParameterTypes())) {
- list.add(method);
- }
- }
- }
- if (list.size() > 0) {
- if (list.size() == 1) {
- return (Method)list.get(0);
- }
- else {
- ListIterator iterator = list.listIterator();
- Method method;
- while (iterator.hasNext()) {
- method = (Method)iterator.next();
- if (matchExplicitArguments(argClasses, method.getParameterTypes())) {
- return method;
- }
- }
- // There are more than one method which matches this signature.
- // try to return the most specific method.
- return getMostSpecificMethod(list, argClasses);
- }
- }
- return null;
- }
-
- /**
- * Return the most specific method from the list of methods which
- * matches the args. The most specific method will have the most
- * number of equal parameters or will be closest in the inheritance
- * heirarchy to the runtime execution arguments.
- * <p>
- * See the JLS section 15.12
- * http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#20448
- *
- * @param methods List of methods which already have the same param length
- * and arg types are assignable to param types
- * @param args an array of param types to match
- * @return method or null if a specific method cannot be determined
- */
- private static Method getMostSpecificMethod(List methods, Class[] args) {
- Method method = null;
-
- int matches = 0;
- int lastMatch = matches;
-
- ListIterator iterator = methods.listIterator();
- while (iterator.hasNext()) {
- Method m = (Method)iterator.next();
- Class[] mArgs = m.getParameterTypes();
- matches = 0;
- for (int i = 0; i < args.length; i++) {
- Class mArg = mArgs[i];
- if (mArg.isPrimitive()) {
- mArg = typeToClass(mArg);
- }
- if (args[i] == mArg) {
- matches++;
- }
- }
- if (matches == 0 && lastMatch == 0) {
- if (method == null) {
- method = m;
- } else {
- // Test existing method. We already know that the args can
- // be assigned to all the method params. However, if the
- // current method parameters is higher in the inheritance
- // hierarchy then replace it.
- if (!matchArguments(method.getParameterTypes(),
- m.getParameterTypes())) {
- method = m;
- }
- }
- } else if (matches > lastMatch) {
- lastMatch = matches;
- method = m;
- } else if (matches == lastMatch) {
- // ambiguous method selection.
- method = null;
- }
- }
- return method;
- }
-
- /**
- * @return the method or null if it can't be found or is ambiguous.
- */
- public static Method findMethod(Class targetClass, String methodName,
- Class[] argClasses) {
- Method m = findPublicMethod(targetClass, methodName, argClasses);
- if (m != null && Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
- return m;
- }
-
- /*
- Search the interfaces for a public version of this method.
-
- Example: the getKeymap() method of a JTextField
- returns a package private implementation of the
- of the public Keymap interface. In the Keymap
- interface there are a number of "properties" one
- being the "resolveParent" property implied by the
- getResolveParent() method. This getResolveParent()
- cannot be called reflectively because the class
- itself is not public. Instead we search the class's
- interfaces and find the getResolveParent()
- method of the Keymap interface - on which invoke
- may be applied without error.
-
- So in :-
-
- JTextField o = new JTextField("Hello, world");
- Keymap km = o.getKeymap();
- Method m1 = km.getClass().getMethod("getResolveParent", new Class[0]);
- Method m2 = Keymap.class.getMethod("getResolveParent", new Class[0]);
-
- Methods m1 and m2 are different. The invocation of method
- m1 unconditionally throws an IllegalAccessException where
- the invocation of m2 will invoke the implementation of the
- method. Note that (ignoring the overloading of arguments)
- there is only one implementation of the named method which
- may be applied to this target.
- */
- for(Class type = targetClass; type != null; type = type.getSuperclass()) {
- Class[] interfaces = type.getInterfaces();
- for(int i = 0; i < interfaces.length; i++) {
- m = findPublicMethod(interfaces[i], methodName, argClasses);
- if (m != null) {
- return m;
- }
- }
- }
- return null;
- }
-
- /**
- * A class that represents the unique elements of a method that will be a
- * key in the method cache.
- */
- private static class Signature {
- private Class targetClass;
- private String methodName;
- private Class[] argClasses;
-
- private volatile int hashCode = 0;
-
- public Signature(Class targetClass, String methodName, Class[] argClasses) {
- this.targetClass = targetClass;
- this.methodName = methodName;
- this.argClasses = argClasses;
- }
-
- public boolean equals(Object o2) {
- if (this == o2) {
- return true;
- }
- Signature that = (Signature)o2;
- if (!(targetClass == that.targetClass)) {
- return false;
- }
- if (!(methodName.equals(that.methodName))) {
- return false;
- }
- if (argClasses.length != that.argClasses.length) {
- return false;
- }
- for (int i = 0; i < argClasses.length; i++) {
- if (!(argClasses[i] == that.argClasses[i])) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Hash code computed using algorithm suggested in
- * Effective Java, Item 8.
- */
- public int hashCode() {
- if (hashCode == 0) {
- int result = 17;
- result = 37 * result + targetClass.hashCode();
- result = 37 * result + methodName.hashCode();
- if (argClasses != null) {
- for (int i = 0; i < argClasses.length; i++) {
- result = 37 * result + ((argClasses[i] == null) ? 0 :
- argClasses[i].hashCode());
- }
- }
- hashCode = result;
- }
- return hashCode;
- }
- }
-
- /**
- * A wrapper to findMethod(), which will search or populate the method
- * in a cache.
- * @throws exception if the method is ambiguios.
- */
- public static synchronized Method getMethod(Class targetClass,
- String methodName,
- Class[] argClasses) {
- Object signature = new Signature(targetClass, methodName, argClasses);
-
- Method method = null;
- Map methodCache = null;
- boolean cache = false;
- if (ReflectUtil.isPackageAccessible(targetClass)) {
- cache = true;
- }
-
- if (cache && methodCacheRef != null &&
- (methodCache = (Map)methodCacheRef.get()) != null) {
- method = (Method)methodCache.get(signature);
- if (method != null) {
- return method;
- }
- }
- method = findMethod(targetClass, methodName, argClasses);
- if (cache && method != null) {
- if (methodCache == null) {
- methodCache = new HashMap();
- methodCacheRef = new SoftReference(methodCache);
- }
- methodCache.put(signature, method);
- }
- return method;
- }
-
- /**
- * Return a constructor on the class with the arguments.
- *
- * @throws exception if the method is ambiguios.
- */
- public static Constructor getConstructor(Class cls, Class[] args) {
- Constructor constructor = null;
-
- // PENDING: Implement the resolutuion of ambiguities properly.
- Constructor[] ctors = ConstructorUtil.getConstructors(cls);
- for(int i = 0; i < ctors.length; i++) {
- if (matchArguments(args, ctors[i].getParameterTypes())) {
- constructor = ctors[i];
- }
- }
- return constructor;
- }
-
- public static Object getPrivateField(Object instance, Class cls, String name) {
- return getPrivateField(instance, cls, name, null);
- }
-
- /**
* Returns the value of a private field.
*
* @param instance object instance
diff --git a/src/share/classes/java/beans/Statement.java b/src/share/classes/java/beans/Statement.java
index 25d7e3307..7bf2fccda 100644
--- a/src/share/classes/java/beans/Statement.java
+++ b/src/share/classes/java/beans/Statement.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -31,6 +31,8 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.sun.beans.finder.ClassFinder;
+import com.sun.beans.finder.ConstructorFinder;
+import com.sun.beans.finder.MethodFinder;
import sun.reflect.misc.MethodUtil;
/**
@@ -195,13 +197,18 @@ public class Statement {
argClasses[0] == String.class) {
return new Character(((String)arguments[0]).charAt(0));
}
- m = ReflectionUtils.getConstructor((Class)target, argClasses);
+ try {
+ m = ConstructorFinder.findConstructor((Class)target, argClasses);
+ }
+ catch (NoSuchMethodException exception) {
+ m = null;
+ }
}
if (m == null && target != Class.class) {
- m = ReflectionUtils.getMethod((Class)target, methodName, argClasses);
+ m = getMethod((Class)target, methodName, argClasses);
}
if (m == null) {
- m = ReflectionUtils.getMethod(Class.class, methodName, argClasses);
+ m = getMethod(Class.class, methodName, argClasses);
}
}
else {
@@ -224,7 +231,7 @@ public class Statement {
return null;
}
}
- m = ReflectionUtils.getMethod(target.getClass(), methodName, argClasses);
+ m = getMethod(target.getClass(), methodName, argClasses);
}
if (m != null) {
try {
@@ -289,4 +296,13 @@ public class Statement {
result.append(");");
return result.toString();
}
+
+ static Method getMethod(Class<?> type, String name, Class<?>... args) {
+ try {
+ return MethodFinder.findMethod(type, name, args);
+ }
+ catch (NoSuchMethodException exception) {
+ return null;
+ }
+ }
}
diff --git a/src/share/classes/java/beans/XMLDecoder.java b/src/share/classes/java/beans/XMLDecoder.java
index 688edfae4..5e0327d65 100644
--- a/src/share/classes/java/beans/XMLDecoder.java
+++ b/src/share/classes/java/beans/XMLDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2008 Sun Microsystems, Inc. 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
@@ -24,19 +24,14 @@
*/
package java.beans;
-import com.sun.beans.ObjectHandler;
+import com.sun.beans.decoder.DocumentHandler;
+import java.io.Closeable;
import java.io.InputStream;
import java.io.IOException;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
/**
* The <code>XMLDecoder</code> class is used to read XML documents
@@ -66,11 +61,11 @@ import javax.xml.parsers.SAXParser;
* @author Philip Milne
*/
public class XMLDecoder {
- private InputStream in;
+ private final DocumentHandler handler = new DocumentHandler();
+ private final InputSource input;
private Object owner;
- private ExceptionListener exceptionListener;
- private ObjectHandler handler;
- private Reference clref;
+ private Object[] array;
+ private int index;
/**
* Creates a new input stream for reading archives
@@ -126,36 +121,45 @@ public class XMLDecoder {
*/
public XMLDecoder(InputStream in, Object owner,
ExceptionListener exceptionListener, ClassLoader cl) {
- this.in = in;
- setOwner(owner);
- setExceptionListener(exceptionListener);
- setClassLoader(cl);
+ this(new InputSource(in), owner, exceptionListener, cl);
}
/**
- * Set the class loader used to instantiate objects for this stream.
+ * Creates a new decoder to parse XML archives
+ * created by the {@code XMLEncoder} class.
+ * If the input source {@code is} is {@code null},
+ * no exception is thrown and no parsing is performed.
+ * This behavior is similar to behavior of other constructors
+ * that use {@code InputStream} as a parameter.
+ *
+ * @param is the input source to parse
*
- * @param cl a classloader to use; if null then the default class loader
- * will be used
+ * @since 1.7
*/
- private void setClassLoader(ClassLoader cl) {
- if (cl != null) {
- this.clref = new WeakReference(cl);
- }
+ public XMLDecoder(InputSource is) {
+ this(is, null, null, null);
}
/**
- * Return the class loader used to instantiate objects. If the class loader
- * has not been explicitly set then null is returned.
+ * Creates a new decoder to parse XML archives
+ * created by the {@code XMLEncoder} class.
+ *
+ * @param is the input source to parse
+ * @param owner the owner of this decoder
+ * @param el the exception handler for the parser,
+ * or {@code null} to use the default exception handler
+ * @param cl the class loader used for instantiating objects,
+ * or {@code null} to use the default class loader
*
- * @return the class loader used to instantiate objects
+ * @since 1.7
*/
- private ClassLoader getClassLoader() {
- if (clref != null) {
- return (ClassLoader)clref.get();
- }
- return null;
+ private XMLDecoder(InputSource is, Object owner, ExceptionListener el, ClassLoader cl) {
+ this.input = is;
+ this.owner = owner;
+ setExceptionListener(el);
+ this.handler.setClassLoader(cl);
+ this.handler.setOwner(this);
}
/**
@@ -163,8 +167,14 @@ public class XMLDecoder {
* with this stream.
*/
public void close() {
+ if (parsingComplete()) {
+ close(this.input.getCharacterStream());
+ close(this.input.getByteStream());
+ }
+ }
+
+ private void close(Closeable in) {
if (in != null) {
- getHandler();
try {
in.close();
}
@@ -174,6 +184,17 @@ public class XMLDecoder {
}
}
+ private boolean parsingComplete() {
+ if (this.input == null) {
+ return false;
+ }
+ if (this.array == null) {
+ this.handler.parse(this.input);
+ this.array = this.handler.getObjects();
+ }
+ return true;
+ }
+
/**
* Sets the exception handler for this stream to <code>exceptionListener</code>.
* The exception handler is notified when this stream catches recoverable
@@ -185,7 +206,10 @@ public class XMLDecoder {
* @see #getExceptionListener
*/
public void setExceptionListener(ExceptionListener exceptionListener) {
- this.exceptionListener = exceptionListener;
+ if (exceptionListener == null) {
+ exceptionListener = Statement.defaultExceptionListener;
+ }
+ this.handler.setExceptionListener(exceptionListener);
}
/**
@@ -197,8 +221,7 @@ public class XMLDecoder {
* @see #setExceptionListener
*/
public ExceptionListener getExceptionListener() {
- return (exceptionListener != null) ? exceptionListener :
- Statement.defaultExceptionListener;
+ return this.handler.getExceptionListener();
}
/**
@@ -212,10 +235,9 @@ public class XMLDecoder {
* @see XMLEncoder#writeObject
*/
public Object readObject() {
- if (in == null) {
- return null;
- }
- return getHandler().dequeueResult();
+ return (parsingComplete())
+ ? this.array[this.index++]
+ : null;
}
/**
@@ -241,33 +263,32 @@ public class XMLDecoder {
}
/**
- * Returns the object handler for input stream.
- * The object handler is created if necessary.
+ * Creates a new handler for SAX parser
+ * that can be used to parse embedded XML archives
+ * created by the {@code XMLEncoder} class.
+ *
+ * The {@code owner} should be used if parsed XML document contains
+ * the method call within context of the &lt;java&gt; element.
+ * The {@code null} value may cause illegal parsing in such case.
+ * The same problem may occur, if the {@code owner} class
+ * does not contain expected method to call. See details <a
+ * href="http://java.sun.com/products/jfc/tsc/articles/persistence3/">here</a>.
+ *
+ * @param owner the owner of the default handler
+ * that can be used as a value of &lt;java&gt; element
+ * @param el the exception handler for the parser,
+ * or {@code null} to use the default exception handler
+ * @param cl the class loader used for instantiating objects,
+ * or {@code null} to use the default class loader
+ * @return an instance of {@code DefaultHandler} for SAX parser
*
- * @return the object handler
+ * @since 1.7
*/
- private ObjectHandler getHandler() {
- if ( handler == null ) {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- try {
- SAXParser parser = factory.newSAXParser();
- handler = new ObjectHandler( this, getClassLoader() );
- parser.parse( in, handler );
- }
- catch ( ParserConfigurationException e ) {
- getExceptionListener().exceptionThrown( e );
- }
- catch ( SAXException se ) {
- Exception e = se.getException();
- if ( e == null ) {
- e = se;
- }
- getExceptionListener().exceptionThrown( e );
- }
- catch ( IOException ioe ) {
- getExceptionListener().exceptionThrown( ioe );
- }
- }
+ public static DefaultHandler createHandler(Object owner, ExceptionListener el, ClassLoader cl) {
+ DocumentHandler handler = new DocumentHandler();
+ handler.setOwner(owner);
+ handler.setExceptionListener(el);
+ handler.setClassLoader(cl);
return handler;
}
}
diff --git a/src/share/classes/java/io/File.java b/src/share/classes/java/io/File.java
index d573b89f4..6a0415101 100644
--- a/src/share/classes/java/io/File.java
+++ b/src/share/classes/java/io/File.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. 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
@@ -30,12 +30,12 @@ import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Hashtable;
-import java.util.Random;
+import java.util.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
import java.security.AccessController;
-import java.security.AccessControlException;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
import sun.security.action.GetPropertyAction;
@@ -131,6 +131,18 @@ import sun.security.action.GetPropertyAction;
* created, the abstract pathname represented by a <code>File</code> object
* will never change.
*
+ * <h4>Interoperability with {@code java.nio.file} package</h4>
+ *
+ * <p> The <a href="../../java/nio/file/package-summary.html">{@code java.nio.file}</a>
+ * package defines interfaces and classes for the Java virtual machine to access
+ * files, file attributes, and file systems. This API may be used to overcome
+ * many of the limitations of the {@code java.io.File} class.
+ * The {@link #toPath toPath} method may be used to obtain a {@link
+ * Path} that uses the abstract path represented by a {@code File} object to
+ * locate a file. The resulting {@code Path} provides more efficient and
+ * extensive access to file attributes, additional file operations, and I/O
+ * exceptions to help diagnose errors when an operation on a file fails.
+ *
* @author unascribed
* @since JDK1.0
*/
@@ -573,6 +585,7 @@ public class File
* read access to the file
*
* @since JDK1.1
+ * @see Path#toRealPath
*/
public String getCanonicalPath() throws IOException {
return fs.canonicalize(fs.resolve(this));
@@ -597,6 +610,7 @@ public class File
* read access to the file
*
* @since 1.2
+ * @see Path#toRealPath
*/
public File getCanonicalFile() throws IOException {
String canonPath = getCanonicalPath();
@@ -663,6 +677,14 @@ public class File
* system is converted into an abstract pathname in a virtual machine on a
* different operating system.
*
+ * <p> Note that when this abstract pathname represents a UNC pathname then
+ * all components of the UNC (including the server name component) are encoded
+ * in the {@code URI} path. The authority component is undefined, meaning
+ * that it is represented as {@code null}. The {@link Path} class defines the
+ * {@link Path#toUri toUri} method to encode the server name in the authority
+ * component of the resulting {@code URI}. The {@link #toPath toPath} method
+ * may be used to obtain a {@code Path} representing this abstract pathname.
+ *
* @return An absolute, hierarchical URI with a scheme equal to
* <tt>"file"</tt>, a path representing this abstract pathname,
* and undefined authority, query, and fragment components
@@ -764,6 +786,8 @@ public class File
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the file
+ *
+ * @see Attributes#readBasicFileAttributes
*/
public boolean isDirectory() {
SecurityManager security = System.getSecurityManager();
@@ -788,6 +812,8 @@ public class File
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the file
+ *
+ * @see Attributes#readBasicFileAttributes
*/
public boolean isFile() {
SecurityManager security = System.getSecurityManager();
@@ -836,6 +862,8 @@ public class File
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the file
+ *
+ * @see Attributes#readBasicFileAttributes
*/
public long lastModified() {
SecurityManager security = System.getSecurityManager();
@@ -858,6 +886,8 @@ public class File
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the file
+ *
+ * @see Attributes#readBasicFileAttributes
*/
public long length() {
SecurityManager security = System.getSecurityManager();
@@ -907,6 +937,12 @@ public class File
* this pathname denotes a directory, then the directory must be empty in
* order to be deleted.
*
+ * <p> Note that the {@link Path} class defines the {@link Path#delete
+ * delete} method to throw an {@link IOException} when a file cannot be
+ * deleted. This is useful for error reporting and to diagnose why a file
+ * cannot be deleted. The {@link #toPath toPath} method may be used to
+ * obtain a {@code Path} representing this abstract pathname.
+ *
* @return <code>true</code> if and only if the file or directory is
* successfully deleted; <code>false</code> otherwise
*
@@ -973,6 +1009,13 @@ public class File
* will appear in any specific order; they are not, in particular,
* guaranteed to appear in alphabetical order.
*
+ * <p> Note that the {@link Path} class defines the {@link
+ * Path#newDirectoryStream newDirectoryStream} method to open a directory
+ * and iterate over the names of the files in the directory. This may use
+ * less resources when working with very large directories. The {@link
+ * #toPath toPath} method may be used to obtain a {@code Path} representing
+ * this abstract pathname.
+ *
* @return An array of strings naming the files and directories in the
* directory denoted by this abstract pathname. The array will be
* empty if the directory is empty. Returns {@code null} if
@@ -1024,13 +1067,13 @@ public class File
if ((names == null) || (filter == null)) {
return names;
}
- ArrayList v = new ArrayList();
+ List<String> v = new ArrayList<String>();
for (int i = 0 ; i < names.length ; i++) {
if (filter.accept(this, names[i])) {
v.add(names[i]);
}
}
- return (String[])(v.toArray(new String[v.size()]));
+ return v.toArray(new String[v.size()]);
}
/**
@@ -1052,6 +1095,13 @@ public class File
* will appear in any specific order; they are not, in particular,
* guaranteed to appear in alphabetical order.
*
+ * <p> Note that the {@link Path} class defines the {@link
+ * Path#newDirectoryStream newDirectoryStream} method to open a directory
+ * and iterate over the names of the files in the directory. This may use
+ * less resources when working with very large directories. The {@link
+ * #toPath toPath} method may be used to obtain a {@code Path} representing
+ * this abstract pathname.
+ *
* @return An array of abstract pathnames denoting the files and
* directories in the directory denoted by this abstract pathname.
* The array will be empty if the directory is empty. Returns
@@ -1157,6 +1207,12 @@ public class File
/**
* Creates the directory named by this abstract pathname.
*
+ * <p> Note that the {@link Path} class defines the {@link Path#createDirectory
+ * createDirectory} method to throw an {@link IOException} when a directory
+ * cannot be created. This is useful for error reporting and to diagnose why
+ * a directory cannot be created. The {@link #toPath toPath} method may be
+ * used to obtain a {@code Path} representing this abstract pathname.
+ *
* @return <code>true</code> if and only if the directory was
* created; <code>false</code> otherwise
*
@@ -1222,6 +1278,11 @@ public class File
* already exists. The return value should always be checked to make sure
* that the rename operation was successful.
*
+ * <p> Note that the {@link Path} class defines the {@link Path#moveTo
+ * moveTo} method to move or rename a file in a platform independent manner.
+ * The {@link #toPath toPath} method may be used to obtain a {@code Path}
+ * representing this abstract pathname.
+ *
* @param dest The new abstract pathname for the named file
*
* @return <code>true</code> if and only if the renaming succeeded;
@@ -1304,10 +1365,14 @@ public class File
return fs.setReadOnly(this);
}
- /**
+ /**
* Sets the owner's or everybody's write permission for this abstract
* pathname.
*
+ * <p> The {@link Attributes Attributes} class defines methods that operate
+ * on file attributes including file permissions. This may be used when
+ * finer manipulation of file permissions is required.
+ *
* @param writable
* If <code>true</code>, sets the access permission to allow write
* operations; if <code>false</code> to disallow write operations
@@ -1371,6 +1436,10 @@ public class File
* Sets the owner's or everybody's read permission for this abstract
* pathname.
*
+ * <p> The {@link Attributes Attributes} class defines methods that operate
+ * on file attributes including file permissions. This may be used when
+ * finer manipulation of file permissions is required.
+ *
* @param readable
* If <code>true</code>, sets the access permission to allow read
* operations; if <code>false</code> to disallow read operations
@@ -1440,6 +1509,10 @@ public class File
* Sets the owner's or everybody's execute permission for this abstract
* pathname.
*
+ * <p> The {@link Attributes Attributes} class defines methods that operate
+ * on file attributes including file permissions. This may be used when
+ * finer manipulation of file permissions is required.
+ *
* @param executable
* If <code>true</code>, sets the access permission to allow execute
* operations; if <code>false</code> to disallow execute operations
@@ -1678,44 +1751,44 @@ public class File
/* -- Temporary files -- */
- private static final Object tmpFileLock = new Object();
+ private static class TemporaryDirectory {
+ private TemporaryDirectory() { }
- private static int counter = -1; /* Protected by tmpFileLock */
+ static final String valueAsString = fs.normalize(
+ AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir")));
+ static final File valueAsFile =
+ new File(valueAsString, fs.prefixLength(valueAsString));
- private static File generateFile(String prefix, String suffix, File dir)
- throws IOException
- {
- if (counter == -1) {
- counter = new Random().nextInt() & 0xffff;
+ // file name generation
+ private static final SecureRandom random = new SecureRandom();
+ static File generateFile(String prefix, String suffix, File dir) {
+ long n = random.nextLong();
+ if (n == Long.MIN_VALUE) {
+ n = 0; // corner case
+ } else {
+ n = Math.abs(n);
+ }
+ return new File(dir, prefix + Long.toString(n) + suffix);
}
- counter++;
- return new File(dir, prefix + Integer.toString(counter) + suffix);
- }
- private static String tmpdir; /* Protected by tmpFileLock */
-
- private static String getTempDir() {
- if (tmpdir == null)
- tmpdir = fs.normalize(
- AccessController.doPrivileged(
- new GetPropertyAction("java.io.tmpdir")));
- return tmpdir;
- }
-
- private static boolean checkAndCreate(String filename, SecurityManager sm)
- throws IOException
- {
- if (sm != null) {
- try {
- sm.checkWrite(filename);
- } catch (AccessControlException x) {
- /* Throwing the original AccessControlException could disclose
- the location of the default temporary directory, so we
- re-throw a more innocuous SecurityException */
- throw new SecurityException("Unable to create temporary file");
- }
+ // default file permissions
+ static final FileAttribute<Set<PosixFilePermission>> defaultPosixFilePermissions =
+ PosixFilePermissions.asFileAttribute(EnumSet
+ .of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE));
+ static final boolean isPosix = isPosix();
+ static boolean isPosix() {
+ return AccessController.doPrivileged(
+ new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ try {
+ return FileSystems.getDefault().getPath(valueAsString)
+ .getFileStore().supportsFileAttributeView("posix");
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+ });
}
- return fs.createFileExclusively(filename);
}
/**
@@ -1791,22 +1864,29 @@ public class File
File directory)
throws IOException
{
- if (prefix == null) throw new NullPointerException();
if (prefix.length() < 3)
throw new IllegalArgumentException("Prefix string too short");
- String s = (suffix == null) ? ".tmp" : suffix;
- synchronized (tmpFileLock) {
- if (directory == null) {
- String tmpDir = getTempDir();
- directory = new File(tmpDir, fs.prefixLength(tmpDir));
+ if (suffix == null)
+ suffix = ".tmp";
+
+ File tmpdir = (directory != null) ?
+ directory : TemporaryDirectory.valueAsFile;
+ SecurityManager sm = System.getSecurityManager();
+ File f;
+ do {
+ f = TemporaryDirectory.generateFile(prefix, suffix, tmpdir);
+ if (sm != null) {
+ try {
+ sm.checkWrite(f.getPath());
+ } catch (SecurityException se) {
+ // don't reveal temporary directory location
+ if (directory == null)
+ throw new SecurityException("Unable to create temporary file");
+ throw se;
+ }
}
- SecurityManager sm = System.getSecurityManager();
- File f;
- do {
- f = generateFile(prefix, s, directory);
- } while (!checkAndCreate(f.getPath(), sm));
- return f;
- }
+ } while (!fs.createFileExclusively(f.getPath()));
+ return f;
}
/**
@@ -1844,6 +1924,122 @@ public class File
return createTempFile(prefix, suffix, null);
}
+ /**
+ * Creates an empty file in the default temporary-file directory, using
+ * the given prefix and suffix to generate its name. This method is
+ * equivalent to invoking the {@link #createTempFile(String,String)
+ * createTempFile(prefix,&nbsp;suffix)} method with the addition that the
+ * resulting pathname may be requested to be deleted when the Java virtual
+ * machine terminates, and the initial file attributes to set atomically
+ * when creating the file may be specified.
+ *
+ * <p> When the value of the {@code deleteOnExit} method is {@code true}
+ * then the resulting file is requested to be deleted when the Java virtual
+ * machine terminates as if by invoking the {@link #deleteOnExit deleteOnExit}
+ * method.
+ *
+ * <p> The {@code attrs} parameter is an optional array of {@link FileAttribute
+ * attributes} to set atomically when creating the file. Each attribute is
+ * identified by its {@link FileAttribute#name name}. If more than one attribute
+ * of the same name is included in the array then all but the last occurrence
+ * is ignored.
+ *
+ * @param prefix
+ * The prefix string to be used in generating the file's
+ * name; must be at least three characters long
+ * @param suffix
+ * The suffix string to be used in generating the file's
+ * name; may be {@code null}, in which case the suffix
+ * {@code ".tmp"} will be used
+ * @param deleteOnExit
+ * {@code true} if the file denoted by resulting pathname be
+ * deleted when the Java virtual machine terminates
+ * @param attrs
+ * An optional list of file attributes to set atomically when creating
+ * the file
+ *
+ * @return An abstract pathname denoting a newly-created empty file
+ *
+ * @throws IllegalArgumentException
+ * If the <code>prefix</code> argument contains fewer than three
+ * characters
+ * @throws UnsupportedOperationException
+ * If the array contains an attribute that cannot be set atomically
+ * when creating the file
+ * @throws IOException
+ * If a file could not be created
+ * @throws SecurityException
+ * If a security manager exists and its <code>{@link
+ * java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
+ * method does not allow a file to be created. When the {@code
+ * deleteOnExit} parameter has the value {@code true} then the
+ * security manager's {@link
+ * java.lang.SecurityManager#checkDelete(java.lang.String)} is
+ * invoked to check delete access to the file.
+ * @since 1.7
+ */
+ public static File createTempFile(String prefix,
+ String suffix,
+ boolean deleteOnExit,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ if (prefix.length() < 3)
+ throw new IllegalArgumentException("Prefix string too short");
+ suffix = (suffix == null) ? ".tmp" : suffix;
+
+ // special case POSIX environments so that 0600 is used as the file mode
+ if (TemporaryDirectory.isPosix) {
+ if (attrs.length == 0) {
+ // no attributes so use default permissions
+ attrs = new FileAttribute<?>[1];
+ attrs[0] = TemporaryDirectory.defaultPosixFilePermissions;
+ } else {
+ // check if posix permissions given; if not use default
+ boolean hasPermissions = false;
+ for (int i=0; i<attrs.length; i++) {
+ if (attrs[i].name().equals("posix:permissions")) {
+ hasPermissions = true;
+ break;
+ }
+ }
+ if (!hasPermissions) {
+ FileAttribute<?>[] copy = new FileAttribute<?>[attrs.length+1];
+ System.arraycopy(attrs, 0, copy, 0, attrs.length);
+ attrs = copy;
+ attrs[attrs.length-1] =
+ TemporaryDirectory.defaultPosixFilePermissions;
+ }
+ }
+ }
+
+ // use Path#createFile to create file
+ SecurityManager sm = System.getSecurityManager();
+ for (;;) {
+ File f = TemporaryDirectory
+ .generateFile(prefix, suffix, TemporaryDirectory.valueAsFile);
+ if (sm != null && deleteOnExit)
+ sm.checkDelete(f.getPath());
+ try {
+ f.toPath().createFile(attrs);
+ if (deleteOnExit)
+ DeleteOnExitHook.add(f.getPath());
+ return f;
+ } catch (InvalidPathException e) {
+ // don't reveal temporary directory location
+ if (sm != null)
+ throw new IllegalArgumentException("Invalid prefix or suffix");
+ throw e;
+ } catch (SecurityException e) {
+ // don't reveal temporary directory location
+ if (sm != null)
+ throw new SecurityException("Unable to create temporary file");
+ throw e;
+ } catch (FileAlreadyExistsException e) {
+ // ignore
+ }
+ }
+ }
/* -- Basic infrastructure -- */
@@ -1963,5 +2159,46 @@ public class File
);
}
+ // -- Integration with java.nio.file --
+
+ private volatile transient Path filePath;
+ /**
+ * Returns a {@link Path java.nio.file.Path} object constructed from the
+ * this abstract path. The first invocation of this method works as if
+ * invoking it were equivalent to evaluating the expression:
+ * <blockquote><pre>
+ * {@link FileSystems#getDefault FileSystems.getDefault}().{@link FileSystem#getPath getPath}(this.{@link #getPath getPath}());
+ * </pre></blockquote>
+ * Subsequent invocations of this method return the same {@code Path}.
+ *
+ * <p> If this abstract pathname is the empty abstract pathname then this
+ * method returns a {@code Path} that may be used to access to the current
+ * user directory.
+ *
+ * @return A {@code Path} constructed from this abstract path. The resulting
+ * {@code Path} is associated with the {@link FileSystems#getDefault
+ * default-filesystem}.
+ *
+ * @throws InvalidPathException
+ * If a {@code Path} object cannot be constructed from the abstract
+ * path (see {@link java.nio.file.FileSystem#getPath FileSystem.getPath})
+ *
+ * @since 1.7
+ */
+ public Path toPath() {
+ if (filePath == null) {
+ synchronized (this) {
+ if (filePath == null) {
+ if (path.length() == 0) {
+ // assume default file system treats "." as current directory
+ filePath = Paths.get(".");
+ } else {
+ filePath = Paths.get(path);
+ }
+ }
+ }
+ }
+ return filePath;
+ }
}
diff --git a/src/share/classes/java/io/FilePermission.java b/src/share/classes/java/io/FilePermission.java
index 9758e35de..88c98fbdd 100644
--- a/src/share/classes/java/io/FilePermission.java
+++ b/src/share/classes/java/io/FilePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -29,7 +29,6 @@ import java.security.*;
import java.util.Enumeration;
import java.util.List;
import java.util.ArrayList;
-import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Collections;
import java.io.ObjectStreamField;
@@ -58,7 +57,8 @@ import sun.security.util.SecurityConstants;
* <P>
* The actions to be granted are passed to the constructor in a string containing
* a list of one or more comma-separated keywords. The possible keywords are
- * "read", "write", "execute", and "delete". Their meaning is defined as follows:
+ * "read", "write", "execute", "delete", and "readlink". Their meaning is
+ * defined as follows:
* <P>
* <DL>
* <DT> read <DD> read permission
@@ -69,6 +69,11 @@ import sun.security.util.SecurityConstants;
* <DT> delete
* <DD> delete permission. Allows <code>File.delete</code> to
* be called. Corresponds to <code>SecurityManager.checkDelete</code>.
+ * <DT> readlink
+ * <DD> read link permission. Allows the target of a
+ * <a href="../nio/file/package-summary.html#links">symbolic link</a>
+ * to be read by invoking the {@link java.nio.file.Path#readSymbolicLink
+ * readSymbolicLink } method.
* </DL>
* <P>
* The actions string is converted to lowercase before processing.
@@ -114,11 +119,15 @@ public final class FilePermission extends Permission implements Serializable {
* Delete action.
*/
private final static int DELETE = 0x8;
+ /**
+ * Read link action.
+ */
+ private final static int READLINK = 0x10;
/**
- * All actions (read,write,execute,delete)
+ * All actions (read,write,execute,delete,readlink)
*/
- private final static int ALL = READ|WRITE|EXECUTE|DELETE;
+ private final static int ALL = READ|WRITE|EXECUTE|DELETE|READLINK;
/**
* No actions.
*/
@@ -235,7 +244,7 @@ public final class FilePermission extends Permission implements Serializable {
* <i>path</i> is the pathname of a file or directory, and <i>actions</i>
* contains a comma-separated list of the desired actions granted on the
* file or directory. Possible actions are
- * "read", "write", "execute", and "delete".
+ * "read", "write", "execute", "delete", and "readlink".
*
* <p>A pathname that ends in "/*" (where "/" is
* the file separator character, <code>File.separatorChar</code>)
@@ -425,6 +434,8 @@ public final class FilePermission extends Permission implements Serializable {
return EXECUTE;
} else if (actions == SecurityConstants.FILE_DELETE_ACTION) {
return DELETE;
+ } else if (actions == SecurityConstants.FILE_READLINK_ACTION) {
+ return READLINK;
}
char[] a = actions.toCharArray();
@@ -485,6 +496,18 @@ public final class FilePermission extends Permission implements Serializable {
matchlen = 6;
mask |= DELETE;
+ } else if (i >= 7 && (a[i-7] == 'r' || a[i-7] == 'R') &&
+ (a[i-6] == 'e' || a[i-6] == 'E') &&
+ (a[i-5] == 'a' || a[i-5] == 'A') &&
+ (a[i-4] == 'd' || a[i-4] == 'D') &&
+ (a[i-3] == 'l' || a[i-3] == 'L') &&
+ (a[i-2] == 'i' || a[i-2] == 'I') &&
+ (a[i-1] == 'n' || a[i-1] == 'N') &&
+ (a[i] == 'k' || a[i] == 'K'))
+ {
+ matchlen = 8;
+ mask |= READLINK;
+
} else {
// parse error
throw new IllegalArgumentException(
@@ -529,7 +552,7 @@ public final class FilePermission extends Permission implements Serializable {
/**
* Return the canonical string representation of the actions.
* Always returns present actions in the following order:
- * read, write, execute, delete.
+ * read, write, execute, delete, readlink.
*
* @return the canonical string representation of the actions.
*/
@@ -561,14 +584,20 @@ public final class FilePermission extends Permission implements Serializable {
sb.append("delete");
}
+ if ((mask & READLINK) == READLINK) {
+ if (comma) sb.append(',');
+ else comma = true;
+ sb.append("readlink");
+ }
+
return sb.toString();
}
/**
* Returns the "canonical string representation" of the actions.
* That is, this method always returns present actions in the following order:
- * read, write, execute, delete. For example, if this FilePermission object
- * allows both write and read actions, a call to <code>getActions</code>
+ * read, write, execute, delete, readlink. For example, if this FilePermission
+ * object allows both write and read actions, a call to <code>getActions</code>
* will return the string "read,write".
*
* @return the canonical string representation of the actions.
@@ -678,7 +707,7 @@ final class FilePermissionCollection extends PermissionCollection
implements Serializable {
// Not serialized; see serialization section at end of class
- private transient List perms;
+ private transient List<Permission> perms;
/**
* Create an empty FilePermissions object.
@@ -686,7 +715,7 @@ implements Serializable {
*/
public FilePermissionCollection() {
- perms = new ArrayList();
+ perms = new ArrayList<Permission>();
}
/**
@@ -791,7 +820,7 @@ implements Serializable {
// Don't call out.defaultWriteObject()
// Write out Vector
- Vector permissions = new Vector(perms.size());
+ Vector<Permission> permissions = new Vector<Permission>(perms.size());
synchronized (this) {
permissions.addAll(perms);
}
@@ -804,6 +833,7 @@ implements Serializable {
/*
* Reads in a Vector of FilePermissions and saves them in the perms field.
*/
+ @SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
// Don't call defaultReadObject()
@@ -812,8 +842,8 @@ implements Serializable {
ObjectInputStream.GetField gfields = in.readFields();
// Get the one we want
- Vector permissions = (Vector)gfields.get("permissions", null);
- perms = new ArrayList(permissions.size());
+ Vector<Permission> permissions = (Vector<Permission>)gfields.get("permissions", null);
+ perms = new ArrayList<Permission>(permissions.size());
perms.addAll(permissions);
}
}
diff --git a/src/share/classes/java/io/InputStream.java b/src/share/classes/java/io/InputStream.java
index 12da2bc93..86922fe8b 100644
--- a/src/share/classes/java/io/InputStream.java
+++ b/src/share/classes/java/io/InputStream.java
@@ -90,7 +90,7 @@ public abstract class InputStream implements Closeable {
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
- * <code>-1</code> is there is no more data because the end of
+ * <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception IOException If the first byte cannot be read for any reason
* other than the end of the file, if the input stream has been closed, or
diff --git a/src/share/classes/java/lang/Double.java b/src/share/classes/java/lang/Double.java
index 852d27dcc..9866d5d4a 100644
--- a/src/share/classes/java/lang/Double.java
+++ b/src/share/classes/java/lang/Double.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. 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
@@ -404,8 +404,19 @@ public final class Double extends Number implements Comparable<Double> {
* binary value that is then rounded to type {@code double}
* by the usual round-to-nearest rule of IEEE 754 floating-point
* arithmetic, which includes preserving the sign of a zero
- * value. Finally, a {@code Double} object representing this
- * {@code double} value is returned.
+ * value.
+ *
+ * Note that the round-to-nearest rule also implies overflow and
+ * underflow behaviour; if the exact value of {@code s} is large
+ * enough in magnitude (greater than or equal to ({@link
+ * #MAX_VALUE} + {@link Math#ulp(double) ulp(MAX_VALUE)}/2),
+ * rounding to {@code double} will result in an infinity and if the
+ * exact value of {@code s} is small enough in magnitude (less
+ * than or equal to {@link #MIN_VALUE}/2), rounding to float will
+ * result in a zero.
+ *
+ * Finally, after rounding a {@code Double} object representing
+ * this {@code double} value is returned.
*
* <p> To interpret localized string representations of a
* floating-point value, use subclasses of {@link
diff --git a/src/share/classes/java/lang/Enum.java b/src/share/classes/java/lang/Enum.java
index 175b4021f..5df8146c1 100644
--- a/src/share/classes/java/lang/Enum.java
+++ b/src/share/classes/java/lang/Enum.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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
@@ -34,6 +34,13 @@ import java.io.ObjectStreamException;
/**
* This is the common base class of all Java language enumeration types.
*
+ * More information about enums, including descriptions of the
+ * implicitly declared methods synthesized by the compiler, can be
+ * found in <i>The Java&trade; Language Specification, Third
+ * Edition</i>, <a
+ * href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9">&sect;8.9</a>.
+ *
+ * @param <E> The enum type subclass
* @author Josh Bloch
* @author Neal Gafter
* @see Class#getEnumConstants()
@@ -192,6 +199,15 @@ public abstract class Enum<E extends Enum<E>>
* to declare an enum constant in this type. (Extraneous whitespace
* characters are not permitted.)
*
+ * <p>Note that for a particular enum type {@code T}, the
+ * implicitly declared {@code public static T valueOf(String)}
+ * method on that enum may be used instead of this method to map
+ * from a name to the corresponding enum constant. All the
+ * constants of an enum type can be obtained by calling the
+ * implicit {@code public static T[] values()} method of that
+ * type.
+ *
+ * @param <T> The enum type whose constant is to be returned
* @param enumType the {@code Class} object of the enum type from which
* to return a constant
* @param name the name of the constant to return
@@ -212,7 +228,7 @@ public abstract class Enum<E extends Enum<E>>
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
- "No enum const " + enumType +"." + name);
+ "No enum constant " + enumType.getCanonicalName() + "." + name);
}
/**
@@ -225,10 +241,10 @@ public abstract class Enum<E extends Enum<E>>
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
- throw new InvalidObjectException("can't deserialize enum");
+ throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
- throw new InvalidObjectException("can't deserialize enum");
+ throw new InvalidObjectException("can't deserialize enum");
}
}
diff --git a/src/share/classes/java/lang/Float.java b/src/share/classes/java/lang/Float.java
index b8b6a1afc..1c89a458f 100644
--- a/src/share/classes/java/lang/Float.java
+++ b/src/share/classes/java/lang/Float.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. 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
@@ -365,8 +365,19 @@ public final class Float extends Number implements Comparable<Float> {
* binary value that is then rounded to type {@code float}
* by the usual round-to-nearest rule of IEEE 754 floating-point
* arithmetic, which includes preserving the sign of a zero
- * value. Finally, a {@code Float} object representing this
- * {@code float} value is returned.
+ * value.
+ *
+ * Note that the round-to-nearest rule also implies overflow and
+ * underflow behaviour; if the exact value of {@code s} is large
+ * enough in magnitude (greater than or equal to ({@link
+ * #MAX_VALUE} + {@link Math#ulp(float) ulp(MAX_VALUE)}/2),
+ * rounding to {@code float} will result in an infinity and if the
+ * exact value of {@code s} is small enough in magnitude (less
+ * than or equal to {@link #MIN_VALUE}/2), rounding to float will
+ * result in a zero.
+ *
+ * Finally, after rounding a {@code Float} object representing
+ * this {@code float} value is returned.
*
* <p>To interpret localized string representations of a
* floating-point value, use subclasses of {@link
diff --git a/src/share/classes/java/lang/Object.java b/src/share/classes/java/lang/Object.java
index d3a08de90..deb0e27a6 100644
--- a/src/share/classes/java/lang/Object.java
+++ b/src/share/classes/java/lang/Object.java
@@ -26,8 +26,8 @@
package java.lang;
/**
- * Class <code>Object</code> is the root of the class hierarchy.
- * Every class has <code>Object</code> as a superclass. All objects,
+ * Class {@code Object} is the root of the class hierarchy.
+ * Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
*
* @author unascribed
@@ -66,30 +66,30 @@ public class Object {
/**
* Returns a hash code value for the object. This method is
- * supported for the benefit of hashtables such as those provided by
- * <code>java.util.Hashtable</code>.
+ * supported for the benefit of hash tables such as those provided by
+ * {@link java.util.HashMap}.
* <p>
- * The general contract of <code>hashCode</code> is:
+ * The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
- * an execution of a Java application, the <tt>hashCode</tt> method
+ * an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
- * used in <tt>equals</tt> comparisons on the object is modified.
+ * used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
- * <li>If two objects are equal according to the <tt>equals(Object)</tt>
- * method, then calling the <code>hashCode</code> method on each of
+ * <li>If two objects are equal according to the {@code equals(Object)}
+ * method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
- * method, then calling the <tt>hashCode</tt> method on each of the
+ * method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
- * for unequal objects may improve the performance of hashtables.
+ * for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
- * class <tt>Object</tt> does return distinct integers for distinct
+ * class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
@@ -97,55 +97,55 @@ public class Object {
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
- * @see java.util.Hashtable
+ * @see java.lang.System#identityHashCode
*/
public native int hashCode();
/**
* Indicates whether some other object is "equal to" this one.
* <p>
- * The <code>equals</code> method implements an equivalence relation
+ * The {@code equals} method implements an equivalence relation
* on non-null object references:
* <ul>
* <li>It is <i>reflexive</i>: for any non-null reference value
- * <code>x</code>, <code>x.equals(x)</code> should return
- * <code>true</code>.
+ * {@code x}, {@code x.equals(x)} should return
+ * {@code true}.
* <li>It is <i>symmetric</i>: for any non-null reference values
- * <code>x</code> and <code>y</code>, <code>x.equals(y)</code>
- * should return <code>true</code> if and only if
- * <code>y.equals(x)</code> returns <code>true</code>.
+ * {@code x} and {@code y}, {@code x.equals(y)}
+ * should return {@code true} if and only if
+ * {@code y.equals(x)} returns {@code true}.
* <li>It is <i>transitive</i>: for any non-null reference values
- * <code>x</code>, <code>y</code>, and <code>z</code>, if
- * <code>x.equals(y)</code> returns <code>true</code> and
- * <code>y.equals(z)</code> returns <code>true</code>, then
- * <code>x.equals(z)</code> should return <code>true</code>.
+ * {@code x}, {@code y}, and {@code z}, if
+ * {@code x.equals(y)} returns {@code true} and
+ * {@code y.equals(z)} returns {@code true}, then
+ * {@code x.equals(z)} should return {@code true}.
* <li>It is <i>consistent</i>: for any non-null reference values
- * <code>x</code> and <code>y</code>, multiple invocations of
- * <tt>x.equals(y)</tt> consistently return <code>true</code>
- * or consistently return <code>false</code>, provided no
- * information used in <code>equals</code> comparisons on the
+ * {@code x} and {@code y}, multiple invocations of
+ * {@code x.equals(y)} consistently return {@code true}
+ * or consistently return {@code false}, provided no
+ * information used in {@code equals} comparisons on the
* objects is modified.
- * <li>For any non-null reference value <code>x</code>,
- * <code>x.equals(null)</code> should return <code>false</code>.
+ * <li>For any non-null reference value {@code x},
+ * {@code x.equals(null)} should return {@code false}.
* </ul>
* <p>
- * The <tt>equals</tt> method for class <code>Object</code> implements
+ * The {@code equals} method for class {@code Object} implements
* the most discriminating possible equivalence relation on objects;
- * that is, for any non-null reference values <code>x</code> and
- * <code>y</code>, this method returns <code>true</code> if and only
- * if <code>x</code> and <code>y</code> refer to the same object
- * (<code>x == y</code> has the value <code>true</code>).
+ * that is, for any non-null reference values {@code x} and
+ * {@code y}, this method returns {@code true} if and only
+ * if {@code x} and {@code y} refer to the same object
+ * ({@code x == y} has the value {@code true}).
* <p>
- * Note that it is generally necessary to override the <tt>hashCode</tt>
+ * Note that it is generally necessary to override the {@code hashCode}
* method whenever this method is overridden, so as to maintain the
- * general contract for the <tt>hashCode</tt> method, which states
+ * general contract for the {@code hashCode} method, which states
* that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
- * @return <code>true</code> if this object is the same as the obj
- * argument; <code>false</code> otherwise.
+ * @return {@code true} if this object is the same as the obj
+ * argument; {@code false} otherwise.
* @see #hashCode()
- * @see java.util.Hashtable
+ * @see java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
@@ -154,7 +154,7 @@ public class Object {
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
- * intent is that, for any object <tt>x</tt>, the expression:
+ * intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
@@ -162,49 +162,49 @@ public class Object {
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
- * will be <tt>true</tt>, but these are not absolute requirements.
+ * will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
- * will be <tt>true</tt>, this is not an absolute requirement.
+ * will be {@code true}, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
- * <tt>super.clone</tt>. If a class and all of its superclasses (except
- * <tt>Object</tt>) obey this convention, it will be the case that
- * <tt>x.clone().getClass() == x.getClass()</tt>.
+ * {@code super.clone}. If a class and all of its superclasses (except
+ * {@code Object}) obey this convention, it will be the case that
+ * {@code x.clone().getClass() == x.getClass()}.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
- * by <tt>super.clone</tt> before returning it. Typically, this means
+ * by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
- * the case that no fields in the object returned by <tt>super.clone</tt>
+ * the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* <p>
- * The method <tt>clone</tt> for class <tt>Object</tt> performs a
+ * The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
- * not implement the interface <tt>Cloneable</tt>, then a
- * <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
- * are considered to implement the interface <tt>Cloneable</tt>.
+ * not implement the interface {@code Cloneable}, then a
+ * {@code CloneNotSupportedException} is thrown. Note that all arrays
+ * are considered to implement the interface {@code Cloneable}.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
- * The class <tt>Object</tt> does not itself implement the interface
- * <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
- * whose class is <tt>Object</tt> will result in throwing an
+ * The class {@code Object} does not itself implement the interface
+ * {@code Cloneable}, so calling the {@code clone} method on an object
+ * whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @exception CloneNotSupportedException if the object's class does not
- * support the <code>Cloneable</code> interface. Subclasses
- * that override the <code>clone</code> method can also
+ * support the {@code Cloneable} interface. Subclasses
+ * that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
@@ -213,15 +213,15 @@ public class Object {
/**
* Returns a string representation of the object. In general, the
- * <code>toString</code> method returns a string that
+ * {@code toString} method returns a string that
* "textually represents" this object. The result should
* be a concise but informative representation that is easy for a
* person to read.
* It is recommended that all subclasses override this method.
* <p>
- * The <code>toString</code> method for class <code>Object</code>
+ * The {@code toString} method for class {@code Object}
* returns a string consisting of the name of the class of which the
- * object is an instance, the at-sign character `<code>@</code>', and
+ * object is an instance, the at-sign character `{@code @}', and
* the unsigned hexadecimal representation of the hash code of the
* object. In other words, this method returns a string equal to the
* value of:
@@ -241,7 +241,7 @@ public class Object {
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
- * monitor by calling one of the <code>wait</code> methods.
+ * monitor by calling one of the {@code wait} methods.
* <p>
* The awakened thread will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened thread will
@@ -255,9 +255,9 @@ public class Object {
* object's monitor in one of three ways:
* <ul>
* <li>By executing a synchronized instance method of that object.
- * <li>By executing the body of a <code>synchronized</code> statement
+ * <li>By executing the body of a {@code synchronized} statement
* that synchronizes on the object.
- * <li>For objects of type <code>Class,</code> by executing a
+ * <li>For objects of type {@code Class,} by executing a
* synchronized static method of that class.
* </ul>
* <p>
@@ -273,7 +273,7 @@ public class Object {
/**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
- * <code>wait</code> methods.
+ * {@code wait} methods.
* <p>
* The awakened threads will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened threads
@@ -283,7 +283,7 @@ public class Object {
* being the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
- * of this object's monitor. See the <code>notify</code> method for a
+ * of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
@@ -308,15 +308,15 @@ public class Object {
* becomes disabled for thread scheduling purposes and lies dormant
* until one of four things happens:
* <ul>
- * <li>Some other thread invokes the <tt>notify</tt> method for this
+ * <li>Some other thread invokes the {@code notify} method for this
* object and thread <var>T</var> happens to be arbitrarily chosen as
* the thread to be awakened.
- * <li>Some other thread invokes the <tt>notifyAll</tt> method for this
+ * <li>Some other thread invokes the {@code notifyAll} method for this
* object.
* <li>Some other thread {@linkplain Thread#interrupt() interrupts}
* thread <var>T</var>.
* <li>The specified amount of real time has elapsed, more or less. If
- * <tt>timeout</tt> is zero, however, then real time is not taken into
+ * {@code timeout} is zero, however, then real time is not taken into
* consideration and the thread simply waits until notified.
* </ul>
* The thread <var>T</var> is then removed from the wait set for this
@@ -324,11 +324,11 @@ public class Object {
* usual manner with other threads for the right to synchronize on the
* object; once it has gained control of the object, all its
* synchronization claims on the object are restored to the status quo
- * ante - that is, to the situation as of the time that the <tt>wait</tt>
+ * ante - that is, to the situation as of the time that the {@code wait}
* method was invoked. Thread <var>T</var> then returns from the
- * invocation of the <tt>wait</tt> method. Thus, on return from the
- * <tt>wait</tt> method, the synchronization state of the object and of
- * thread <tt>T</tt> is exactly as it was when the <tt>wait</tt> method
+ * invocation of the {@code wait} method. Thus, on return from the
+ * {@code wait} method, the synchronization state of the object and of
+ * thread {@code T} is exactly as it was when the {@code wait} method
* was invoked.
* <p>
* A thread can also wake up without being notified, interrupted, or
@@ -351,18 +351,18 @@ public class Object {
*
* <p>If the current thread is {@linkplain java.lang.Thread#interrupt()
* interrupted} by any thread before or while it is waiting, then an
- * <tt>InterruptedException</tt> is thrown. This exception is not
+ * {@code InterruptedException} is thrown. This exception is not
* thrown until the lock status of this object has been restored as
* described above.
*
* <p>
- * Note that the <tt>wait</tt> method, as it places the current thread
+ * Note that the {@code wait} method, as it places the current thread
* into the wait set for this object, unlocks only this object; any
* other objects on which the current thread may be synchronized remain
* locked while the thread waits.
* <p>
* This method should only be called by a thread that is the owner
- * of this object's monitor. See the <code>notify</code> method for a
+ * of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
@@ -388,7 +388,7 @@ public class Object {
* some other thread interrupts the current thread, or a certain
* amount of real time has elapsed.
* <p>
- * This method is similar to the <code>wait</code> method of one
+ * This method is similar to the {@code wait} method of one
* argument, but it allows finer control over the amount of time to
* wait for a notification before giving up. The amount of real time,
* measured in nanoseconds, is given by:
@@ -398,17 +398,17 @@ public class Object {
* <p>
* In all other respects, this method does the same thing as the
* method {@link #wait(long)} of one argument. In particular,
- * <tt>wait(0, 0)</tt> means the same thing as <tt>wait(0)</tt>.
+ * {@code wait(0, 0)} means the same thing as {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until either of the
* following two conditions has occurred:
* <ul>
* <li>Another thread notifies threads waiting on this object's monitor
- * to wake up either through a call to the <code>notify</code> method
- * or the <code>notifyAll</code> method.
- * <li>The timeout period, specified by <code>timeout</code>
- * milliseconds plus <code>nanos</code> nanoseconds arguments, has
+ * to wake up either through a call to the {@code notify} method
+ * or the {@code notifyAll} method.
+ * <li>The timeout period, specified by {@code timeout}
+ * milliseconds plus {@code nanos} nanoseconds arguments, has
* elapsed.
* </ul>
* <p>
@@ -425,7 +425,7 @@ public class Object {
* }
* </pre>
* This method should only be called by a thread that is the owner
- * of this object's monitor. See the <code>notify</code> method for a
+ * of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
@@ -465,13 +465,13 @@ public class Object {
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
* In other words, this method behaves exactly as if it simply
- * performs the call <tt>wait(0)</tt>.
+ * performs the call {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
- * either through a call to the <code>notify</code> method or the
- * <code>notifyAll</code> method. The thread then waits until it can
+ * either through a call to the {@code notify} method or the
+ * {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* <p>
* As in the one argument version, interrupts and spurious wakeups are
@@ -484,7 +484,7 @@ public class Object {
* }
* </pre>
* This method should only be called by a thread that is the owner
- * of this object's monitor. See the <code>notify</code> method for a
+ * of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
@@ -505,49 +505,49 @@ public class Object {
/**
* Called by the garbage collector on an object when garbage collection
* determines that there are no more references to the object.
- * A subclass overrides the <code>finalize</code> method to dispose of
+ * A subclass overrides the {@code finalize} method to dispose of
* system resources or to perform other cleanup.
* <p>
- * The general contract of <tt>finalize</tt> is that it is invoked
+ * The general contract of {@code finalize} is that it is invoked
* if and when the Java<font size="-2"><sup>TM</sup></font> virtual
* machine has determined that there is no longer any
* means by which this object can be accessed by any thread that has
* not yet died, except as a result of an action taken by the
* finalization of some other object or class which is ready to be
- * finalized. The <tt>finalize</tt> method may take any action, including
+ * finalized. The {@code finalize} method may take any action, including
* making this object available again to other threads; the usual purpose
- * of <tt>finalize</tt>, however, is to perform cleanup actions before
+ * of {@code finalize}, however, is to perform cleanup actions before
* the object is irrevocably discarded. For example, the finalize method
* for an object that represents an input/output connection might perform
* explicit I/O transactions to break the connection before the object is
* permanently discarded.
* <p>
- * The <tt>finalize</tt> method of class <tt>Object</tt> performs no
+ * The {@code finalize} method of class {@code Object} performs no
* special action; it simply returns normally. Subclasses of
- * <tt>Object</tt> may override this definition.
+ * {@code Object} may override this definition.
* <p>
* The Java programming language does not guarantee which thread will
- * invoke the <tt>finalize</tt> method for any given object. It is
+ * invoke the {@code finalize} method for any given object. It is
* guaranteed, however, that the thread that invokes finalize will not
* be holding any user-visible synchronization locks when finalize is
* invoked. If an uncaught exception is thrown by the finalize method,
* the exception is ignored and finalization of that object terminates.
* <p>
- * After the <tt>finalize</tt> method has been invoked for an object, no
+ * After the {@code finalize} method has been invoked for an object, no
* further action is taken until the Java virtual machine has again
* determined that there is no longer any means by which this object can
* be accessed by any thread that has not yet died, including possible
* actions by other objects or classes which are ready to be finalized,
* at which point the object may be discarded.
* <p>
- * The <tt>finalize</tt> method is never invoked more than once by a Java
+ * The {@code finalize} method is never invoked more than once by a Java
* virtual machine for any given object.
* <p>
- * Any exception thrown by the <code>finalize</code> method causes
+ * Any exception thrown by the {@code finalize} method causes
* the finalization of this object to be halted, but is otherwise
* ignored.
*
- * @throws Throwable the <code>Exception</code> raised by this method
+ * @throws Throwable the {@code Exception} raised by this method
*/
protected void finalize() throws Throwable { }
}
diff --git a/src/share/classes/java/lang/RuntimePermission.java b/src/share/classes/java/lang/RuntimePermission.java
index fce9e08f1..717444329 100644
--- a/src/share/classes/java/lang/RuntimePermission.java
+++ b/src/share/classes/java/lang/RuntimePermission.java
@@ -100,6 +100,13 @@ import java.util.StringTokenizer;
* </tr>
*
* <tr>
+ * <td>closeClassLoader</td>
+ * <td>Closing of a ClassLoader</td>
+ * <td>Granting this permission allows code to close any URLClassLoader
+ * that it has a reference to.</td>
+ * </tr>
+ *
+ * <tr>
* <td>setSecurityManager</td>
* <td>Setting of the security manager (possibly replacing an existing one)
* </td>
diff --git a/src/share/classes/java/lang/Thread.java b/src/share/classes/java/lang/Thread.java
index 60300ebc9..3922f702c 100644
--- a/src/share/classes/java/lang/Thread.java
+++ b/src/share/classes/java/lang/Thread.java
@@ -25,13 +25,17 @@
package java.lang;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
-import sun.misc.SoftCache;
import sun.nio.ch.Interruptible;
import sun.security.util.SecurityConstants;
@@ -1640,8 +1644,17 @@ class Thread implements Runnable {
new RuntimePermission("enableContextClassLoaderOverride");
/** cache of subclass security audit results */
- private static final SoftCache subclassAudits = new SoftCache(10);
-
+ /* Replace with ConcurrentReferenceHashMap when/if it appears in a future
+ * release */
+ private static class Caches {
+ /** cache of subclass security audit results */
+ static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
+ new ConcurrentHashMap<WeakClassKey,Boolean>();
+
+ /** queue for WeakReferences to audited subclasses */
+ static final ReferenceQueue<Class<?>> subclassAuditsQueue =
+ new ReferenceQueue<Class<?>>();
+ }
/**
* Verifies that this (possibly subclass) instance can be constructed
@@ -1652,19 +1665,15 @@ class Thread implements Runnable {
private static boolean isCCLOverridden(Class cl) {
if (cl == Thread.class)
return false;
- Boolean result = null;
- synchronized (subclassAudits) {
- result = (Boolean) subclassAudits.get(cl);
- if (result == null) {
- /*
- * Note: only new Boolean instances (i.e., not Boolean.TRUE or
- * Boolean.FALSE) must be used as cache values, otherwise cache
- * entry will pin associated class.
- */
- result = new Boolean(auditSubclass(cl));
- subclassAudits.put(cl, result);
- }
+
+ processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
+ WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
+ Boolean result = Caches.subclassAudits.get(key);
+ if (result == null) {
+ result = Boolean.valueOf(auditSubclass(cl));
+ Caches.subclassAudits.putIfAbsent(key, result);
}
+
return result.booleanValue();
}
@@ -1967,6 +1976,68 @@ class Thread implements Runnable {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
+ /**
+ * Removes from the specified map any keys that have been enqueued
+ * on the specified reference queue.
+ */
+ static void processQueue(ReferenceQueue<Class<?>> queue,
+ ConcurrentMap<? extends
+ WeakReference<Class<?>>, ?> map)
+ {
+ Reference<? extends Class<?>> ref;
+ while((ref = queue.poll()) != null) {
+ map.remove(ref);
+ }
+ }
+
+ /**
+ * Weak key for Class objects.
+ **/
+ static class WeakClassKey extends WeakReference<Class<?>> {
+ /**
+ * saved value of the referent's identity hash code, to maintain
+ * a consistent hash code after the referent has been cleared
+ */
+ private final int hash;
+
+ /**
+ * Create a new WeakClassKey to the given object, registered
+ * with a queue.
+ */
+ WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
+ super(cl, refQueue);
+ hash = System.identityHashCode(cl);
+ }
+
+ /**
+ * Returns the identity hash code of the original referent.
+ */
+ @Override
+ public int hashCode() {
+ return hash;
+ }
+
+ /**
+ * Returns true if the given object is this identical
+ * WeakClassKey instance, or, if this object's referent has not
+ * been cleared, if the given object is another WeakClassKey
+ * instance with the identical non-null referent as this one.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+
+ if (obj instanceof WeakClassKey) {
+ Object referent = get();
+ return (referent != null) &&
+ (referent == ((WeakClassKey) obj).get());
+ } else {
+ return false;
+ }
+ }
+ }
+
/* Some private helper methods */
private native void setPriority0(int newPriority);
private native void stop0(Object o);
diff --git a/src/share/classes/java/lang/annotation/Annotation.java b/src/share/classes/java/lang/annotation/Annotation.java
index b8f9c89f0..6b4dcdf4e 100644
--- a/src/share/classes/java/lang/annotation/Annotation.java
+++ b/src/share/classes/java/lang/annotation/Annotation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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
@@ -31,6 +31,10 @@ package java.lang.annotation;
* an annotation type. Also note that this interface does not itself
* define an annotation type.
*
+ * More information about annotation types can be found in <i>The
+ * Java&trade; Language Specification, Third Edition</i>, <a
+ * href="http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.6">&sect;9.6</a>.
+ *
* @author Josh Bloch
* @since 1.5
*/
diff --git a/src/share/classes/java/lang/instrument/package.html b/src/share/classes/java/lang/instrument/package.html
index dde4dfd7e..f2835598f 100644
--- a/src/share/classes/java/lang/instrument/package.html
+++ b/src/share/classes/java/lang/instrument/package.html
@@ -1,3 +1,28 @@
+<!--
+ Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
+ particular file as subject to the "Classpath" exception as provided
+ by Sun in the LICENSE file that accompanied this code.
+
+ 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ CA 95054 USA or visit www.sun.com if you need additional information or
+ have any questions.
+-->
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
diff --git a/src/share/classes/java/net/CookieManager.java b/src/share/classes/java/net/CookieManager.java
index e262dd8c2..6b7b11653 100644
--- a/src/share/classes/java/net/CookieManager.java
+++ b/src/share/classes/java/net/CookieManager.java
@@ -107,8 +107,9 @@ import java.io.IOException;
* </ul>
* </blockquote>
*
- * <p>The implementation conforms to RFC 2965, section 3.3.
+ * <p>The implementation conforms to <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>, section 3.3.
*
+ * @see CookiePolicy
* @author Edward Wang
* @since 1.6
*/
diff --git a/src/share/classes/java/net/DatagramSocket.java b/src/share/classes/java/net/DatagramSocket.java
index 4558632c2..40c95ef5a 100644
--- a/src/share/classes/java/net/DatagramSocket.java
+++ b/src/share/classes/java/net/DatagramSocket.java
@@ -41,10 +41,11 @@ import java.security.PrivilegedExceptionAction;
* one machine to another may be routed differently, and may arrive in
* any order.
*
- * <p>UDP broadcasts sends are always enabled on a DatagramSocket.
- * In order to receive broadcast packets a DatagramSocket
- * should be bound to the wildcard address. In some
- * implementations, broadcast packets may also be received when
+ * <p> Where possible, a newly constructed {@code DatagramSocket} has the
+ * {@link SocketOptions#SO_BROADCAST SO_BROADCAST} socket option enabled so as
+ * to allow the transmission of broadcast datagrams. In order to receive
+ * broadcast packets a DatagramSocket should be bound to the wildcard address.
+ * In some implementations, broadcast packets may also be received when
* a DatagramSocket is bound to a more specific address.
* <p>
* Example:
@@ -1017,9 +1018,18 @@ class DatagramSocket implements java.io.Closeable {
/**
* Enable/disable SO_BROADCAST.
- * @param on whether or not to have broadcast turned on.
- * @exception SocketException if there is an error
- * in the underlying protocol, such as an UDP error.
+ *
+ * <p> Some operating systems may require that the Java virtual machine be
+ * started with implementation specific privileges to enable this option or
+ * send broadcast datagrams.
+ *
+ * @param on
+ * whether or not to have broadcast turned on.
+ *
+ * @throws SocketException
+ * if there is an error in the underlying protocol, such as an UDP
+ * error.
+ *
* @since 1.4
* @see #getBroadcast()
*/
diff --git a/src/share/classes/java/net/HttpCookie.java b/src/share/classes/java/net/HttpCookie.java
index 1fcdd6c51..bd16d4f27 100644
--- a/src/share/classes/java/net/HttpCookie.java
+++ b/src/share/classes/java/net/HttpCookie.java
@@ -33,6 +33,7 @@ import java.util.TimeZone;
import java.util.Date;
import java.lang.NullPointerException; // for javadoc
+import java.util.Locale;
/**
* An HttpCookie object represents an http cookie, which carries state
@@ -1058,8 +1059,7 @@ public final class HttpCookie implements Cloneable {
if (assignor != null) {
assignor.assign(cookie, attrName, attrValue);
} else {
- // must be an error
- throw new IllegalArgumentException("Illegal cookie attribute");
+ // Ignore the attribute as per RFC 2965
}
}
@@ -1097,7 +1097,7 @@ public final class HttpCookie implements Cloneable {
static {
cDateFormats = new SimpleDateFormat[COOKIE_DATE_FORMATS.length];
for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) {
- cDateFormats[i] = new SimpleDateFormat(COOKIE_DATE_FORMATS[i]);
+ cDateFormats[i] = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], Locale.US);
cDateFormats[i].setTimeZone(TimeZone.getTimeZone("GMT"));
}
}
diff --git a/src/share/classes/java/net/StandardProtocolFamily.java b/src/share/classes/java/net/StandardProtocolFamily.java
index 7c11b32f5..d4b03ed88 100644
--- a/src/share/classes/java/net/StandardProtocolFamily.java
+++ b/src/share/classes/java/net/StandardProtocolFamily.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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,7 @@
package java.net;
/**
- * Defines the standard family of communication protocols.
+ * Defines the standard families of communication protocols.
*
* @since 1.7
*/
diff --git a/src/share/classes/java/net/StandardSocketOption.java b/src/share/classes/java/net/StandardSocketOption.java
index 405038cc1..dba8ff22d 100644
--- a/src/share/classes/java/net/StandardSocketOption.java
+++ b/src/share/classes/java/net/StandardSocketOption.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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
@@ -59,6 +59,7 @@ public final class StandardSocketOption {
*
* @see <a href="http://www.ietf.org/rfc/rfc919.txt">RFC&nbsp;929:
* Broadcasting Internet Datagrams</a>
+ * @see DatagramSocket#setBroadcast
*/
public static final SocketOption<Boolean> SO_BROADCAST =
new StdSocketOption<Boolean>("SO_BROADCAST", Boolean.class);
@@ -78,6 +79,7 @@ public final class StandardSocketOption {
*
* @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC&nbsp;1122
* Requirements for Internet Hosts -- Communication Layers</a>
+ * @see Socket#setKeepAlive
*/
public static final SocketOption<Boolean> SO_KEEPALIVE =
new StdSocketOption<Boolean>("SO_KEEPALIVE", Boolean.class);
@@ -107,6 +109,8 @@ public final class StandardSocketOption {
* socket is bound or connected. Whether an implementation allows the
* socket send buffer to be changed after the socket is bound is system
* dependent.
+ *
+ * @see Socket#setSendBufferSize
*/
public static final SocketOption<Integer> SO_SNDBUF =
new StdSocketOption<Integer>("SO_SNDBUF", Integer.class);
@@ -145,6 +149,8 @@ public final class StandardSocketOption {
*
* @see <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC&nbsp;1323: TCP
* Extensions for High Performance</a>
+ * @see Socket#setReceiveBufferSize
+ * @see ServerSocket#setReceiveBufferSize
*/
public static final SocketOption<Integer> SO_RCVBUF =
new StdSocketOption<Integer>("SO_RCVBUF", Integer.class);
@@ -175,6 +181,7 @@ public final class StandardSocketOption {
*
* @see <a href="http://www.ietf.org/rfc/rfc793.txt">RFC&nbsp;793: Transmission
* Control Protocol</a>
+ * @see ServerSocket#setReuseAddress
*/
public static final SocketOption<Boolean> SO_REUSEADDR =
new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class);
@@ -205,6 +212,8 @@ public final class StandardSocketOption {
* is system dependent. Setting the linger interval to a value that is
* greater than its maximum value causes the linger interval to be set to
* its maximum value.
+ *
+ * @see Socket#setSoLinger
*/
public static final SocketOption<Integer> SO_LINGER =
new StdSocketOption<Integer>("SO_LINGER", Integer.class);
@@ -215,15 +224,15 @@ public final class StandardSocketOption {
/**
* The Type of Service (ToS) octet in the Internet Protocol (IP) header.
*
- * <p> The value of this socket option is an {@code Integer}, the least
- * significant 8 bits of which represents the value of the ToS octet in IP
- * packets sent by sockets to an {@link StandardProtocolFamily#INET IPv4}
- * socket. The interpretation of the ToS octet is network specific and
- * is not defined by this class. Further information on the ToS octet can be
- * found in <a href="http://www.ietf.org/rfc/rfc1349.txt">RFC&nbsp;1349</a>
- * and <a href="http://www.ietf.org/rfc/rfc2474.txt">RFC&nbsp;2474</a>. The
- * value of the socket option is a <em>hint</em>. An implementation may
- * ignore the value, or ignore specific values.
+ * <p> The value of this socket option is an {@code Integer} representing
+ * the value of the ToS octet in IP packets sent by sockets to an {@link
+ * StandardProtocolFamily#INET IPv4} socket. The interpretation of the ToS
+ * octet is network specific and is not defined by this class. Further
+ * information on the ToS octet can be found in <a
+ * href="http://www.ietf.org/rfc/rfc1349.txt">RFC&nbsp;1349</a> and <a
+ * href="http://www.ietf.org/rfc/rfc2474.txt">RFC&nbsp;2474</a>. The value
+ * of the socket option is a <em>hint</em>. An implementation may ignore the
+ * value, or ignore specific values.
*
* <p> The initial/default value of the TOS field in the ToS octet is
* implementation specific but will typically be {@code 0}. For
@@ -235,6 +244,8 @@ public final class StandardSocketOption {
* <p> The behavior of this socket option on a stream-oriented socket, or an
* {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this
* release.
+ *
+ * @see DatagramSocket#setTrafficClass
*/
public static final SocketOption<Integer> IP_TOS =
new StdSocketOption<Integer>("IP_TOS", Integer.class);
@@ -257,6 +268,7 @@ public final class StandardSocketOption {
* is system dependent.
*
* @see java.nio.channels.MulticastChannel
+ * @see MulticastSocket#setInterface
*/
public static final SocketOption<NetworkInterface> IP_MULTICAST_IF =
new StdSocketOption<NetworkInterface>("IP_MULTICAST_IF", NetworkInterface.class);
@@ -283,6 +295,7 @@ public final class StandardSocketOption {
* prior to binding the socket is system dependent.
*
* @see java.nio.channels.MulticastChannel
+ * @see MulticastSocket#setTimeToLive
*/
public static final SocketOption<Integer> IP_MULTICAST_TTL =
new StdSocketOption<Integer>("IP_MULTICAST_TTL", Integer.class);
@@ -307,6 +320,7 @@ public final class StandardSocketOption {
* binding the socket is system dependent.
*
* @see java.nio.channels.MulticastChannel
+ * @see MulticastSocket#setLoopbackMode
*/
public static final SocketOption<Boolean> IP_MULTICAST_LOOP =
new StdSocketOption<Boolean>("IP_MULTICAST_LOOP", Boolean.class);
@@ -328,11 +342,12 @@ public final class StandardSocketOption {
* coalescing impacts performance. The socket option may be enabled at any
* time. In other words, the Nagle Algorithm can be disabled. Once the option
* is enabled, it is system dependent whether it can be subsequently
- * disabled. In that case, invoking the {@code setOption} method to disable
- * the option has no effect.
+ * disabled. If it cannot, then invoking the {@code setOption} method to
+ * disable the option has no effect.
*
* @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC&nbsp;1122:
* Requirements for Internet Hosts -- Communication Layers</a>
+ * @see Socket#setTcpNoDelay
*/
public static final SocketOption<Boolean> TCP_NODELAY =
new StdSocketOption<Boolean>("TCP_NODELAY", Boolean.class);
diff --git a/src/share/classes/java/net/URLClassLoader.java b/src/share/classes/java/net/URLClassLoader.java
index 9ffd0287c..afb92b8c9 100644
--- a/src/share/classes/java/net/URLClassLoader.java
+++ b/src/share/classes/java/net/URLClassLoader.java
@@ -31,10 +31,12 @@ import java.io.File;
import java.io.FilePermission;
import java.io.InputStream;
import java.io.IOException;
+import java.io.Closeable;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandlerFactory;
import java.util.Enumeration;
+import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.jar.Manifest;
@@ -70,7 +72,7 @@ import sun.security.util.SecurityConstants;
* @author David Connelly
* @since 1.2
*/
-public class URLClassLoader extends SecureClassLoader {
+public class URLClassLoader extends SecureClassLoader implements Closeable {
/* The search path for classes and resources */
URLClassPath ucp;
@@ -85,13 +87,13 @@ public class URLClassLoader extends SecureClassLoader {
* to refer to a JAR file which will be downloaded and opened as needed.
*
* <p>If there is a security manager, this method first
- * calls the security manager's <code>checkCreateClassLoader</code> method
+ * calls the security manager's {@code checkCreateClassLoader} method
* to ensure creation of a class loader is allowed.
*
* @param urls the URLs from which to load classes and resources
* @param parent the parent class loader for delegation
* @exception SecurityException if a security manager exists and its
- * <code>checkCreateClassLoader</code> method doesn't allow
+ * {@code checkCreateClassLoader} method doesn't allow
* creation of a class loader.
* @see SecurityManager#checkCreateClassLoader
*/
@@ -169,12 +171,65 @@ public class URLClassLoader extends SecureClassLoader {
acc = AccessController.getContext();
}
+
+ /**
+ * Closes this URLClassLoader, so that it can no longer be used to load
+ * new classes or resources that are defined by this loader.
+ * Classes and resources defined by any of this loader's parents in the
+ * delegation hierarchy are still accessible. Also, any classes or resources
+ * that are already loaded, are still accessible.
+ * <p>
+ * In the case of jar: and file: URLs, it also closes any class files,
+ * or JAR files that were opened by it. If another thread is loading a
+ * class when the {@code close} method is invoked, then the result of
+ * that load is undefined.
+ * <p>
+ * The method makes a best effort attempt to close all opened files,
+ * by catching {@link IOException}s internally. Unchecked exceptions
+ * and errors are not caught. Calling close on an already closed
+ * loader has no effect.
+ * <p>
+ * @throws IOException if closing any file opened by this class loader
+ * resulted in an IOException. Any such exceptions are caught, and a
+ * single IOException is thrown after the last file has been closed.
+ * If only one exception was thrown, it will be set as the <i>cause</i>
+ * of this IOException.
+ *
+ * @throws SecurityException if a security manager is set, and it denies
+ * {@link RuntimePermission}<tt>("closeClassLoader")</tt>
+ *
+ * @since 1.7
+ */
+ public void close() throws IOException {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(new RuntimePermission("closeClassLoader"));
+ }
+ List<IOException> errors = ucp.closeLoaders();
+ if (errors.isEmpty()) {
+ return;
+ }
+ if (errors.size() == 1) {
+ throw new IOException (
+ "Error closing URLClassLoader resource",
+ errors.get(0)
+ );
+ }
+ // Several exceptions. So, just combine the error messages
+ String errormsg = "Error closing resources: ";
+ for (IOException error: errors) {
+ errormsg = errormsg + "[" + error.toString() + "] ";
+ }
+ throw new IOException (errormsg);
+ }
+
/**
* Appends the specified URL to the list of URLs to search for
* classes and resources.
* <p>
* If the URL specified is <code>null</code> or is already in the
- * list of URLs, then invoking this method has no effect.
+ * list of URLs, or if this loader is closed, then invoking this
+ * method has no effect.
*
* @param url the URL to be added to the search path of URLs
*/
@@ -199,7 +254,8 @@ public class URLClassLoader extends SecureClassLoader {
*
* @param name the name of the class
* @return the resulting class
- * @exception ClassNotFoundException if the class could not be found
+ * @exception ClassNotFoundException if the class could not be found,
+ * or if the loader is closed.
*/
protected Class<?> findClass(final String name)
throws ClassNotFoundException
@@ -370,7 +426,7 @@ public class URLClassLoader extends SecureClassLoader {
*
* @param name the name of the resource
* @return a <code>URL</code> for the resource, or <code>null</code>
- * if the resource could not be found.
+ * if the resource could not be found, or if the loader is closed.
*/
public URL findResource(final String name) {
/*
@@ -393,6 +449,7 @@ public class URLClassLoader extends SecureClassLoader {
* @param name the resource name
* @exception IOException if an I/O exception occurs
* @return an <code>Enumeration</code> of <code>URL</code>s
+ * If the loader is closed, the Enumeration will be empty.
*/
public Enumeration<URL> findResources(final String name)
throws IOException
diff --git a/src/share/classes/java/nio/channels/AsynchronousByteChannel.java b/src/share/classes/java/nio/channels/AsynchronousByteChannel.java
new file mode 100644
index 000000000..7bc433574
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousByteChannel.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.Future;
+
+/**
+ * An asynchronous channel that can read and write bytes.
+ *
+ * <p> Some channels may not allow more than one read or write to be outstanding
+ * at any given time. If a thread invokes a read method before a previous read
+ * operation has completed then a {@link ReadPendingException} will be thrown.
+ * Similarly, if a write method is invoked before a previous write has completed
+ * then {@link WritePendingException} is thrown. Whether or not other kinds of
+ * I/O operations may proceed concurrently with a read operation depends upon
+ * the type of the channel.
+ *
+ * <p> Note that {@link java.nio.ByteBuffer ByteBuffers} are not safe for use by
+ * multiple concurrent threads. When a read or write operation is initiated then
+ * care must be taken to ensure that the buffer is not accessed until the
+ * operation completes.
+ *
+ * @see Channels#newInputStream(AsynchronousByteChannel)
+ * @see Channels#newOutputStream(AsynchronousByteChannel)
+ *
+ * @since 1.7
+ */
+
+public interface AsynchronousByteChannel
+ extends AsynchronousChannel
+{
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * <p> This method initiates an operation to read a sequence of bytes from
+ * this channel into the given buffer. The method returns a {@link Future}
+ * representing the pending result of the operation. The result of the
+ * operation, obtained by invoking the {@code Future} 's {@link
+ * Future#get() get} method, is the number of bytes read or {@code -1} if
+ * all bytes have been read and the channel has reached end-of-stream.
+ *
+ * <p> This method initiates a read operation to read up to <i>r</i> bytes
+ * from the channel, where <i>r</i> is the number of bytes remaining in the
+ * buffer, that is, {@code dst.remaining()} at the time that the read is
+ * attempted. Where <i>r</i> is 0, the read operation completes immediately
+ * with a result of {@code 0} without initiating an I/O operation.
+ *
+ * <p> Suppose that a byte sequence of length <i>n</i> is read, where
+ * <tt>0</tt>&nbsp;<tt>&lt;</tt>&nbsp;<i>n</i>&nbsp;<tt>&lt;=</tt>&nbsp;<i>r</i>.
+ * This byte sequence will be transferred into the buffer so that the first
+ * byte in the sequence is at index <i>p</i> and the last byte is at index
+ * <i>p</i>&nbsp;<tt>+</tt>&nbsp;<i>n</i>&nbsp;<tt>-</tt>&nbsp;<tt>1</tt>,
+ * where <i>p</i> is the buffer's position at the moment the read is
+ * performed. Upon completion the buffer's position will be equal to
+ * <i>p</i>&nbsp;<tt>+</tt>&nbsp;<i>n</i>; its limit will not have changed.
+ *
+ * <p> Buffers are not safe for use by multiple concurrent threads so care
+ * should be taken to not to access the buffer until the operaton has completed.
+ *
+ * <p> This method may be invoked at any time. Some channel types may not
+ * allow more than one read to be outstanding at any given time. If a thread
+ * initiates a read operation before a previous read operation has
+ * completed then a {@link ReadPendingException} will be thrown.
+ *
+ * <p> The <tt>handler</tt> parameter is used to specify a {@link
+ * CompletionHandler}. When the read operation completes the handler's
+ * {@link CompletionHandler#completed completed} method is executed.
+ *
+ *
+ * @param dst
+ * The buffer into which bytes are to be transferred
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The completion handler object; can be {@code null}
+ *
+ * @return A Future representing the result of the operation
+ *
+ * @throws IllegalArgumentException
+ * If the buffer is read-only
+ * @throws ReadPendingException
+ * If the channel does not allow more than one read to be outstanding
+ * and a previous read has not completed
+ */
+ <A> Future<Integer> read(ByteBuffer dst,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * <p> An invocation of this method of the form <tt>c.read(dst)</tt>
+ * behaves in exactly the same manner as the invocation
+ * <blockquote><pre>
+ * c.read(dst, null, null);</pre></blockquote>
+ *
+ * @param dst
+ * The buffer into which bytes are to be transferred
+ *
+ * @return A Future representing the result of the operation
+ *
+ * @throws IllegalArgumentException
+ * If the buffer is read-only
+ * @throws ReadPendingException
+ * If the channel does not allow more than one read to be outstanding
+ * and a previous read has not completed
+ */
+ Future<Integer> read(ByteBuffer dst);
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * <p> This method initiates an operation to write a sequence of bytes to
+ * this channel from the given buffer. This method returns a {@link
+ * Future} representing the pending result of the operation. The result
+ * of the operation, obtained by invoking the <tt>Future</tt>'s {@link
+ * Future#get() get} method, is the number of bytes written, possibly zero.
+ *
+ * <p> This method initiates a write operation to write up to <i>r</i> bytes
+ * to the channel, where <i>r</i> is the number of bytes remaining in the
+ * buffer, that is, {@code src.remaining()} at the moment the write is
+ * attempted. Where <i>r</i> is 0, the write operation completes immediately
+ * with a result of {@code 0} without initiating an I/O operation.
+ *
+ * <p> Suppose that a byte sequence of length <i>n</i> is written, where
+ * <tt>0</tt>&nbsp;<tt>&lt;</tt>&nbsp;<i>n</i>&nbsp;<tt>&lt;=</tt>&nbsp;<i>r</i>.
+ * This byte sequence will be transferred from the buffer starting at index
+ * <i>p</i>, where <i>p</i> is the buffer's position at the moment the
+ * write is performed; the index of the last byte written will be
+ * <i>p</i>&nbsp;<tt>+</tt>&nbsp;<i>n</i>&nbsp;<tt>-</tt>&nbsp;<tt>1</tt>.
+ * Upon completion the buffer's position will be equal to
+ * <i>p</i>&nbsp;<tt>+</tt>&nbsp;<i>n</i>; its limit will not have changed.
+ *
+ * <p> Buffers are not safe for use by multiple concurrent threads so care
+ * should be taken to not to access the buffer until the operaton has completed.
+ *
+ * <p> This method may be invoked at any time. Some channel types may not
+ * allow more than one write to be outstanding at any given time. If a thread
+ * initiates a write operation before a previous write operation has
+ * completed then a {@link WritePendingException} will be thrown.
+ *
+ * <p> The <tt>handler</tt> parameter is used to specify a {@link
+ * CompletionHandler}. When the write operation completes the handler's
+ * {@link CompletionHandler#completed completed} method is executed.
+ *
+ * @param src
+ * The buffer from which bytes are to be retrieved
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The completion handler object; can be {@code null}
+ *
+ * @return A Future representing the result of the operation
+ *
+ * @throws WritePendingException
+ * If the channel does not allow more than one write to be outstanding
+ * and a previous write has not completed
+ */
+ <A> Future<Integer> write(ByteBuffer src,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * <p> An invocation of this method of the form <tt>c.write(src)</tt>
+ * behaves in exactly the same manner as the invocation
+ * <blockquote><pre>
+ * c.write(src, null, null);</pre></blockquote>
+ *
+ * @param src
+ * The buffer from which bytes are to be retrieved
+ *
+ * @return A Future representing the result of the operation
+ *
+ * @throws WritePendingException
+ * If the channel does not allow more than one write to be outstanding
+ * and a previous write has not completed
+ */
+ Future<Integer> write(ByteBuffer src);
+}
diff --git a/src/share/classes/java/nio/channels/AsynchronousChannel.java b/src/share/classes/java/nio/channels/AsynchronousChannel.java
new file mode 100644
index 000000000..f3e4ffe4e
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousChannel.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.io.IOException;
+import java.util.concurrent.Future; // javadoc
+
+/**
+ * A channel that supports asynchronous I/O operations. Asynchronous I/O
+ * operations will usually take one of two forms:
+ *
+ * <ol>
+ * <li><pre>{@link Future}&lt;V&gt; <em>operation</em>(<em>...</em>)</pre></li>
+ * <li><pre>Future&lt;V&gt; <em>operation</em>(<em>...</em> A attachment, {@link CompletionHandler}&lt;V,? super A&gt handler)</pre></li>
+ * </ol>
+ *
+ * where <i>operation</i> is the name of the I/O operation (read or write for
+ * example), <i>V</i> is the result type of the I/O operation, and <i>A</i> is
+ * the type of an object attached to the I/O operation to provide context when
+ * consuming the result. The attachment is important for cases where a
+ * <em>state-less</em> {@code CompletionHandler} is used to consume the result
+ * of many I/O operations.
+ *
+ * <p> In the first form, the methods defined by the {@link Future Future}
+ * interface may be used to check if the operation has completed, wait for its
+ * completion, and to retrieve the result. In the second form, a {@link
+ * CompletionHandler} is invoked to consume the result of the I/O operation when
+ * it completes, fails, or is cancelled.
+ *
+ * <p> A channel that implements this interface is <em>asynchronously
+ * closeable</em>: If an I/O operation is outstanding on the channel and the
+ * channel's {@link #close close} method is invoked, then the I/O operation
+ * fails with the exception {@link AsynchronousCloseException}.
+ *
+ * <p> Asynchronous channels are safe for use by multiple concurrent threads.
+ * Some channel implementations may support concurrent reading and writing, but
+ * may not allow more than one read and one write operation to be outstanding at
+ * any given time.
+ *
+ * <h4>Cancellation</h4>
+ *
+ * <p> The {@code Future} interface defines the {@link Future#cancel cancel}
+ * method to cancel execution of a task.
+ *
+ * <p> Where the {@code cancel} method is invoked with the {@code
+ * mayInterruptIfRunning} parameter set to {@code true} then the I/O operation
+ * may be interrupted by closing the channel. This will cause any other I/O
+ * operations outstanding on the channel to complete with the exception {@link
+ * AsynchronousCloseException}.
+ *
+ * <p> If a {@code CompletionHandler} is specified when initiating an I/O
+ * operation, and the {@code cancel} method is invoked to cancel the I/O
+ * operation before it completes, then the {@code CompletionHandler}'s {@link
+ * CompletionHandler#cancelled cancelled} method is invoked.
+ *
+ * <p> If an implementation of this interface supports a means to cancel I/O
+ * operations, and where cancellation may leave the channel, or the entity to
+ * which it is connected, in an inconsistent state, then the channel is put into
+ * an implementation specific <em>error state</em> that prevents further
+ * attempts to initiate I/O operations on the channel. For example, if a read
+ * operation is cancelled but the implementation cannot guarantee that bytes
+ * have not been read from the channel then it puts the channel into error state
+ * state; further attempts to initiate a {@code read} operation causes an
+ * unspecified runtime exception to be thrown.
+ *
+ * <p> Where the {@code cancel} method is invoked to cancel read or write
+ * operations then it recommended that all buffers used in the I/O operations be
+ * discarded or care taken to ensure that the buffers are not accessed while the
+ * channel remains open.
+ *
+ * @since 1.7
+ */
+
+public interface AsynchronousChannel
+ extends Channel
+{
+ /**
+ * Closes this channel.
+ *
+ * <p> Any outstanding asynchronous operations upon this channel will
+ * complete with the exception {@link AsynchronousCloseException}. After a
+ * channel is closed then further attempts to initiate asynchronous I/O
+ * operations complete immediately with cause {@link ClosedChannelException}.
+ *
+ * <p> This method otherwise behaves exactly as specified by the {@link
+ * Channel} interface.
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ @Override
+ void close() throws IOException;
+}
diff --git a/src/share/classes/java/nio/channels/AsynchronousChannelGroup.java b/src/share/classes/java/nio/channels/AsynchronousChannelGroup.java
new file mode 100644
index 000000000..1199e16b5
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousChannelGroup.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A grouping of asynchronous channels for the purpose of resource sharing.
+ *
+ * <p> An asynchronous channel group encapsulates the mechanics required to
+ * handle the completion of I/O operations initiated by {@link AsynchronousChannel
+ * asynchronous channels} that are bound to the group. A group has an associated
+ * thread pool to which tasks are submitted to handle I/O events and dispatch to
+ * {@link CompletionHandler completion-handlers} that consume the result of
+ * asynchronous operations performed on channels in the group. In addition to
+ * handling I/O events, the pooled threads may also execute other tasks required
+ * to support the execution of asynchronous I/O operations.
+ *
+ * <p> An asynchronous channel group is created by invoking the {@link
+ * #withFixedThreadPool withFixedThreadPool} or {@link #withCachedThreadPool
+ * withCachedThreadPool} methods defined here. Channels are bound to a group by
+ * specifying the group when constructing the channel. The associated thread
+ * pool is <em>owned</em> by the group; termination of the group results in the
+ * shutdown of the associated thread pool.
+ *
+ * <p> In addition to groups created explicitly, the Java virtual machine
+ * maintains a system-wide <em>default group</em> that is constructed
+ * automatically. Asynchronous channels that do not specify a group at
+ * construction time are bound to the default group. The default group has an
+ * associated thread pool that creates new threads as needed. The default group
+ * may be configured by means of system properties defined in the table below.
+ * Where the {@link java.util.concurrent.ThreadFactory ThreadFactory} for the
+ * default group is not configured then the pooled threads of the default group
+ * are {@link Thread#isDaemon daemon} threads.
+ *
+ * <table border>
+ * <tr>
+ * <th>System property</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <tr>
+ * <td> {@code java.nio.channels.DefaultThreadPool.threadFactory} </td>
+ * <td> The value of this property is taken to be the fully-qualified name
+ * of a concrete {@link java.util.concurrent.ThreadFactory ThreadFactory}
+ * class. The class is loaded using the system class loader and instantiated.
+ * The factory's {@link java.util.concurrent.ThreadFactory#newThread
+ * newThread} method is invoked to create each thread for the default
+ * group's thread pool. If the process to load and instantiate the value
+ * of the property fails then an unspecified error is thrown during the
+ * construction of the default group. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code java.nio.channels.DefaultThreadPool.initialSize} </td>
+ * <td> The value of the {@code initialSize} parameter for the default
+ * group (see {@link #withCachedThreadPool withCachedThreadPool}).
+ * The value of the property is taken to be the {@code String}
+ * representation of an {@code Integer} that is the initial size parameter.
+ * If the value cannot be parsed as an {@code Integer} it causes an
+ * unspecified error to be thrown during the construction of the default
+ * group. </td>
+ * </tr>
+ * </table>
+ *
+ * <a name="threading"><h4>Threading</h4></a>
+ *
+ * <p> The completion handler for an I/O operation initiated on a channel bound
+ * to a group is guaranteed to be invoked by one of the pooled threads in the
+ * group. This ensures that the completion handler is run by a thread with the
+ * expected <em>identity</em>.
+ *
+ * <p> Where an I/O operation completes immediately, and the initiating thread
+ * is one of the pooled threads in the group then the completion handler may
+ * be invoked directly by the initiating thread. To avoid stack overflow, an
+ * implementation may impose a limit as to the number of activations on the
+ * thread stack. Some I/O operations may prohibit invoking the completion
+ * handler directly by the initiating thread (see {@link
+ * AsynchronousServerSocketChannel#accept(Object,CompletionHandler) accept}).
+ *
+ * <a name="shutdown"><h4>Shutdown and Termination</h4></a>
+ *
+ * <p> The {@link #shutdown() shutdown} method is used to initiate an <em>orderly
+ * shutdown</em> of a group. An orderly shutdown marks the group as shutdown;
+ * further attempts to construct a channel that binds to the group will throw
+ * {@link ShutdownChannelGroupException}. Whether or not a group is shutdown can
+ * be tested using the {@link #isShutdown() isShutdown} method. Once shutdown,
+ * the group <em>terminates</em> when all asynchronous channels that are bound to
+ * the group are closed, all actively executing completion handlers have run to
+ * completion, and resources used by the group are released. No attempt is made
+ * to stop or interrupt threads that are executing completion handlers. The
+ * {@link #isTerminated() isTerminated} method is used to test if the group has
+ * terminated, and the {@link #awaitTermination awaitTermination} method can be
+ * used to block until the group has terminated.
+ *
+ * <p> The {@link #shutdownNow() shutdownNow} method can be used to initiate a
+ * <em>forceful shutdown</em> of the group. In addition to the actions performed
+ * by an orderly shutdown, the {@code shutdownNow} method closes all open channels
+ * in the group as if by invoking the {@link AsynchronousChannel#close close}
+ * method.
+ *
+ * @since 1.7
+ *
+ * @see AsynchronousSocketChannel#open(AsynchronousChannelGroup)
+ * @see AsynchronousServerSocketChannel#open(AsynchronousChannelGroup)
+ */
+
+public abstract class AsynchronousChannelGroup {
+ private final AsynchronousChannelProvider provider;
+
+ /**
+ * Initialize a new instance of this class.
+ *
+ * @param provider
+ * The asynchronous channel provider for this group
+ */
+ protected AsynchronousChannelGroup(AsynchronousChannelProvider provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Returns the provider that created this channel group.
+ *
+ * @return The provider that created this channel group
+ */
+ public final AsynchronousChannelProvider provider() {
+ return provider;
+ }
+
+ /**
+ * Creates an asynchronous channel group with a fixed thread pool.
+ *
+ * <p> The resulting asynchronous channel group reuses a fixed number of
+ * threads. At any point, at most {@code nThreads} threads will be active
+ * processing tasks that are submitted to handle I/O events and dispatch
+ * completion results for operations initiated on asynchronous channels in
+ * the group.
+ *
+ * <p> The group is created by invoking the {@link
+ * AsynchronousChannelProvider#openAsynchronousChannelGroup(int,ThreadFactory)
+ * openAsynchronousChannelGroup(int,ThreadFactory)} method of the system-wide
+ * default {@link AsynchronousChannelProvider} object.
+ *
+ * @param nThreads
+ * The number of threads in the pool
+ * @param threadFactory
+ * The factory to use when creating new threads
+ *
+ * @return A new asynchronous channel group
+ *
+ * @throws IllegalArgumentException
+ * If {@code nThreads <= 0}
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousChannelGroup withFixedThreadPool(int nThreads,
+ ThreadFactory threadFactory)
+ throws IOException
+ {
+ return AsynchronousChannelProvider.provider()
+ .openAsynchronousChannelGroup(nThreads, threadFactory);
+ }
+
+ /**
+ * Creates an asynchronous channel group with a given thread pool that
+ * creates new threads as needed.
+ *
+ * <p> The {@code executor} parameter is an {@code ExecutorService} that
+ * creates new threads as needed to execute tasks that are submitted to
+ * handle I/O events and dispatch completion results for operations initiated
+ * on asynchronous channels in the group. It may reuse previously constructed
+ * threads when they are available.
+ *
+ * <p> The {@code initialSize} parameter may be used by the implementation
+ * as a <em>hint</em> as to the initial number of tasks it may submit. For
+ * example, it may be used to indictae the initial number of threads that
+ * wait on I/O events.
+ *
+ * <p> The executor is intended to be used exclusively by the resulting
+ * asynchronous channel group. Termination of the group results in the
+ * orderly {@link ExecutorService#shutdown shutdown} of the executor
+ * service. Shutting down the executor service by other means results in
+ * unspecified behavior.
+ *
+ * <p> The group is created by invoking the {@link
+ * AsynchronousChannelProvider#openAsynchronousChannelGroup(ExecutorService,int)
+ * openAsynchronousChannelGroup(ExecutorService,int)} method of the system-wide
+ * default {@link AsynchronousChannelProvider} object.
+ *
+ * @param executor
+ * The thread pool for the resulting group
+ * @param initialSize
+ * A value {@code >=0} or a negative value for implementation
+ * specific default
+ *
+ * @return A new asynchronous channel group
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see java.util.concurrent.Executors#newCachedThreadPool
+ */
+ public static AsynchronousChannelGroup withCachedThreadPool(ExecutorService executor,
+ int initialSize)
+ throws IOException
+ {
+ return AsynchronousChannelProvider.provider()
+ .openAsynchronousChannelGroup(executor, initialSize);
+ }
+
+ /**
+ * Creates an asynchronous channel group with a given thread pool.
+ *
+ * <p> The {@code executor} parameter is an {@code ExecutorService} that
+ * executes tasks submitted to dispatch completion results for operations
+ * initiated on asynchronous channels in the group.
+ *
+ * <p> Care should be taken when configuring the executor service. It
+ * should support <em>direct handoff</em> or <em>unbounded queuing</em> of
+ * submitted tasks, and the thread that invokes the {@link
+ * ExecutorService#execute execute} method should never invoke the task
+ * directly. An implementation may mandate additional constraints.
+ *
+ * <p> The executor is intended to be used exclusively by the resulting
+ * asynchronous channel group. Termination of the group results in the
+ * orderly {@link ExecutorService#shutdown shutdown} of the executor
+ * service. Shutting down the executor service by other means results in
+ * unspecified behavior.
+ *
+ * <p> The group is created by invoking the {@link
+ * AsynchronousChannelProvider#openAsynchronousChannelGroup(ExecutorService,int)
+ * openAsynchronousChannelGroup(ExecutorService,int)} method of the system-wide
+ * default {@link AsynchronousChannelProvider} object with an {@code
+ * initialSize} of {@code 0}.
+ *
+ * @param executor
+ * The thread pool for the resulting group
+ *
+ * @return A new asynchronous channel group
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousChannelGroup withThreadPool(ExecutorService executor)
+ throws IOException
+ {
+ return AsynchronousChannelProvider.provider()
+ .openAsynchronousChannelGroup(executor, 0);
+ }
+
+ /**
+ * Tells whether or not this asynchronous channel group is shutdown.
+ *
+ * @return {@code true} if this asynchronous channel group is shutdown or
+ * has been marked for shutdown.
+ */
+ public abstract boolean isShutdown();
+
+ /**
+ * Tells whether or not this group has terminated.
+ *
+ * <p> Where this method returns {@code true}, then the associated thread
+ * pool has also {@link ExecutorService#isTerminated terminated}.
+ *
+ * @return {@code true} if this group has terminated
+ */
+ public abstract boolean isTerminated();
+
+ /**
+ * Initiates an orderly shutdown of the group.
+ *
+ * <p> This method marks the group as shutdown. Further attempts to construct
+ * channel that binds to this group will throw {@link ShutdownChannelGroupException}.
+ * The group terminates when all asynchronous channels in the group are
+ * closed, all actively executing completion handlers have run to completion,
+ * and all resources have been released. This method has no effect if the
+ * group is already shutdown.
+ */
+ public abstract void shutdown();
+
+ /**
+ * Shuts down the group and closes all open channels in the group.
+ *
+ * <p> In addition to the actions performed by the {@link #shutdown() shutdown}
+ * method, this method invokes the {@link AsynchronousChannel#close close}
+ * method on all open channels in the group. This method does not attempt to
+ * stop or interrupt threads that are executing completion handlers. The
+ * group terminates when all actively executing completion handlers have run
+ * to completion and all resources have been released. This method may be
+ * invoked at any time. If some other thread has already invoked it, then
+ * another invocation will block until the first invocation is complete,
+ * after which it will return without effect.
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract void shutdownNow() throws IOException;
+
+ /**
+ * Awaits termination of the group.
+
+ * <p> This method blocks until the group has terminated, or the timeout
+ * occurs, or the current thread is interrupted, whichever happens first.
+ *
+ * @param timeout
+ * The maximum time to wait, or zero or less to not wait
+ * @param unit
+ * The time unit of the timeout argument
+ *
+ * @return {@code true} if the group has terminated; {@code false} if the
+ * timeout elapsed before termination
+ *
+ * @throws InterruptedException
+ * If interrupted while waiting
+ */
+ public abstract boolean awaitTermination(long timeout, TimeUnit unit)
+ throws InterruptedException;
+}
diff --git a/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java b/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java
new file mode 100644
index 000000000..6a9d9f097
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java
@@ -0,0 +1,718 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.channels.spi.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.Future;
+import java.io.IOException;
+import java.net.SocketOption;
+import java.net.SocketAddress;
+import java.net.ProtocolFamily;
+import java.nio.ByteBuffer;
+
+/**
+ * An asynchronous channel for datagram-oriented sockets.
+ *
+ * <p> An asynchronous datagram channel is created by invoking one of the {@link
+ * #open open} methods defined by this class. It is not possible to create a channel
+ * for an arbitrary, pre-existing datagram socket. A newly-created asynchronous
+ * datagram channel is open but not connected. It need not be connected in order
+ * for the {@link #send send} and {@link #receive receive} methods to be used.
+ * A datagram channel may be connected, by invoking its {@link #connect connect}
+ * method, in order to avoid the overhead of the security checks that are otherwise
+ * performed as part of every send and receive operation when a security manager
+ * is set. The channel must be connected in order to use the {@link #read read}
+ * and {@link #write write} methods, since those methods do not accept or return
+ * socket addresses. Once connected, an asynchronous datagram channel remains
+ * connected until it is disconnected or closed.
+ *
+ * <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
+ * setOption} method. An asynchronous datagram channel to an Internet Protocol
+ * (IP) socket supports the following options:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} </td>
+ * <td> The size of the socket send buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td>
+ * <td> The size of the socket receive buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td>
+ * <td> Re-use address </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} </td>
+ * <td> Allow transmission of broadcast datagrams </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#IP_TOS IP_TOS} </td>
+ * <td> The Type of Service (ToS) octet in the Internet Protocol (IP) header </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} </td>
+ * <td> The network interface for Internet Protocol (IP) multicast datagrams </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_TTL
+ * IP_MULTICAST_TTL} </td>
+ * <td> The <em>time-to-live</em> for Internet Protocol (IP) multicast
+ * datagrams </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_LOOP
+ * IP_MULTICAST_LOOP} </td>
+ * <td> Loopback for Internet Protocol (IP) multicast datagrams </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported.
+ *
+ * <p> Asynchronous datagram channels allow more than one read/receive and
+ * write/send to be oustanding at any given time.
+ *
+ * <p> <b>Usage Example:</b>
+ * <pre>
+ * final AsynchronousDatagramChannel dc = AsynchronousDatagramChannel.open()
+ * .bind(new InetSocketAddress(4000));
+ *
+ * // print the source address of all packets that we receive
+ * dc.receive(buffer, buffer, new CompletionHandler&lt;SocketAddress,ByteBuffer&gt;() {
+ * public void completed(SocketAddress sa, ByteBuffer buffer) {
+ * try {
+ * System.out.println(sa);
+ *
+ * buffer.clear();
+ * dc.receive(buffer, buffer, this);
+ * } catch (...) { ... }
+ * }
+ * public void failed(Throwable exc, ByteBuffer buffer) {
+ * ...
+ * }
+ * public void cancelled(ByteBuffer buffer) {
+ * ...
+ * }
+ * });
+ * </pre>
+ *
+ * @since 1.7
+ */
+
+public abstract class AsynchronousDatagramChannel
+ implements AsynchronousByteChannel, MulticastChannel
+{
+ private final AsynchronousChannelProvider provider;
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AsynchronousDatagramChannel(AsynchronousChannelProvider provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Returns the provider that created this channel.
+ */
+ public final AsynchronousChannelProvider provider() {
+ return provider;
+ }
+
+ /**
+ * Opens an asynchronous datagram channel.
+ *
+ * <p> The new channel is created by invoking the {@link
+ * java.nio.channels.spi.AsynchronousChannelProvider#openAsynchronousDatagramChannel
+ * openAsynchronousDatagramChannel} method on the {@link
+ * java.nio.channels.spi.AsynchronousChannelProvider} object that created
+ * the given group (or the default provider where {@code group} is {@code
+ * null}).
+ *
+ * <p> The {@code family} parameter is used to specify the {@link ProtocolFamily}.
+ * If the datagram channel is to be used for Internet Protocol {@link
+ * MulticastChannel multicasting} then this parameter should correspond to
+ * the address type of the multicast groups that this channel will join.
+ *
+ * @param family
+ * The protocol family, or {@code null} to use the default protocol
+ * family
+ * @param group
+ * The group to which the newly constructed channel should be bound,
+ * or {@code null} for the default group
+ *
+ * @return A new asynchronous datagram channel
+ *
+ * @throws UnsupportedOperationException
+ * If the specified protocol family is not supported. For example,
+ * suppose the parameter is specified as {@link
+ * java.net.StandardProtocolFamily#INET6 INET6} but IPv6 is not
+ * enabled on the platform.
+ * @throws ShutdownChannelGroupException
+ * The specified group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousDatagramChannel open(ProtocolFamily family,
+ AsynchronousChannelGroup group)
+ throws IOException
+ {
+ AsynchronousChannelProvider provider = (group == null) ?
+ AsynchronousChannelProvider.provider() : group.provider();
+ return provider.openAsynchronousDatagramChannel(family, group);
+ }
+
+ /**
+ * Opens an asynchronous datagram channel.
+ *
+ * <p> This method returns an asynchronous datagram channel that is
+ * bound to the <em>default group</em>. This method is equivalent to evaluating
+ * the expression:
+ * <blockquote><pre>
+ * open((ProtocolFamily)null,&nbsp;(AsynchronousChannelGroup)null);
+ * </pre></blockquote>
+ *
+ * @return A new asynchronous datagram channel
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousDatagramChannel open()
+ throws IOException
+ {
+ return open(null, null);
+ }
+
+ // -- Socket-specific operations --
+
+ /**
+ * @throws AlreadyBoundException {@inheritDoc}
+ * @throws UnsupportedAddressTypeException {@inheritDoc}
+ * @throws ClosedChannelException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException
+ * If a security manager has been installed and its {@link
+ * SecurityManager#checkListen checkListen} method denies the
+ * operation
+ */
+ @Override
+ public abstract AsynchronousDatagramChannel bind(SocketAddress local)
+ throws IOException;
+
+ /**
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws ClosedChannelException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public abstract <T> AsynchronousDatagramChannel setOption(SocketOption<T> name, T value)
+ throws IOException;
+
+ /**
+ * Returns the remote address to which this channel is connected.
+ *
+ * <p> Where the channel is connected to an Internet Protocol socket address
+ * then the return value from this method is of type {@link
+ * java.net.InetSocketAddress}.
+ *
+ * @return The remote address; {@code null} if the channel's socket is not
+ * connected
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract SocketAddress getRemoteAddress() throws IOException;
+
+ /**
+ * Connects this channel's socket.
+ *
+ * <p> The channel's socket is configured so that it only receives
+ * datagrams from, and sends datagrams to, the given remote <i>peer</i>
+ * address. Once connected, datagrams may not be received from or sent to
+ * any other address. A datagram socket remains connected until it is
+ * explicitly disconnected or until it is closed.
+ *
+ * <p> This method performs exactly the same security checks as the {@link
+ * java.net.DatagramSocket#connect connect} method of the {@link
+ * java.net.DatagramSocket} class. That is, if a security manager has been
+ * installed then this method verifies that its {@link
+ * java.lang.SecurityManager#checkAccept checkAccept} and {@link
+ * java.lang.SecurityManager#checkConnect checkConnect} methods permit
+ * datagrams to be received from and sent to, respectively, the given
+ * remote address.
+ *
+ * <p> This method may be invoked at any time. Whether it has any effect
+ * on outstanding read or write operations is implementation specific and
+ * therefore not specified.
+ *
+ * @param remote
+ * The remote address to which this channel is to be connected
+ *
+ * @return This datagram channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws SecurityException
+ * If a security manager has been installed
+ * and it does not permit access to the given remote address
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract AsynchronousDatagramChannel connect(SocketAddress remote)
+ throws IOException;
+
+ /**
+ * Disconnects this channel's socket.
+ *
+ * <p> The channel's socket is configured so that it can receive datagrams
+ * from, and sends datagrams to, any remote address so long as the security
+ * manager, if installed, permits it.
+ *
+ * <p> This method may be invoked at any time. Whether it has any effect
+ * on outstanding read or write operations is implementation specific and
+ * therefore not specified.
+ *
+ * @return This datagram channel
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract AsynchronousDatagramChannel disconnect() throws IOException;
+
+ /**
+ * Receives a datagram via this channel.
+ *
+ * <p> This method initiates the receiving of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The {@code Future}'s {@link Future#get() get} method returns
+ * the source address of the datagram upon successful completion.
+ *
+ * <p> The datagram is transferred into the given byte buffer starting at
+ * its current position, as if by a regular {@link AsynchronousByteChannel#read
+ * read} operation. If there are fewer bytes remaining in the buffer
+ * than are required to hold the datagram then the remainder of the datagram
+ * is silently discarded.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then the operation completes with the exception {@link
+ * InterruptedByTimeoutException}. When a timeout elapses then the state of
+ * the {@link ByteBuffer} is not defined. The buffers should be discarded or
+ * at least care must be taken to ensure that the buffer is not accessed
+ * while the channel remains open.
+ *
+ * <p> When a security manager has been installed and the channel is not
+ * connected, then it verifies that the source's address and port number are
+ * permitted by the security manager's {@link SecurityManager#checkAccept
+ * checkAccept} method. The permission check is performed with privileges that
+ * are restricted by the calling context of this method. If the permission
+ * check fails then the operation completes with a {@link SecurityException}.
+ * The overhead of this security check can be avoided by first connecting the
+ * socket via the {@link #connect connect} method.
+ *
+ * @param dst
+ * The buffer into which the datagram is to be transferred
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the timeout is negative or the buffer is read-only
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<SocketAddress> receive(ByteBuffer dst,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<SocketAddress,? super A> handler);
+
+ /**
+ * Receives a datagram via this channel.
+ *
+ * <p> This method initiates the receiving of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The {@code Future}'s {@link Future#get() get} method returns
+ * the source address of the datagram upon successful completion.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #receive(ByteBuffer,long,TimeUnit,Object,CompletionHandler)} with a
+ * timeout of {@code 0L}.
+ *
+ * @param dst
+ * The buffer into which the datagram is to be transferred
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the buffer is read-only
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public final <A> Future<SocketAddress> receive(ByteBuffer dst,
+ A attachment,
+ CompletionHandler<SocketAddress,? super A> handler)
+ {
+ return receive(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * Receives a datagram via this channel.
+ *
+ * <p> This method initiates the receiving of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The {@code Future}'s {@link Future#get() get} method returns
+ * the source address of the datagram upon successful completion.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #receive(ByteBuffer,long,TimeUnit,Object,CompletionHandler)} with a
+ * timeout of {@code 0L}, and an attachment and completion handler
+ * of {@code null}.
+ *
+ * @param dst
+ * The buffer into which the datagram is to be transferred
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the buffer is read-only
+ */
+ public final <A> Future<SocketAddress> receive(ByteBuffer dst) {
+ return receive(dst, 0L, TimeUnit.MILLISECONDS, null, null);
+ }
+
+ /**
+ * Sends a datagram via this channel.
+ *
+ * <p> This method initiates sending of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The operation sends the remaining bytes in the given buffer as a single
+ * datagram to the given target address. The result of the operation, obtained
+ * by invoking the {@code Future}'s {@link Future#get() get}
+ * method, is the number of bytes sent.
+ *
+ * <p> The datagram is transferred from the byte buffer as if by a regular
+ * {@link AsynchronousByteChannel#write write} operation.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then the operation completes with the exception {@link
+ * InterruptedByTimeoutException}. When a timeout elapses then the state of
+ * the {@link ByteBuffer} is not defined. The buffers should be discarded or
+ * at least care must be taken to ensure that the buffer is not accessed
+ * while the channel remains open.
+ *
+ * <p> If there is a security manager installed and the the channel is not
+ * connected then this method verifies that the target address and port number
+ * are permitted by the security manager's {@link SecurityManager#checkConnect
+ * checkConnect} method. The overhead of this security check can be avoided
+ * by first connecting the socket via the {@link #connect connect} method.
+ *
+ * @param src
+ * The buffer containing the datagram to be sent
+ * @param target
+ * The address to which the datagram is to be sent
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws IllegalArgumentException
+ * If the timeout is negative, or if the channel's socket is
+ * connected to an address that is not equal to {@code target}
+ * @throws SecurityException
+ * If a security manager has been installed and it does not permit
+ * datagrams to be sent to the given address
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Integer> send(ByteBuffer src,
+ SocketAddress target,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * Sends a datagram via this channel.
+ *
+ * <p> This method initiates sending of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The operation sends the remaining bytes in the given buffer as a single
+ * datagram to the given target address. The result of the operation, obtained
+ * by invoking the {@code Future}'s {@link Future#get() get}
+ * method, is the number of bytes sent.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #send(ByteBuffer,SocketAddress,long,TimeUnit,Object,CompletionHandler)}
+ * with a timeout of {@code 0L}.
+ *
+ * @param src
+ * The buffer containing the datagram to be sent
+ * @param target
+ * The address to which the datagram is to be sent
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws IllegalArgumentException
+ * If the channel's socket is connected and is connected to an
+ * address that is not equal to {@code target}
+ * @throws SecurityException
+ * If a security manager has been installed and it does not permit
+ * datagrams to be sent to the given address
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public final <A> Future<Integer> send(ByteBuffer src,
+ SocketAddress target,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ return send(src, target, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * Sends a datagram via this channel.
+ *
+ * <p> This method initiates sending of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The operation sends the remaining bytes in the given buffer as a single
+ * datagram to the given target address. The result of the operation, obtained
+ * by invoking the {@code Future}'s {@link Future#get() get}
+ * method, is the number of bytes sent.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #send(ByteBuffer,SocketAddress,long,TimeUnit,Object,CompletionHandler)}
+ * with a timeout of {@code 0L} and an attachment and completion handler
+ * of {@code null}.
+ *
+ * @param src
+ * The buffer containing the datagram to be sent
+ * @param target
+ * The address to which the datagram is to be sent
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws IllegalArgumentException
+ * If the channel's socket is connected and is connected to an
+ * address that is not equal to {@code target}
+ * @throws SecurityException
+ * If a security manager has been installed and it does not permit
+ * datagrams to be sent to the given address
+ */
+ public final Future<Integer> send(ByteBuffer src, SocketAddress target) {
+ return send(src, target, 0L, TimeUnit.MILLISECONDS, null, null);
+ }
+
+ /**
+ * Receives a datagram via this channel.
+ *
+ * <p> This method initiates the receiving of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The {@code Future}'s {@link Future#get() get} method returns
+ * the number of bytes transferred upon successful completion.
+ *
+ * <p> This method may only be invoked if this channel is connected, and it
+ * only accepts datagrams from the peer that the channel is connected too.
+ * The datagram is transferred into the given byte buffer starting at
+ * its current position and exactly as specified in the {@link
+ * AsynchronousByteChannel} interface. If there are fewer bytes
+ * remaining in the buffer than are required to hold the datagram then the
+ * remainder of the datagram is silently discarded.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then the operation completes with the exception {@link
+ * InterruptedByTimeoutException}. When a timeout elapses then the state of
+ * the {@link ByteBuffer} is not defined. The buffers should be discarded or
+ * at least care must be taken to ensure that the buffer is not accessed
+ * while the channel remains open.
+ *
+ * @param dst
+ * The buffer into which the datagram is to be transferred
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the timeout is negative or buffer is read-only
+ * @throws NotYetConnectedException
+ * If this channel is not connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Integer> read(ByteBuffer dst,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * @throws NotYetConnectedException
+ * If this channel is not connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ @Override
+ public final <A> Future<Integer> read(ByteBuffer dst,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ return read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * @throws NotYetConnectedException
+ * If this channel is not connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ @Override
+ public final Future<Integer> read(ByteBuffer dst) {
+ return read(dst, 0L, TimeUnit.MILLISECONDS, null, null);
+ }
+
+ /**
+ * Writes a datagram to this channel.
+ *
+ * <p> This method initiates sending of a datagram, returning a
+ * {@code Future} representing the pending result of the operation.
+ * The operation sends the remaining bytes in the given buffer as a single
+ * datagram. The result of the operation, obtained by invoking the
+ * {@code Future}'s {@link Future#get() get} method, is the
+ * number of bytes sent.
+ *
+ * <p> The datagram is transferred from the byte buffer as if by a regular
+ * {@link AsynchronousByteChannel#write write} operation.
+ *
+ * <p> This method may only be invoked if this channel is connected,
+ * in which case it sends datagrams directly to the socket's peer. Otherwise
+ * it behaves exactly as specified in the {@link
+ * AsynchronousByteChannel} interface.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then the operation completes with the exception {@link
+ * InterruptedByTimeoutException}. When a timeout elapses then the state of
+ * the {@link ByteBuffer} is not defined. The buffers should be discarded or
+ * at least care must be taken to ensure that the buffer is not accessed
+ * while the channel remains open.
+ *
+ * @param src
+ * The buffer containing the datagram to be sent
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the timeout is negative
+ * @throws NotYetConnectedException
+ * If this channel is not connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Integer> write(ByteBuffer src,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+ /**
+ * @throws NotYetConnectedException
+ * If this channel is not connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ @Override
+ public final <A> Future<Integer> write(ByteBuffer src,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ return write(src, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * @throws NotYetConnectedException
+ * If this channel is not connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ @Override
+ public final Future<Integer> write(ByteBuffer src) {
+ return write(src, 0L, TimeUnit.MILLISECONDS, null, null);
+ }
+}
diff --git a/src/share/classes/java/nio/channels/AsynchronousFileChannel.java b/src/share/classes/java/nio/channels/AsynchronousFileChannel.java
new file mode 100644
index 000000000..a9bff5f16
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousFileChannel.java
@@ -0,0 +1,774 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.file.*;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.spi.*;
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ExecutorService;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * An asynchronous channel for reading, writing, and manipulating a file.
+ *
+ * <p> An asynchronous file channel is created when a file is opened by invoking
+ * one of the {@link #open open} methods defined by this class. The file contains
+ * a variable-length sequence of bytes that can be read and written and whose
+ * current size can be {@link #size() queried}. The size of the file increases
+ * when bytes are written beyond its current size; the size of the file decreases
+ * when it is {@link #truncate truncated}.
+ *
+ * <p> An asynchronous file channel does not have a <i>current position</i>
+ * within the file. Instead, the file position is specified to each read and
+ * write operation.
+ *
+ * <p> In addition to read and write operations, this class defines the
+ * following operations: </p>
+ *
+ * <ul>
+ *
+ * <li><p> Updates made to a file may be {@link #force <i>forced
+ * out</i>} to the underlying storage device, ensuring that data are not
+ * lost in the event of a system crash. </p></li>
+ *
+ * <li><p> A region of a file may be {@link FileLock <i>locked</i>}
+ * against access by other programs. </p></li>
+ *
+ * </ul>
+ *
+ * <p> The {@link #read read}, {@link #write write}, and {@link #lock lock}
+ * methods defined by this class are asynchronous and return a {@link Future}
+ * to represent the pending result of the operation. This may be used to check
+ * if the operation has completed, to wait for its completion, and to retrieve
+ * the result. These method may optionally specify a {@link CompletionHandler}
+ * that is invoked to consume the result of the I/O operation when it completes.
+ *
+ * <p> An {@code AsynchronousFileChannel} is associated with a thread pool to
+ * which tasks are submitted to handle I/O events and dispatch to completion
+ * handlers that consume the results of I/O operations on the channel. The
+ * completion handler for an I/O operation initiated on a channel is guaranteed
+ * to be invoked by one threads in the thread pool (This ensures that the
+ * completion handler is run by a thread with the expected <em>identity</em>).
+ * Where an I/O operation completes immediately, and the initiating thread is
+ * itself a thread in the thread pool, then the completion handler may be invoked
+ * directly by the initiating thread. When an {@code AsynchronousFileChannel} is
+ * created without specifying a thread pool then the channel is associated with
+ * a system-dependent and default thread pool that may be shared with other
+ * channels. The default thread pool is configured by the system properties
+ * defined by the {@link AsynchronousChannelGroup} class.
+ *
+ * <p> Channels of this type are safe for use by multiple concurrent threads. The
+ * {@link Channel#close close} method may be invoked at any time, as specified
+ * by the {@link Channel} interface. This causes all outstanding asynchronous
+ * operations on the channel to complete with the exception {@link
+ * AsynchronousCloseException}. Multiple read and write operations may be
+ * outstanding at the same time. When multiple read and write operations are
+ * outstanding then the ordering of the I/O operations, and the order that the
+ * completion handlers are invoked, is not specified; they are not, in particular,
+ * guaranteed to execute in the order that the operations were initiated. The
+ * {@link java.nio.ByteBuffer ByteBuffers} used when reading or writing are not
+ * safe for use by multiple concurrent I/O operations. Furthermore, after an I/O
+ * operation is initiated then care should be taken to ensure that the buffer is
+ * not accessed until after the operation has completed.
+ *
+ * <p> As with {@link FileChannel}, the view of a file provided by an instance of
+ * this class is guaranteed to be consistent with other views of the same file
+ * provided by other instances in the same program. The view provided by an
+ * instance of this class may or may not, however, be consistent with the views
+ * seen by other concurrently-running programs due to caching performed by the
+ * underlying operating system and delays induced by network-filesystem protocols.
+ * This is true regardless of the language in which these other programs are
+ * written, and whether they are running on the same machine or on some other
+ * machine. The exact nature of any such inconsistencies are system-dependent
+ * and are therefore unspecified.
+ *
+ * @since 1.7
+ */
+
+public abstract class AsynchronousFileChannel
+ implements AsynchronousChannel
+{
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AsynchronousFileChannel() {
+ }
+
+ /**
+ * Closes this channel.
+ *
+ * <p> If this channel is associated with its own thread pool then closing
+ * the channel causes the thread pool to shutdown after all actively
+ * executing completion handlers have completed. No attempt is made to stop
+ * or interrupt actively completion handlers.
+ *
+ * <p> This method otherwise behaves exactly as specified by the {@link
+ * AsynchronousChannel} interface.
+ *
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public abstract void close() throws IOException;
+
+ /**
+ * Opens or creates a file for reading and/or writing, returning an
+ * asynchronous file channel to access the file.
+ *
+ * <p> The {@code options} parameter determines how the file is opened.
+ * The {@link StandardOpenOption#READ READ} and {@link StandardOpenOption#WRITE
+ * WRITE} options determines if the file should be opened for reading and/or
+ * writing. If neither option is contained in the array then an existing file
+ * is opened for reading.
+ *
+ * <p> In addition to {@code READ} and {@code WRITE}, the following options
+ * may be present:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Option</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} </td>
+ * <td> When opening an existing file, the file is first truncated to a
+ * size of 0 bytes. This option is ignored when the file is opened only
+ * for reading.</td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#CREATE_NEW CREATE_NEW} </td>
+ * <td> If this option is present then a new file is created, failing if
+ * the file already exists. When creating a file the check for the
+ * existence of the file and the creation of the file if it does not exist
+ * is atomic with respect to other file system operations. This option is
+ * ignored when the file is opened only for reading. </td>
+ * </tr>
+ * <tr>
+ * <td > {@link StandardOpenOption#CREATE CREATE} </td>
+ * <td> If this option is present then an existing file is opened if it
+ * exists, otherwise a new file is created. When creating a file the check
+ * for the existence of the file and the creation of the file if it does
+ * not exist is atomic with respect to other file system operations. This
+ * option is ignored if the {@code CREATE_NEW} option is also present or
+ * the file is opened only for reading. </td>
+ * </tr>
+ * <tr>
+ * <td > {@link StandardOpenOption#DELETE_ON_CLOSE DELETE_ON_CLOSE} </td>
+ * <td> When this option is present then the implementation makes a
+ * <em>best effort</em> attempt to delete the file when closed by the
+ * the {@link #close close} method. If the {@code close} method is not
+ * invoked then a <em>best effort</em> attempt is made to delete the file
+ * when the Java virtual machine terminates. </td>
+ * </tr>
+ * <tr>
+ * <td>{@link StandardOpenOption#SPARSE SPARSE} </td>
+ * <td> When creating a new file this option is a <em>hint</em> that the
+ * new file will be sparse. This option is ignored when not creating
+ * a new file. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#SYNC SYNC} </td>
+ * <td> Requires that every update to the file's content or metadata be
+ * written synchronously to the underlying storage device. (see <a
+ * href="../file/package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * <tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#DSYNC DSYNC} </td>
+ * <td> Requires that every update to the file's content be written
+ * synchronously to the underlying storage device. (see <a
+ * href="../file/package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * </tr>
+ * </table>
+ *
+ * <p> An implementation may also support additional options.
+ *
+ * <p> The {@code executor} parameter is the {@link ExecutorService} to
+ * which tasks are submitted to handle I/O events and dispatch completion
+ * results for operations initiated on resulting channel.
+ * The nature of these tasks is highly implementation specific and so care
+ * should be taken when configuring the {@code Executor}. Minimally it
+ * should support an unbounded work queue and should not run tasks on the
+ * caller thread of the {@link ExecutorService#execute execute} method.
+ * {@link #close Closing} the channel results in the orderly {@link
+ * ExecutorService#shutdown shutdown} of the executor service. Shutting down
+ * the executor service by other means results in unspecified behavior.
+ *
+ * <p> The {@code attrs} parameter is an optional array of file {@link
+ * FileAttribute file-attributes} to set atomically when creating the file.
+ *
+ * <p> The new channel is created by invoking the {@link
+ * FileSystemProvider#newFileChannel newFileChannel} method on the
+ * provider that created the {@code Path}.
+ *
+ * @param file
+ * The path of the file to open or create
+ * @param options
+ * Options specifying how the file is opened
+ * @param executor
+ * The thread pool or {@code null} to associate the channel with
+ * the default thread pool
+ * @param attrs
+ * An optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return A new asynchronous file channel
+ *
+ * @throws IllegalArgumentException
+ * If the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * If the {@code file} is associated with a provider that does not
+ * support creating asynchronous file channels, or an unsupported
+ * open option is specified, or the array contains an attribute that
+ * cannot be set atomically when creating the file
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an
+ * unspecified permission required by the implementation.
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check
+ * read access if the file is opened for reading. The {@link
+ * SecurityManager#checkWrite(String)} method is invoked to check
+ * write access if the file is opened for writing
+ */
+ public static AsynchronousFileChannel open(Path file,
+ Set<? extends OpenOption> options,
+ ExecutorService executor,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ FileSystemProvider provider = file.getFileSystem().provider();
+ return provider.newAsynchronousFileChannel(file, options, executor, attrs);
+ }
+
+ private static final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];
+
+ /**
+ * Opens or creates a file for reading and/or writing, returning an
+ * asynchronous file channel to access the file.
+ *
+ * <p> An invocation of this method behaves in exactly the same way as the
+ * invocation
+ * <pre>
+ * ch.{@link #open(Path,Set,ExecutorService,FileAttribute[]) open}(file, opts, null, new FileAttribute&lt;?&gt;[0]);
+ * </pre>
+ * where {@code opts} is a {@code Set} containing the options specified to
+ * this method.
+ *
+ * <p> The resulting channel is associated with default thread pool to which
+ * tasks are submitted to handle I/O events and dispatch to completion
+ * handlers that consume the result of asynchronous operations performed on
+ * the resulting channel.
+ *
+ * @param file
+ * The path of the file to open or create
+ * @param options
+ * Options specifying how the file is opened
+ *
+ * @return A new asynchronous file channel
+ *
+ * @throws IllegalArgumentException
+ * If the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * If the {@code file} is associated with a provider that does not
+ * support creating file channels, or an unsupported open option is
+ * specified
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an
+ * unspecified permission required by the implementation.
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check
+ * read access if the file is opened for reading. The {@link
+ * SecurityManager#checkWrite(String)} method is invoked to check
+ * write access if the file is opened for writing
+ */
+ public static AsynchronousFileChannel open(Path file, OpenOption... options)
+ throws IOException
+ {
+ Set<OpenOption> set = new HashSet<OpenOption>(options.length);
+ Collections.addAll(set, options);
+ return open(file, set, null, NO_ATTRIBUTES);
+ }
+
+ /**
+ * Returns the current size of this channel's file.
+ *
+ * @return The current size of this channel's file, measured in bytes
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract long size() throws IOException;
+
+ /**
+ * Truncates this channel's file to the given size.
+ *
+ * <p> If the given size is less than the file's current size then the file
+ * is truncated, discarding any bytes beyond the new end of the file. If
+ * the given size is greater than or equal to the file's current size then
+ * the file is not modified. </p>
+ *
+ * @param size
+ * The new size, a non-negative byte count
+ *
+ * @return This file channel
+ *
+ * @throws NonWritableChannelException
+ * If this channel was not opened for writing
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IllegalArgumentException
+ * If the new size is negative
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract AsynchronousFileChannel truncate(long size) throws IOException;
+
+ /**
+ * Forces any updates to this channel's file to be written to the storage
+ * device that contains it.
+ *
+ * <p> If this channel's file resides on a local storage device then when
+ * this method returns it is guaranteed that all changes made to the file
+ * since this channel was created, or since this method was last invoked,
+ * will have been written to that device. This is useful for ensuring that
+ * critical information is not lost in the event of a system crash.
+ *
+ * <p> If the file does not reside on a local device then no such guarantee
+ * is made.
+ *
+ * <p> The {@code metaData} parameter can be used to limit the number of
+ * I/O operations that this method is required to perform. Passing
+ * {@code false} for this parameter indicates that only updates to the
+ * file's content need be written to storage; passing {@code true}
+ * indicates that updates to both the file's content and metadata must be
+ * written, which generally requires at least one more I/O operation.
+ * Whether this parameter actually has any effect is dependent upon the
+ * underlying operating system and is therefore unspecified.
+ *
+ * <p> Invoking this method may cause an I/O operation to occur even if the
+ * channel was only opened for reading. Some operating systems, for
+ * example, maintain a last-access time as part of a file's metadata, and
+ * this time is updated whenever the file is read. Whether or not this is
+ * actually done is system-dependent and is therefore unspecified.
+ *
+ * <p> This method is only guaranteed to force changes that were made to
+ * this channel's file via the methods defined in this class.
+ *
+ * @param metaData
+ * If {@code true} then this method is required to force changes
+ * to both the file's content and metadata to be written to
+ * storage; otherwise, it need only force content changes to be
+ * written
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract void force(boolean metaData) throws IOException;
+
+ /**
+ * Acquires a lock on the given region of this channel's file.
+ *
+ * <p> This method initiates an operation to acquire a lock on the given region
+ * of this channel's file. The method returns a {@code Future} representing
+ * the pending result of the operation. Its {@link Future#get() get}
+ * method returns the {@link FileLock} on successful completion.
+ *
+ * <p> The region specified by the {@code position} and {@code size}
+ * parameters need not be contained within, or even overlap, the actual
+ * underlying file. Lock regions are fixed in size; if a locked region
+ * initially contains the end of the file and the file grows beyond the
+ * region then the new portion of the file will not be covered by the lock.
+ * If a file is expected to grow in size and a lock on the entire file is
+ * required then a region starting at zero, and no smaller than the
+ * expected maximum size of the file, should be locked. The two-argument
+ * {@link #lock(Object,CompletionHandler)} method simply locks a region
+ * of size {@link Long#MAX_VALUE}. If a lock that overlaps the requested
+ * region is already held by this Java virtual machine, or this method has
+ * been invoked to lock an overlapping region and that operation has not
+ * completed, then this method throws {@link OverlappingFileLockException}.
+ *
+ * <p> Some operating systems do not support a mechanism to acquire a file
+ * lock in an asynchronous manner. Consequently an implementation may
+ * acquire the file lock in a background thread or from a task executed by
+ * a thread in the associated thread pool. If there are many lock operations
+ * outstanding then it may consume threads in the Java virtual machine for
+ * indefinite periods.
+ *
+ * <p> Some operating systems do not support shared locks, in which case a
+ * request for a shared lock is automatically converted into a request for
+ * an exclusive lock. Whether the newly-acquired lock is shared or
+ * exclusive may be tested by invoking the resulting lock object's {@link
+ * FileLock#isShared() isShared} method.
+ *
+ * <p> File locks are held on behalf of the entire Java virtual machine.
+ * They are not suitable for controlling access to a file by multiple
+ * threads within the same virtual machine.
+ *
+ * @param position
+ * The position at which the locked region is to start; must be
+ * non-negative
+ * @param size
+ * The size of the locked region; must be non-negative, and the sum
+ * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative
+ * @param shared
+ * {@code true} to request a shared lock, in which case this
+ * channel must be open for reading (and possibly writing);
+ * {@code false} to request an exclusive lock, in which case this
+ * channel must be open for writing (and possibly reading)
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws OverlappingFileLockException
+ * If a lock that overlaps the requested region is already held by
+ * this Java virtual machine, or there is already a pending attempt
+ * to lock an overlapping region
+ * @throws IllegalArgumentException
+ * If the preconditions on the parameters do not hold
+ * @throws NonReadableChannelException
+ * If {@code shared} is true this channel but was not opened for reading
+ * @throws NonWritableChannelException
+ * If {@code shared} is false but this channel was not opened for writing
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, the channel is closed, and the channel
+ * was originally created with its own thread pool
+ */
+ public abstract <A> Future<FileLock> lock(long position,
+ long size,
+ boolean shared,
+ A attachment,
+ CompletionHandler<FileLock,? super A> handler);
+
+ /**
+ * Acquires an exclusive lock on this channel's file.
+ *
+ * <p> This method initiates an operation to acquire an exclusive lock on this
+ * channel's file. The method returns a {@code Future} representing
+ * the pending result of the operation. Its {@link Future#get() get}
+ * method returns the {@link FileLock} on successful completion.
+ *
+ * <p> An invocation of this method of the form {@code ch.lock(att,handler)}
+ * behaves in exactly the same way as the invocation
+ * <pre>
+ * ch.{@link #lock(long,long,boolean,Object,CompletionHandler) lock}(0L, Long.MAX_VALUE, false, att, handler)
+ * </pre>
+ *
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return a {@code Future} object representing the pending result
+ *
+ * @throws OverlappingFileLockException
+ * If a lock is already held by this Java virtual machine, or there
+ * is already a pending attempt to lock a region
+ * @throws NonWritableChannelException
+ * If this channel was not opened for writing
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, the channel is closed, and the channel
+ * was originally created with its own thread pool
+ */
+ public final <A> Future<FileLock> lock(A attachment,
+ CompletionHandler<FileLock,? super A> handler)
+ {
+ return lock(0L, Long.MAX_VALUE, false, attachment, handler);
+ }
+
+ /**
+ * Acquires an exclusive lock on this channel's file.
+ *
+ * <p> This method initiates an operation to acquire an exclusive lock on this
+ * channel's file. The method returns a {@code Future} representing the
+ * pending result of the operation. Its {@link Future#get() get} method
+ * returns the {@link FileLock} on successful completion.
+ *
+ * <p> An invocation of this method behaves in exactly the same way as the
+ * invocation
+ * <pre>
+ * ch.{@link #lock(long,long,boolean,Object,CompletionHandler) lock}(0L, Long.MAX_VALUE, false, null, null)
+ * </pre>
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws OverlappingFileLockException
+ * If a lock is already held by this Java virtual machine, or there
+ * is already a pending attempt to lock a region
+ * @throws NonWritableChannelException
+ * If this channel was not opened for writing
+ */
+ public final Future<FileLock> lock() {
+ return lock(0L, Long.MAX_VALUE, false, null, null);
+ }
+
+ /**
+ * Attempts to acquire a lock on the given region of this channel's file.
+ *
+ * <p> This method does not block. An invocation always returns immediately,
+ * either having acquired a lock on the requested region or having failed to
+ * do so. If it fails to acquire a lock because an overlapping lock is held
+ * by another program then it returns {@code null}. If it fails to acquire
+ * a lock for any other reason then an appropriate exception is thrown.
+ *
+ * @param position
+ * The position at which the locked region is to start; must be
+ * non-negative
+ *
+ * @param size
+ * The size of the locked region; must be non-negative, and the sum
+ * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative
+ *
+ * @param shared
+ * {@code true} to request a shared lock,
+ * {@code false} to request an exclusive lock
+ *
+ * @return A lock object representing the newly-acquired lock,
+ * or {@code null} if the lock could not be acquired
+ * because another program holds an overlapping lock
+ *
+ * @throws IllegalArgumentException
+ * If the preconditions on the parameters do not hold
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws OverlappingFileLockException
+ * If a lock that overlaps the requested region is already held by
+ * this Java virtual machine, or if another thread is already
+ * blocked in this method and is attempting to lock an overlapping
+ * region of the same file
+ * @throws NonReadableChannelException
+ * If {@code shared} is true this channel but was not opened for reading
+ * @throws NonWritableChannelException
+ * If {@code shared} is false but this channel was not opened for writing
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ *
+ * @see #lock(Object,CompletionHandler)
+ * @see #lock(long,long,boolean,Object,CompletionHandler)
+ * @see #tryLock()
+ */
+ public abstract FileLock tryLock(long position, long size, boolean shared)
+ throws IOException;
+
+ /**
+ * Attempts to acquire an exclusive lock on this channel's file.
+ *
+ * <p> An invocation of this method of the form {@code ch.tryLock()}
+ * behaves in exactly the same way as the invocation
+ *
+ * <pre>
+ * ch.{@link #tryLock(long,long,boolean) tryLock}(0L, Long.MAX_VALUE, false) </pre>
+ *
+ * @return A lock object representing the newly-acquired lock,
+ * or {@code null} if the lock could not be acquired
+ * because another program holds an overlapping lock
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws OverlappingFileLockException
+ * If a lock that overlaps the requested region is already held by
+ * this Java virtual machine, or if another thread is already
+ * blocked in this method and is attempting to lock an overlapping
+ * region
+ * @throws NonWritableChannelException
+ * If {@code shared} is false but this channel was not opened for writing
+ *
+ * @throws IOException
+ * If some other I/O error occurs
+ *
+ * @see #lock(Object,CompletionHandler)
+ * @see #lock(long,long,boolean,Object,CompletionHandler)
+ * @see #tryLock(long,long,boolean)
+ */
+ public final FileLock tryLock() throws IOException {
+ return tryLock(0L, Long.MAX_VALUE, false);
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer,
+ * starting at the given file position.
+ *
+ * <p> This method initiates the reading of a sequence of bytes from this
+ * channel into the given buffer, starting at the given file position. This
+ * method returns a {@code Future} representing the pending result of the
+ * operation. The Future's {@link Future#get() get} method returns the
+ * number of bytes read or {@code -1} if the given position is greater than
+ * or equal to the file's size at the time that the read is attempted.
+ *
+ * <p> This method works in the same manner as the {@link
+ * AsynchronousByteChannel#read(ByteBuffer,Object,CompletionHandler)}
+ * method, except that bytes are read starting at the given file position.
+ * If the given file position is greater than the file's size at the time
+ * that the read is attempted then no bytes are read.
+ *
+ * @param dst
+ * The buffer into which bytes are to be transferred
+ * @param position
+ * The file position at which the transfer is to begin;
+ * must be non-negative
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the position is negative or the buffer is read-only
+ * @throws NonReadableChannelException
+ * If this channel was not opened for reading
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, the channel is closed, and the channel
+ * was originally created with its own thread pool
+ */
+ public abstract <A> Future<Integer> read(ByteBuffer dst,
+ long position,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer,
+ * starting at the given file position.
+ *
+ * <p> This method initiates the reading of a sequence of bytes from this
+ * channel into the given buffer, starting at the given file position. This
+ * method returns a {@code Future} representing the pending result of the
+ * operation. The Future's {@link Future#get() get} method returns the
+ * number of bytes read or {@code -1} if the given position is greater
+ * than or equal to the file's size at the time that the read is attempted.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #read(ByteBuffer,long,Object,CompletionHandler)} with the {@code attachment}
+ * and handler parameters set to {@code null}.
+ *
+ * @param dst
+ * The buffer into which bytes are to be transferred
+ * @param position
+ * The file position at which the transfer is to begin;
+ * must be non-negative
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the position is negative or the buffer is read-only
+ * @throws NonReadableChannelException
+ * If this channel was not opened for reading
+ */
+ public final Future<Integer> read(ByteBuffer dst, long position) {
+ return read(dst, position, null, null);
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer, starting
+ * at the given file position.
+ *
+ * <p> This method initiates the writing of a sequence of bytes to this channel
+ * from the given buffer, starting at the given file position. The method
+ * returns a {@code Future} representing the pending result of the write
+ * operation. The Future's {@link Future#get() get} method returns the
+ * number of bytes written.
+ *
+ * <p> This method works in the same manner as the {@link
+ * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)}
+ * method, except that bytes are written starting at the given file position.
+ * If the given position is greater than the file's size, at the time that
+ * the write is attempted, then the file will be grown to accommodate the new
+ * bytes; the values of any bytes between the previous end-of-file and the
+ * newly-written bytes are unspecified.
+ *
+ * @param src
+ * The buffer from which bytes are to be transferred
+ * @param position
+ * The file position at which the transfer is to begin;
+ * must be non-negative
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the position is negative
+ * @throws NonWritableChannelException
+ * If this channel was not opened for writing
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, the channel is closed, and the channel
+ * was originally created with its own thread pool
+ */
+ public abstract <A> Future<Integer> write(ByteBuffer src,
+ long position,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer, starting
+ * at the given file position.
+ *
+ * <p> This method initiates the writing of a sequence of bytes to this channel
+ * from the given buffer, starting at the given file position. The method
+ * returns a {@code Future} representing the pending result of the write
+ * operation. The Future's {@link Future#get() get} method returns the
+ * number of bytes written.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #write(ByteBuffer,long,Object,CompletionHandler)} with the {@code attachment}
+ * and handler parameters set to {@code null}.
+ *
+ * @param src
+ * The buffer from which bytes are to be transferred
+ * @param position
+ * The file position at which the transfer is to begin;
+ * must be non-negative
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the position is negative
+ * @throws NonWritableChannelException
+ * If this channel was not opened for writing
+ */
+ public final Future<Integer> write(ByteBuffer src, long position) {
+ return write(src, position, null, null);
+ }
+}
diff --git a/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java b/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java
new file mode 100644
index 000000000..99c56fa59
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.channels.spi.*;
+import java.net.SocketOption;
+import java.net.SocketAddress;
+import java.util.concurrent.Future;
+import java.io.IOException;
+
+/**
+ * An asynchronous channel for stream-oriented listening sockets.
+ *
+ * <p> An asynchronous server-socket channel is created by invoking the
+ * {@link #open open} method of this class.
+ * A newly-created asynchronous server-socket channel is open but not yet bound.
+ * It can be bound to a local address and configured to listen for connections
+ * by invoking the {@link #bind(SocketAddress,int) bind} method. Once bound,
+ * the {@link #accept(Object,CompletionHandler) accept} method
+ * is used to initiate the accepting of connections to the channel's socket.
+ * An attempt to invoke the <tt>accept</tt> method on an unbound channel will
+ * cause a {@link NotYetBoundException} to be thrown.
+ *
+ * <p> Channels of this type are safe for use by multiple concurrent threads
+ * though at most one accept operation can be outstanding at any time.
+ * If a thread initiates an accept operation before a previous accept operation
+ * has completed then an {@link AcceptPendingException} will be thrown.
+ *
+ * <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
+ * setOption} method. Channels of this type support the following options:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td>
+ * <td> The size of the socket receive buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td>
+ * <td> Re-use address </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported.
+ *
+ * <p> <b>Usage Example:</b>
+ * <pre>
+ * final AsynchronousServerSocketChannel listener =
+ * AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
+ *
+ * listener.accept(null, new CompletionHandler&lt;AsynchronousSocketChannel,Void&gt;() {
+ * public void completed(AsynchronousSocketChannel ch, Void att) {
+ * // accept the next connection
+ * listener.accept(null, this);
+ *
+ * // handle this connection
+ * handle(ch);
+ * }
+ * public void failed(Throwable exc, Void att) {
+ * ...
+ * }
+ * public void cancelled(Void att) {
+ * ...
+ * }
+ * });
+ * </pre>
+ *
+ * @since 1.7
+ */
+
+public abstract class AsynchronousServerSocketChannel
+ implements AsynchronousChannel, NetworkChannel
+{
+ private final AsynchronousChannelProvider provider;
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AsynchronousServerSocketChannel(AsynchronousChannelProvider provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Returns the provider that created this channel.
+ */
+ public final AsynchronousChannelProvider provider() {
+ return provider;
+ }
+
+ /**
+ * Opens an asynchronous server-socket channel.
+ *
+ * <p> The new channel is created by invoking the {@link
+ * java.nio.channels.spi.AsynchronousChannelProvider#openAsynchronousServerSocketChannel
+ * openAsynchronousServerSocketChannel} method on the {@link
+ * java.nio.channels.spi.AsynchronousChannelProvider} object that created
+ * the given group. If the group parameter is <tt>null</tt> then the
+ * resulting channel is created by the system-wide default provider, and
+ * bound to the <em>default group</em>.
+ *
+ * @param group
+ * The group to which the newly constructed channel should be bound,
+ * or <tt>null</tt> for the default group
+ *
+ * @return A new asynchronous server socket channel
+ *
+ * @throws ShutdownChannelGroupException
+ * If the channel group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousServerSocketChannel open(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ AsynchronousChannelProvider provider = (group == null) ?
+ AsynchronousChannelProvider.provider() : group.provider();
+ return provider.openAsynchronousServerSocketChannel(group);
+ }
+
+ /**
+ * Opens an asynchronous server-socket channel.
+ *
+ * <p> This method returns an asynchronous server socket channel that is
+ * bound to the <em>default group</em>. This method is equivalent to evaluating
+ * the expression:
+ * <blockquote><pre>
+ * open((AsynchronousChannelGroup)null);
+ * </pre></blockquote>
+ *
+ * @return A new asynchronous server socket channel
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousServerSocketChannel open()
+ throws IOException
+ {
+ return open(null);
+ }
+
+ /**
+ * Binds the channel's socket to a local address and configures the socket to
+ * listen for connections.
+ *
+ * <p> An invocation of this method is equivalent to the following:
+ * <blockquote><pre>
+ * bind(local, 0);
+ * </pre></blockquote>
+ *
+ * @param local
+ * The local address to bind the socket, or <tt>null</tt> to bind
+ * to an automatically assigned socket address
+ *
+ * @return This channel
+ *
+ * @throws AlreadyBoundException {@inheritDoc}
+ * @throws UnsupportedAddressTypeException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ * @throws ClosedChannelException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ public final AsynchronousServerSocketChannel bind(SocketAddress local)
+ throws IOException
+ {
+ return bind(local, 0);
+ }
+
+ /**
+ * Binds the channel's socket to a local address and configures the socket to
+ * listen for connections.
+ *
+ * <p> This method is used to establish an association between the socket and
+ * a local address. Once an association is established then the socket remains
+ * bound until the associated channel is closed.
+ *
+ * <p> The {@code backlog} parameter is the maximum number of pending
+ * connections on the socket. Its exact semantics are implementation specific.
+ * In particular, an implementation may impose a maximum length or may choose
+ * to ignore the parameter altogther. If the {@code backlog} parameter has
+ * the value {@code 0}, or a negative value, then an implementation specific
+ * default is used.
+ *
+ * @param local
+ * The local address to bind the socket, or {@code null} to bind
+ * to an automatically assigned socket address
+ * @param backlog
+ * The maximum number of pending connections
+ *
+ * @return This channel
+ *
+ * @throws AlreadyBoundException
+ * If the socket is already bound
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ * @throws SecurityException
+ * If a security manager has been installed and its {@link
+ * SecurityManager#checkListen checkListen} method denies the operation
+ * @throws ClosedChannelException
+ * If the channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract AsynchronousServerSocketChannel bind(SocketAddress local, int backlog)
+ throws IOException;
+
+ /**
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws ClosedChannelException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ public abstract <T> AsynchronousServerSocketChannel setOption(SocketOption<T> name, T value)
+ throws IOException;
+
+ /**
+ * Accepts a connection.
+ *
+ * <p> This method initiates accepting a connection made to this channel's
+ * socket, returning a {@link Future} representing the pending result
+ * of the operation. The {@code Future}'s {@link Future#get() get}
+ * method will return the {@link AsynchronousSocketChannel} for the new
+ * connection on successful completion.
+ *
+ * <p> When a new connection is accepted then the resulting {@code
+ * AsynchronousSocketChannel} will be bound to the same {@link
+ * AsynchronousChannelGroup} as this channel. If the group is {@link
+ * AsynchronousChannelGroup#isShutdown shutdown} and a connection is accepted,
+ * then the connection is closed, and the operation completes with an {@code
+ * IOException} and cause {@link ShutdownChannelGroupException}.
+ *
+ * <p> To allow for concurrent handling of new connections, the completion
+ * handler is not invoked directly by the initiating thread when a new
+ * connection is accepted immediately (see <a
+ * href="AsynchronousChannelGroup.html#threading">Threading<a>).
+ *
+ * <p> If a security manager has been installed then it verifies that the
+ * address and port number of the connection's remote endpoint are permitted
+ * by the security manager's {@link SecurityManager#checkAccept checkAccept}
+ * method. The permission check is performed with privileges that are restricted
+ * by the calling context of this method. If the permission check fails then
+ * the connection is closed and the operation completes with a {@link
+ * SecurityException}.
+ *
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return an <tt>Future</tt> object representing the pending result
+ *
+ * @throws AcceptPendingException
+ * If an accept operation is already in progress on this channel
+ * @throws NotYetBoundException
+ * If this channel's socket has not yet been bound
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<AsynchronousSocketChannel>
+ accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler);
+
+ /**
+ * Accepts a connection.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #accept(Object,CompletionHandler)} with the {@code attachment}
+ * and {@code handler} parameters set to {@code null}.
+ *
+ * @return an <tt>Future</tt> object representing the pending result
+ *
+ * @throws AcceptPendingException
+ * If an accept operation is already in progress on this channel
+ * @throws NotYetBoundException
+ * If this channel's socket has not yet been bound
+ */
+ public final Future<AsynchronousSocketChannel> accept() {
+ return accept(null, null);
+ }
+}
diff --git a/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java b/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java
new file mode 100644
index 000000000..b6a5da810
--- /dev/null
+++ b/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.channels.spi.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.Future;
+import java.io.IOException;
+import java.net.SocketOption;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * An asynchronous channel for stream-oriented connecting sockets.
+ *
+ * <p> Asynchronous socket channels are created in one of two ways. A newly-created
+ * {@code AsynchronousSocketChannel} is created by invoking one of the {@link
+ * #open open} methods defined by this class. A newly-created channel is open but
+ * not yet connected. A connected {@code AsynchronousSocketChannel} is created
+ * when a connection is made to the socket of an {@link AsynchronousServerSocketChannel}.
+ * It is not possible to create an asynchronous socket channel for an arbitrary,
+ * pre-existing {@link java.net.Socket socket}.
+ *
+ * <p> A newly-created channel is connected by invoking its {@link #connect connect}
+ * method; once connected, a channel remains connected until it is closed. Whether
+ * or not a socket channel is connected may be determined by invoking its {@link
+ * #getRemoteAddress getRemoteAddress} method. An attempt to invoke an I/O
+ * operation upon an unconnected channel will cause a {@link NotYetConnectedException}
+ * to be thrown.
+ *
+ * <p> Channels of this type are safe for use by multiple concurrent threads.
+ * They support concurrent reading and writing, though at most one read operation
+ * and one write operation can be outstanding at any time.
+ * If a thread initiates a read operation before a previous read operation has
+ * completed then a {@link ReadPendingException} will be thrown. Similarly, an
+ * attempt to initiate a write operation before a previous write has completed
+ * will throw a {@link WritePendingException}.
+ *
+ * <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
+ * setOption} method. Asynchronous socket channels support the following options:
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} </td>
+ * <td> The size of the socket send buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td>
+ * <td> The size of the socket receive buffer </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} </td>
+ * <td> Keep connection alive </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td>
+ * <td> Re-use address </td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} </td>
+ * <td> Disable the Nagle algorithm </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported.
+ *
+ * <h4>Timeouts</h4>
+ *
+ * <p> The {@link #read(ByteBuffer,long,TimeUnit,Object,CompletionHandler) read}
+ * and {@link #write(ByteBuffer,long,TimeUnit,Object,CompletionHandler) write}
+ * methods defined by this class allow a timeout to be specified when initiating
+ * a read or write operation. If the timeout elapses before an operation completes
+ * then the operation completes with the exception {@link
+ * InterruptedByTimeoutException}. A timeout may leave the channel, or the
+ * underlying connection, in an inconsistent state. Where the implementation
+ * cannot guarantee that bytes have not been read from the channel then it puts
+ * the channel into an implementation specific <em>error state</em>. A subsequent
+ * attempt to initiate a {@code read} operation causes an unspecified runtime
+ * exception to be thrown. Similarly if a {@code write} operation times out and
+ * the implementation cannot guarantee bytes have not been written to the
+ * channel then further attempts to {@code write} to the channel cause an
+ * unspecified runtime exception to be thrown. When a timeout elapses then the
+ * state of the {@link ByteBuffer}, or the sequence of buffers, for the I/O
+ * operation is not defined. Buffers should be discarded or at least care must
+ * be taken to ensure that the buffers are not accessed while the channel remains
+ * open.
+ *
+ * @since 1.7
+ */
+
+public abstract class AsynchronousSocketChannel
+ implements AsynchronousByteChannel, NetworkChannel
+{
+ private final AsynchronousChannelProvider provider;
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AsynchronousSocketChannel(AsynchronousChannelProvider provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Returns the provider that created this channel.
+ */
+ public final AsynchronousChannelProvider provider() {
+ return provider;
+ }
+
+ /**
+ * Opens an asynchronous socket channel.
+ *
+ * <p> The new channel is created by invoking the {@link
+ * AsynchronousChannelProvider#openAsynchronousSocketChannel
+ * openAsynchronousSocketChannel} method on the {@link
+ * AsynchronousChannelProvider} that created the group. If the group parameter
+ * is {@code null} then the resulting channel is created by the system-wide
+ * default provider, and bound to the <em>default group</em>.
+ *
+ * @param group
+ * The group to which the newly constructed channel should be bound,
+ * or {@code null} for the default group
+ *
+ * @return A new asynchronous socket channel
+ *
+ * @throws ShutdownChannelGroupException
+ * If the channel group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousSocketChannel open(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ AsynchronousChannelProvider provider = (group == null) ?
+ AsynchronousChannelProvider.provider() : group.provider();
+ return provider.openAsynchronousSocketChannel(group);
+ }
+
+ /**
+ * Opens an asynchronous socket channel.
+ *
+ * <p> This method returns an asynchronous socket channel that is bound to
+ * the <em>default group</em>.This method is equivalent to evaluating the
+ * expression:
+ * <blockquote><pre>
+ * open((AsynchronousChannelGroup)null);
+ * </pre></blockquote>
+ *
+ * @return A new asynchronous socket channel
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static AsynchronousSocketChannel open()
+ throws IOException
+ {
+ return open(null);
+ }
+
+
+ // -- socket options and related --
+
+ /**
+ * @throws ConnectionPendingException
+ * If a connection operation is already in progress on this channel
+ * @throws AlreadyBoundException {@inheritDoc}
+ * @throws UnsupportedAddressTypeException {@inheritDoc}
+ * @throws ClosedChannelException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public abstract AsynchronousSocketChannel bind(SocketAddress local)
+ throws IOException;
+
+ /**
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws ClosedChannelException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public abstract <T> AsynchronousSocketChannel setOption(SocketOption<T> name, T value)
+ throws IOException;
+
+ /**
+ * Shutdown the connection for reading without closing the channel.
+ *
+ * <p> Once shutdown for reading then further reads on the channel will
+ * return {@code -1}, the end-of-stream indication. If the input side of the
+ * connection is already shutdown then invoking this method has no effect.
+ * The effect on an outstanding read operation is system dependent and
+ * therefore not specified. The effect, if any, when there is data in the
+ * socket receive buffer that has not been read, or data arrives subsequently,
+ * is also system dependent.
+ *
+ * @return The channel
+ *
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract AsynchronousSocketChannel shutdownInput() throws IOException;
+
+ /**
+ * Shutdown the connection for writing without closing the channel.
+ *
+ * <p> Once shutdown for writing then further attempts to write to the
+ * channel will throw {@link ClosedChannelException}. If the output side of
+ * the connection is already shutdown then invoking this method has no
+ * effect. The effect on an outstanding write operation is system dependent
+ * and therefore not specified.
+ *
+ * @return The channel
+ *
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public abstract AsynchronousSocketChannel shutdownOutput() throws IOException;
+
+ // -- state --
+
+ /**
+ * Returns the remote address to which this channel's socket is connected.
+ *
+ * <p> Where the channel is bound and connected to an Internet Protocol
+ * socket address then the return value from this method is of type {@link
+ * java.net.InetSocketAddress}.
+ *
+ * @return The remote address; {@code null} if the channel's socket is not
+ * connected
+ *
+ * @throws ClosedChannelException
+ * If the channel is closed
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract SocketAddress getRemoteAddress() throws IOException;
+
+ // -- asynchronous operations --
+
+ /**
+ * Connects this channel.
+ *
+ * <p> This method initiates an operation to connect this channel, returning
+ * a {@code Future} representing the pending result of the operation. If
+ * the connection is successfully established then the {@code Future}'s
+ * {@link Future#get() get} method will return {@code null}. If the
+ * connection cannot be established then the channel is closed. In that case,
+ * invoking the {@code get} method throws {@link
+ * java.util.concurrent.ExecutionException} with an {@code IOException} as
+ * the cause.
+ *
+ * <p> This method performs exactly the same security checks as the {@link
+ * java.net.Socket} class. That is, if a security manager has been
+ * installed then this method verifies that its {@link
+ * java.lang.SecurityManager#checkConnect checkConnect} method permits
+ * connecting to the address and port number of the given remote endpoint.
+ *
+ * @param remote
+ * The remote address to which this channel is to be connected
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws AlreadyConnectedException
+ * If this channel is already connected
+ * @throws ConnectionPendingException
+ * If a connection operation is already in progress on this channel
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ * @throws SecurityException
+ * If a security manager has been installed
+ * and it does not permit access to the given remote endpoint
+ *
+ * @see #getRemoteAddress
+ */
+ public abstract <A> Future<Void> connect(SocketAddress remote,
+ A attachment,
+ CompletionHandler<Void,? super A> handler);
+
+ /**
+ * Connects this channel.
+ *
+ * <p> This method is equivalent to invoking {@link
+ * #connect(SocketAddress,Object,CompletionHandler)} with the {@code attachment}
+ * and handler parameters set to {@code null}.
+ *
+ * @param remote
+ * The remote address to which this channel is to be connected
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws AlreadyConnectedException
+ * If this channel is already connected
+ * @throws ConnectionPendingException
+ * If a connection operation is already in progress on this channel
+ * @throws SecurityException
+ * If a security manager has been installed
+ * and it does not permit access to the given remote endpoint
+ */
+ public final Future<Void> connect(SocketAddress remote) {
+ return connect(remote, null, null);
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * <p> This method initiates the reading of a sequence of bytes from this
+ * channel into the given buffer, returning a {@code Future} representing
+ * the pending result of the operation. The {@code Future}'s {@link
+ * Future#get() get} method returns the number of bytes read or {@code -1}
+ * if all bytes have been read and channel has reached end-of-stream.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then the operation completes with the exception {@link
+ * InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been read, or will not
+ * be read from the channel into the given buffer, then further attempts to
+ * read from the channel will cause an unspecific runtime exception to be
+ * thrown.
+ *
+ * <p> Otherwise this method works in the same manner as the {@link
+ * AsynchronousByteChannel#read(ByteBuffer,Object,CompletionHandler)}
+ * method.
+ *
+ * @param dst
+ * The buffer into which bytes are to be transferred
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the {@code timeout} parameter is negative or the buffer is
+ * read-only
+ * @throws ReadPendingException
+ * If a read operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Integer> read(ByteBuffer dst,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws ReadPendingException {@inheritDoc}
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ @Override
+ public final <A> Future<Integer> read(ByteBuffer dst,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ return read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws ReadPendingException {@inheritDoc}
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ */
+ @Override
+ public final Future<Integer> read(ByteBuffer dst) {
+ return read(dst, 0L, TimeUnit.MILLISECONDS, null, null);
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into a subsequence of the
+ * given buffers. This operation, sometimes called a <em>scattering read</em>,
+ * is often useful when implementing network protocols that group data into
+ * segments consisting of one or more fixed-length headers followed by a
+ * variable-length body.
+ *
+ * <p> This method initiates a read of up to <i>r</i> bytes from this channel,
+ * where <i>r</i> is the total number of bytes remaining in the specified
+ * subsequence of the given buffer array, that is,
+ *
+ * <blockquote><pre>
+ * dsts[offset].remaining()
+ * + dsts[offset+1].remaining()
+ * + ... + dsts[offset+length-1].remaining()</pre></blockquote>
+ *
+ * at the moment that the read is attempted.
+ *
+ * <p> Suppose that a byte sequence of length <i>n</i> is read, where
+ * <tt>0</tt>&nbsp;<tt>&lt;</tt>&nbsp;<i>n</i>&nbsp;<tt>&lt;=</tt>&nbsp;<i>r</i>.
+ * Up to the first <tt>dsts[offset].remaining()</tt> bytes of this sequence
+ * are transferred into buffer <tt>dsts[offset]</tt>, up to the next
+ * <tt>dsts[offset+1].remaining()</tt> bytes are transferred into buffer
+ * <tt>dsts[offset+1]</tt>, and so forth, until the entire byte sequence
+ * is transferred into the given buffers. As many bytes as possible are
+ * transferred into each buffer, hence the final position of each updated
+ * buffer, except the last updated buffer, is guaranteed to be equal to
+ * that buffer's limit. The underlying operating system may impose a limit
+ * on the number of buffers that may be used in an I/O operation. Where the
+ * number of buffers (with bytes remaining), exceeds this limit, then the
+ * I/O operation is performed with the maximum number of buffers allowed by
+ * the operating system.
+ *
+ * <p> The return value from this method is a {@code Future} representing
+ * the pending result of the operation. The {@code Future}'s {@link
+ * Future#get() get} method returns the number of bytes read or {@code -1L}
+ * if all bytes have been read and the channel has reached end-of-stream.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then it completes with the exception {@link
+ * InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been read, or will not
+ * be read from the channel into the given buffers, then further attempts to
+ * read from the channel will cause an unspecific runtime exception to be
+ * thrown.
+ *
+ * @param dsts
+ * The buffers into which bytes are to be transferred
+ * @param offset
+ * The offset within the buffer array of the first buffer into which
+ * bytes are to be transferred; must be non-negative and no larger than
+ * {@code dsts.length}
+ * @param length
+ * The maximum number of buffers to be accessed; must be non-negative
+ * and no larger than {@code dsts.length - offset}
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IndexOutOfBoundsException
+ * If the pre-conditions for the {@code offset} and {@code length}
+ * parameter aren't met
+ * @throws IllegalArgumentException
+ * If the {@code timeout} parameter is negative, or a buffer is
+ * read-only
+ * @throws ReadPendingException
+ * If a read operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Long> read(ByteBuffer[] dsts,
+ int offset,
+ int length,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Long,? super A> handler);
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * <p> This method initiates the writing of a sequence of bytes to this channel
+ * from the given buffer, returning a {@code Future} representing the
+ * pending result of the operation. The {@code Future}'s {@link Future#get()
+ * get} method will return the number of bytes written.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then it completes with the exception {@link
+ * InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been written, or will
+ * not be written to the channel from the given buffer, then further attempts
+ * to write to the channel will cause an unspecific runtime exception to be
+ * thrown.
+ *
+ * <p> Otherwise this method works in the same manner as the {@link
+ * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)}
+ * method.
+ *
+ * @param src
+ * The buffer from which bytes are to be retrieved
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IllegalArgumentException
+ * If the {@code timeout} parameter is negative
+ * @throws WritePendingException
+ * If a write operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Integer> write(ByteBuffer src,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler);
+
+ /**
+ * @throws WritePendingException {@inheritDoc}
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ @Override
+ public final <A> Future<Integer> write(ByteBuffer src,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+
+ {
+ return write(src, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * @throws WritePendingException {@inheritDoc}
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ */
+ @Override
+ public final Future<Integer> write(ByteBuffer src) {
+ return write(src, 0L, TimeUnit.MILLISECONDS, null, null);
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from a subsequence of the given
+ * buffers. This operation, sometimes called a <em>gathering write</em>, is
+ * often useful when implementing network protocols that group data into
+ * segments consisting of one or more fixed-length headers followed by a
+ * variable-length body.
+ *
+ * <p> This method initiates a write of up to <i>r</i> bytes to this channel,
+ * where <i>r</i> is the total number of bytes remaining in the specified
+ * subsequence of the given buffer array, that is,
+ *
+ * <blockquote><pre>
+ * srcs[offset].remaining()
+ * + srcs[offset+1].remaining()
+ * + ... + srcs[offset+length-1].remaining()</pre></blockquote>
+ *
+ * at the moment that the write is attempted.
+ *
+ * <p> Suppose that a byte sequence of length <i>n</i> is written, where
+ * <tt>0</tt>&nbsp;<tt>&lt;</tt>&nbsp;<i>n</i>&nbsp;<tt>&lt;=</tt>&nbsp;<i>r</i>.
+ * Up to the first <tt>srcs[offset].remaining()</tt> bytes of this sequence
+ * are written from buffer <tt>srcs[offset]</tt>, up to the next
+ * <tt>srcs[offset+1].remaining()</tt> bytes are written from buffer
+ * <tt>srcs[offset+1]</tt>, and so forth, until the entire byte sequence is
+ * written. As many bytes as possible are written from each buffer, hence
+ * the final position of each updated buffer, except the last updated
+ * buffer, is guaranteed to be equal to that buffer's limit. The underlying
+ * operating system may impose a limit on the number of buffers that may be
+ * used in an I/O operation. Where the number of buffers (with bytes
+ * remaining), exceeds this limit, then the I/O operation is performed with
+ * the maximum number of buffers allowed by the operating system.
+ *
+ * <p> The return value from this method is a {@code Future} representing
+ * the pending result of the operation. The {@code Future}'s {@link
+ * Future#get() get} method will return the number of bytes written.
+ *
+ * <p> If a timeout is specified and the timeout elapses before the operation
+ * completes then it completes with the exception {@link
+ * InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been written, or will
+ * not be written to the channel from the given buffers, then further attempts
+ * to write to the channel will cause an unspecific runtime exception to be
+ * thrown.
+ *
+ * @param srcs
+ * The buffers from which bytes are to be retrieved
+ * @param offset
+ * The offset within the buffer array of the first buffer from which
+ * bytes are to be retrieved; must be non-negative and no larger
+ * than {@code srcs.length}
+ * @param length
+ * The maximum number of buffers to be accessed; must be non-negative
+ * and no larger than {@code srcs.length - offset}
+ * @param timeout
+ * The timeout, or {@code 0L} for no timeout
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result; can be {@code null}
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws IndexOutOfBoundsException
+ * If the pre-conditions for the {@code offset} and {@code length}
+ * parameter aren't met
+ * @throws IllegalArgumentException
+ * If the {@code timeout} parameter is negative
+ * @throws WritePendingException
+ * If a write operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If a handler is specified, and the channel group is shutdown
+ */
+ public abstract <A> Future<Long> write(ByteBuffer[] srcs,
+ int offset,
+ int length,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Long,? super A> handler);
+}
diff --git a/src/share/classes/java/nio/channels/Channels.java b/src/share/classes/java/nio/channels/Channels.java
index cab960486..4fdcef8ab 100644
--- a/src/share/classes/java/nio/channels/Channels.java
+++ b/src/share/classes/java/nio/channels/Channels.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -33,15 +33,12 @@ import java.io.Reader;
import java.io.Writer;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.BufferOverflowException;
-import java.nio.BufferUnderflowException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.channels.spi.AbstractInterruptibleChannel;
+import java.util.concurrent.ExecutionException;
import sun.nio.ch.ChannelInputStream;
import sun.nio.cs.StreamDecoder;
import sun.nio.cs.StreamEncoder;
@@ -184,6 +181,155 @@ public final class Channels {
};
}
+ /**
+ * {@note new}
+ * Constructs a stream that reads bytes from the given channel.
+ *
+ * <p> The stream will not be buffered, and it will not support the {@link
+ * InputStream#mark mark} or {@link InputStream#reset reset} methods. The
+ * stream will be safe for access by multiple concurrent threads. Closing
+ * the stream will in turn cause the channel to be closed. </p>
+ *
+ * @param ch
+ * The channel from which bytes will be read
+ *
+ * @return A new input stream
+ *
+ * @since 1.7
+ */
+ public static InputStream newInputStream(final AsynchronousByteChannel ch) {
+ checkNotNull(ch, "ch");
+ return new InputStream() {
+
+ private ByteBuffer bb = null;
+ private byte[] bs = null; // Invoker's previous array
+ private byte[] b1 = null;
+
+ @Override
+ public synchronized int read() throws IOException {
+ if (b1 == null)
+ b1 = new byte[1];
+ int n = this.read(b1);
+ if (n == 1)
+ return b1[0] & 0xff;
+ return -1;
+ }
+
+ @Override
+ public synchronized int read(byte[] bs, int off, int len)
+ throws IOException
+ {
+ if ((off < 0) || (off > bs.length) || (len < 0) ||
+ ((off + len) > bs.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0)
+ return 0;
+
+ ByteBuffer bb = ((this.bs == bs)
+ ? this.bb
+ : ByteBuffer.wrap(bs));
+ bb.position(off);
+ bb.limit(Math.min(off + len, bb.capacity()));
+ this.bb = bb;
+ this.bs = bs;
+
+ boolean interrupted = false;
+ try {
+ for (;;) {
+ try {
+ return ch.read(bb).get();
+ } catch (ExecutionException ee) {
+ throw new IOException(ee.getCause());
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ }
+ } finally {
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ ch.close();
+ }
+ };
+ }
+
+ /**
+ * {@note new}
+ * Constructs a stream that writes bytes to the given channel.
+ *
+ * <p> The stream will not be buffered. The stream will be safe for access
+ * by multiple concurrent threads. Closing the stream will in turn cause
+ * the channel to be closed. </p>
+ *
+ * @param ch
+ * The channel to which bytes will be written
+ *
+ * @return A new output stream
+ *
+ * @since 1.7
+ */
+ public static OutputStream newOutputStream(final AsynchronousByteChannel ch) {
+ checkNotNull(ch, "ch");
+ return new OutputStream() {
+
+ private ByteBuffer bb = null;
+ private byte[] bs = null; // Invoker's previous array
+ private byte[] b1 = null;
+
+ @Override
+ public synchronized void write(int b) throws IOException {
+ if (b1 == null)
+ b1 = new byte[1];
+ b1[0] = (byte)b;
+ this.write(b1);
+ }
+
+ @Override
+ public synchronized void write(byte[] bs, int off, int len)
+ throws IOException
+ {
+ if ((off < 0) || (off > bs.length) || (len < 0) ||
+ ((off + len) > bs.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+ ByteBuffer bb = ((this.bs == bs)
+ ? this.bb
+ : ByteBuffer.wrap(bs));
+ bb.limit(Math.min(off + len, bb.capacity()));
+ bb.position(off);
+ this.bb = bb;
+ this.bs = bs;
+
+ boolean interrupted = false;
+ try {
+ while (bb.remaining() > 0) {
+ try {
+ ch.write(bb).get();
+ } catch (ExecutionException ee) {
+ throw new IOException(ee.getCause());
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ }
+ } finally {
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ ch.close();
+ }
+ };
+ }
+
// -- Channels from streams --
@@ -468,5 +614,4 @@ public final class Channels {
checkNotNull(csName, "csName");
return newWriter(ch, Charset.forName(csName).newEncoder(), -1);
}
-
}
diff --git a/src/share/classes/java/nio/channels/CompletionHandler.java b/src/share/classes/java/nio/channels/CompletionHandler.java
new file mode 100644
index 000000000..c4d4add8f
--- /dev/null
+++ b/src/share/classes/java/nio/channels/CompletionHandler.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+/**
+ * A handler for consuming the result of an asynchronous I/O operation.
+ *
+ * <p> The asynchronous channels defined in this package allow a completion
+ * handler to be specified to consume the result of an asynchronous operation.
+ * The {@link #completed completed} method is invoked when the I/O operation
+ * completes successfully. The {@link #failed failed} method is invoked if the
+ * I/O operations fails. The {@link #cancelled cancelled} method is invoked when
+ * the I/O operation is cancelled by invoking the {@link
+ * java.util.concurrent.Future#cancel cancel} method. The implementations of
+ * these methods should complete in a timely manner so as to avoid keeping the
+ * invoking thread from dispatching to other completion handlers.
+ *
+ * @param <V> The result type of the I/O operation
+ * @param <A> The type of the object attached to the I/O operation
+ *
+ * @since 1.7
+ */
+
+public interface CompletionHandler<V,A> {
+
+ /**
+ * Invoked when an operation has completed.
+ *
+ * @param result
+ * The result of the I/O operation.
+ * @param attachment
+ * The object attached to the I/O operation when it was initiated.
+ */
+ void completed(V result, A attachment);
+
+ /**
+ * Invoked when an operation fails.
+ *
+ * @param exc
+ * The exception to indicate why the I/O operation failed
+ * @param attachment
+ * The object attached to the I/O operation when it was initiated.
+ */
+ void failed(Throwable exc, A attachment);
+
+ /**
+ * Invoked when an operation is cancelled by invoking the {@link
+ * java.util.concurrent.Future#cancel cancel} method.
+ *
+ * @param attachment
+ * The object attached to the I/O operation when it was initiated.
+ */
+ void cancelled(A attachment);
+}
diff --git a/src/share/classes/java/nio/channels/DatagramChannel.java b/src/share/classes/java/nio/channels/DatagramChannel.java
index b8697fa1d..c7bd3df8b 100644
--- a/src/share/classes/java/nio/channels/DatagramChannel.java
+++ b/src/share/classes/java/nio/channels/DatagramChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -31,7 +31,8 @@ import java.net.DatagramSocket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
-import java.nio.channels.spi.*;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
/**
* A selectable channel for datagram-oriented sockets.
@@ -53,7 +54,8 @@ import java.nio.channels.spi.*;
* be determined by invoking its {@link #isConnected isConnected} method.
*
* <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
- * setOption} method. Datagram channels support the following options:
+ * setOption} method. A datagram channel to an Internet Protocol socket supports
+ * the following options:
* <blockquote>
* <table border>
* <tr>
@@ -211,6 +213,7 @@ public abstract class DatagramChannel
throws IOException;
/**
+ * @throws UnsupportedOperationException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
@@ -220,7 +223,6 @@ public abstract class DatagramChannel
public abstract <T> DatagramChannel setOption(SocketOption<T> name, T value)
throws IOException;
-
/**
* Retrieves a datagram socket associated with this channel.
*
@@ -313,15 +315,17 @@ public abstract class DatagramChannel
/**
* Returns the remote address to which this channel's socket is connected.
*
- * @return The remote address; {@code null} if the channel is not {@link
- * #isOpen open} or the channel's socket is not connected
+ * @return The remote address; {@code null} if the channel's socket is not
+ * connected
*
+ * @throws ClosedChannelException
+ * If the channel is closed
* @throws IOException
* If an I/O error occurs
*
* @since 1.7
*/
- public abstract SocketAddress getConnectedAddress() throws IOException;
+ public abstract SocketAddress getRemoteAddress() throws IOException;
/**
* Receives a datagram via this channel.
diff --git a/src/share/classes/java/nio/channels/FileChannel.java b/src/share/classes/java/nio/channels/FileChannel.java
index e3b5f5bcb..ab780a520 100644
--- a/src/share/classes/java/nio/channels/FileChannel.java
+++ b/src/share/classes/java/nio/channels/FileChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -29,16 +29,23 @@ import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.spi.AbstractInterruptibleChannel;
-
+import java.nio.file.*;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.spi.*;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
/**
* A channel for reading, writing, mapping, and manipulating a file.
*
- * <p> A file channel has a current <i>position</i> within its file which can
- * be both {@link #position() </code>queried<code>} and {@link #position(long)
- * </code>modified<code>}. The file itself contains a variable-length sequence
+ * <p> {@note revised}
+ * A file channel is a {@link SeekableByteChannel} that is connected to
+ * a file. It has a current <i>position</i> within its file which can
+ * be both {@link #position() <i>queried</i>} and {@link #position(long)
+ * <i>modified</i>}. The file itself contains a variable-length sequence
* of bytes that can be read and written and whose current {@link #size
- * </code><i>size</i><code>} can be queried. The size of the file increases
+ * <i>size</i>} can be queried. The size of the file increases
* when bytes are written beyond its current size; the size of the file
* decreases when it is {@link #truncate </code><i>truncated</i><code>}. The
* file may also have some associated <i>metadata</i> such as access
@@ -50,27 +57,27 @@ import java.nio.channels.spi.AbstractInterruptibleChannel;
*
* <ul>
*
- * <li><p> Bytes may be {@link #read(ByteBuffer, long) </code>read<code>} or
- * {@link #write(ByteBuffer, long) </code>written<code>} at an absolute
+ * <li><p> Bytes may be {@link #read(ByteBuffer, long) read} or
+ * {@link #write(ByteBuffer, long) <i>written</i>} at an absolute
* position in a file in a way that does not affect the channel's current
* position. </p></li>
*
- * <li><p> A region of a file may be {@link #map </code>mapped<code>}
+ * <li><p> A region of a file may be {@link #map <i>mapped</i>}
* directly into memory; for large files this is often much more efficient
* than invoking the usual <tt>read</tt> or <tt>write</tt> methods.
* </p></li>
*
- * <li><p> Updates made to a file may be {@link #force </code>forced
- * out<code>} to the underlying storage device, ensuring that data are not
+ * <li><p> Updates made to a file may be {@link #force <i>forced
+ * out</i>} to the underlying storage device, ensuring that data are not
* lost in the event of a system crash. </p></li>
*
- * <li><p> Bytes can be transferred from a file {@link #transferTo </code>to
- * some other channel<code>}, and {@link #transferFrom </code>vice
- * versa<code>}, in a way that can be optimized by many operating systems
+ * <li><p> Bytes can be transferred from a file {@link #transferTo <i>to
+ * some other channel</i>}, and {@link #transferFrom <i>vice
+ * versa</i>}, in a way that can be optimized by many operating systems
* into a very fast transfer directly to or from the filesystem cache.
* </p></li>
*
- * <li><p> A region of a file may be {@link FileLock </code>locked<code>}
+ * <li><p> A region of a file may be {@link FileLock <i>locked</i>}
* against access by other programs. </p></li>
*
* </ul>
@@ -96,25 +103,23 @@ import java.nio.channels.spi.AbstractInterruptibleChannel;
* machine. The exact nature of any such inconsistencies are system-dependent
* and are therefore unspecified.
*
- * <p> This class does not define methods for opening existing files or for
- * creating new ones; such methods may be added in a future release. In this
- * release a file channel can be obtained from an existing {@link
- * java.io.FileInputStream#getChannel FileInputStream}, {@link
+ * <p> A file channel is created by invoking one of the {@link #open open}
+ * methods defined by this class. A file channel can also be obtained from an
+ * existing {@link java.io.FileInputStream#getChannel FileInputStream}, {@link
* java.io.FileOutputStream#getChannel FileOutputStream}, or {@link
* java.io.RandomAccessFile#getChannel RandomAccessFile} object by invoking
* that object's <tt>getChannel</tt> method, which returns a file channel that
- * is connected to the same underlying file.
- *
- * <p> The state of a file channel is intimately connected to that of the
- * object whose <tt>getChannel</tt> method returned the channel. Changing the
- * channel's position, whether explicitly or by reading or writing bytes, will
- * change the file position of the originating object, and vice versa.
- * Changing the file's length via the file channel will change the length seen
- * via the originating object, and vice versa. Changing the file's content by
- * writing bytes will change the content seen by the originating object, and
- * vice versa.
+ * is connected to the same underlying file. Where the file channel is obtained
+ * from an existing stream or random access file then the state of the file
+ * channel is intimately connected to that of the object whose <tt>getChannel</tt>
+ * method returned the channel. Changing the channel's position, whether
+ * explicitly or by reading or writing bytes, will change the file position of
+ * the originating object, and vice versa. Changing the file's length via the
+ * file channel will change the length seen via the originating object, and vice
+ * versa. Changing the file's content by writing bytes will change the content
+ * seen by the originating object, and vice versa.
*
- * <a name="open-mode"><p> At various points this class specifies that an
+ * <a name="open-mode"></a> <p> At various points this class specifies that an
* instance that is "open for reading," "open for writing," or "open for
* reading and writing" is required. A channel obtained via the {@link
* java.io.FileInputStream#getChannel getChannel} method of a {@link
@@ -127,7 +132,7 @@ import java.nio.channels.spi.AbstractInterruptibleChannel;
* was created with mode <tt>"r"</tt> and will be open for reading and writing
* if the instance was created with mode <tt>"rw"</tt>.
*
- * <a name="append-mode"><p> A file channel that is open for writing may be in
+ * <a name="append-mode"></a><p> A file channel that is open for writing may be in
* <i>append mode</i>, for example if it was obtained from a file-output stream
* that was created by invoking the {@link
* java.io.FileOutputStream#FileOutputStream(java.io.File,boolean)
@@ -138,7 +143,6 @@ import java.nio.channels.spi.AbstractInterruptibleChannel;
* of the data are done in a single atomic operation is system-dependent and
* therefore unspecified.
*
- *
* @see java.io.FileInputStream#getChannel()
* @see java.io.FileOutputStream#getChannel()
* @see java.io.RandomAccessFile#getChannel()
@@ -147,18 +151,190 @@ import java.nio.channels.spi.AbstractInterruptibleChannel;
* @author Mike McCloskey
* @author JSR-51 Expert Group
* @since 1.4
+ * @updated 1.7
*/
public abstract class FileChannel
extends AbstractInterruptibleChannel
- implements ByteChannel, GatheringByteChannel, ScatteringByteChannel
+ implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
{
-
/**
* Initializes a new instance of this class.
*/
protected FileChannel() { }
+ /**
+ * {@note new}
+ * Opens or creates a file, returning a file channel to access the file.
+ *
+ * <p> The {@code options} parameter determines how the file is opened.
+ * The {@link StandardOpenOption#READ READ} and {@link StandardOpenOption#WRITE
+ * WRITE} options determine if the file should be opened for reading and/or
+ * writing. If neither option (or the {@link StandardOpenOption#APPEND APPEND}
+ * option) is contained in the array then the file is opened for reading.
+ * By default reading or writing commences at the beginning of the file.
+ *
+ * <p> In the addition to {@code READ} and {@code WRITE}, the following
+ * options may be present:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Option</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#APPEND APPEND} </td>
+ * <td> If this option is present then the file is opened for writing and
+ * each invocation of the channel's {@code write} method first advances
+ * the position to the end of the file and then writes the requested
+ * data. Whether the advancement of the position and the writing of the
+ * data are done in a single atomic operation is system-dependent and
+ * therefore unspecified. This option may not be used in conjunction
+ * with the {@code READ} or {@code TRUNCATE_EXISTING} options. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} </td>
+ * <td> If this option is present then the existing file is truncated to
+ * a size of 0 bytes. This option is ignored when the file is opened only
+ * for reading. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#CREATE_NEW CREATE_NEW} </td>
+ * <td> If this option is present then a new file is created, failing if
+ * the file already exists. When creating a file the check for the
+ * existence of the file and the creation of the file if it does not exist
+ * is atomic with respect to other file system operations. This option is
+ * ignored when the file is opened only for reading. </td>
+ * </tr>
+ * <tr>
+ * <td > {@link StandardOpenOption#CREATE CREATE} </td>
+ * <td> If this option is present then an existing file is opened if it
+ * exists, otherwise a new file is created. When creating a file the check
+ * for the existence of the file and the creation of the file if it does
+ * not exist is atomic with respect to other file system operations. This
+ * option is ignored if the {@code CREATE_NEW} option is also present or
+ * the file is opened only for reading. </td>
+ * </tr>
+ * <tr>
+ * <td > {@link StandardOpenOption#DELETE_ON_CLOSE DELETE_ON_CLOSE} </td>
+ * <td> When this option is present then the implementation makes a
+ * <em>best effort</em> attempt to delete the file when closed by the
+ * the {@link #close close} method. If the {@code close} method is not
+ * invoked then a <em>best effort</em> attempt is made to delete the file
+ * when the Java virtual machine terminates. </td>
+ * </tr>
+ * <tr>
+ * <td>{@link StandardOpenOption#SPARSE SPARSE} </td>
+ * <td> When creating a new file this option is a <em>hint</em> that the
+ * new file will be sparse. This option is ignored when not creating
+ * a new file. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#SYNC SYNC} </td>
+ * <td> Requires that every update to the file's content or metadata be
+ * written synchronously to the underlying storage device. (see <a
+ * href="../file/package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * <tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#DSYNC DSYNC} </td>
+ * <td> Requires that every update to the file's content be written
+ * synchronously to the underlying storage device. (see <a
+ * href="../file/package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * </tr>
+ * </table>
+ *
+ * <p> An implementation may also support additional options.
+ *
+ * <p> The {@code attrs} parameter is an optional array of file {@link
+ * FileAttribute file-attributes} to set atomically when creating the file.
+ *
+ * <p> The new channel is created by invoking the {@link
+ * FileSystemProvider#newFileChannel newFileChannel} method on the
+ * provider that created the {@code Path}.
+ *
+ * @param file
+ * The path of the file to open or create
+ * @param options
+ * Options specifying how the file is opened
+ * @param attrs
+ * An optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return A new file channel
+ *
+ * @throws IllegalArgumentException
+ * If the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * If the {@code file} is associated with a provider that does not
+ * support creating file channels, or an unsupported open option is
+ * specified, or the array contains an attribute that cannot be set
+ * atomically when creating the file
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an
+ * unspecified permission required by the implementation.
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check
+ * read access if the file is opened for reading. The {@link
+ * SecurityManager#checkWrite(String)} method is invoked to check
+ * write access if the file is opened for writing
+ *
+ * @since 1.7
+ */
+ public static FileChannel open(Path file,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ FileSystemProvider provider = file.getFileSystem().provider();
+ return provider.newFileChannel(file, options, attrs);
+ }
+
+ private static final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];
+
+ /**
+ * {@note new}
+ * Opens or creates a file, returning a file channel to access the file.
+ *
+ * <p> An invocation of this method behaves in exactly the same way as the
+ * invocation
+ * <pre>
+ * fc.{@link #open(Path,Set,FileAttribute[]) open}(file, options, new FileAttribute&lt;?&gt;[0]);
+ * </pre>
+ *
+ * @param file
+ * The path of the file to open or create
+ * @param options
+ * Options specifying how the file is opened
+ *
+ * @return A new file channel
+ *
+ * @throws IllegalArgumentException
+ * If the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * If the {@code file} is associated with a provider that does not
+ * support creating file channels, or an unsupported open option is
+ * specified
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an
+ * unspecified permission required by the implementation.
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check
+ * read access if the file is opened for reading. The {@link
+ * SecurityManager#checkWrite(String)} method is invoked to check
+ * write access if the file is opened for writing
+ *
+ * @since 1.7
+ */
+ public static FileChannel open(Path file, OpenOption... options)
+ throws IOException
+ {
+ Set<OpenOption> set = new HashSet<OpenOption>(options.length);
+ Collections.addAll(set, options);
+ return open(file, set, NO_ATTRIBUTES);
+ }
// -- Channel operations --
@@ -286,7 +462,7 @@ public abstract class FileChannel
public abstract FileChannel position(long newPosition) throws IOException;
/**
- * Returns the current size of this channel's file. </p>
+ * Returns the current size of this channel's file. </p>
*
* @return The current size of this channel's file,
* measured in bytes
@@ -359,7 +535,7 @@ public abstract class FileChannel
* <p> This method is only guaranteed to force changes that were made to
* this channel's file via the methods defined in this class. It may or
* may not force changes that were made by modifying the content of a
- * {@link MappedByteBuffer </code>mapped byte buffer<code>} obtained by
+ * {@link MappedByteBuffer <i>mapped byte buffer</i>} obtained by
* invoking the {@link #map map} method. Invoking the {@link
* MappedByteBuffer#force force} method of the mapped byte buffer will
* force changes made to the buffer's content to be written. </p>
@@ -678,7 +854,7 @@ public abstract class FileChannel
* reading; for a read/write or private mapping, this channel must have
* been opened for both reading and writing.
*
- * <p> The {@link MappedByteBuffer </code>mapped byte buffer<code>}
+ * <p> The {@link MappedByteBuffer <i>mapped byte buffer</i>}
* returned by this method will have a position of zero and a limit and
* capacity of <tt>size</tt>; its mark will be undefined. The buffer and
* the mapping that it represents will remain valid until the buffer itself
@@ -717,6 +893,8 @@ public abstract class FileChannel
* The size of the region to be mapped; must be non-negative and
* no greater than {@link java.lang.Integer#MAX_VALUE}
*
+ * @return The mapped byte buffer
+ *
* @throws NonReadableChannelException
* If the <tt>mode</tt> is {@link MapMode#READ_ONLY READ_ONLY} but
* this channel was not opened for reading
diff --git a/src/share/classes/java/nio/channels/FileLock.java b/src/share/classes/java/nio/channels/FileLock.java
index 921922242..b0ec37f1b 100644
--- a/src/share/classes/java/nio/channels/FileLock.java
+++ b/src/share/classes/java/nio/channels/FileLock.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -27,14 +27,16 @@ package java.nio.channels;
import java.io.IOException;
-
/**
* A token representing a lock on a region of a file.
*
* <p> A file-lock object is created each time a lock is acquired on a file via
* one of the {@link FileChannel#lock(long,long,boolean) lock} or {@link
- * FileChannel#tryLock(long,long,boolean) tryLock} methods of the {@link
- * FileChannel} class.
+ * FileChannel#tryLock(long,long,boolean) tryLock} methods of the
+ * {@link FileChannel} class, or the {@link
+ * AsynchronousFileChannel#lock(long,long,boolean,Object,CompletionHandler) lock}
+ * or {@link AsynchronousFileChannel#tryLock(long,long,boolean) tryLock}
+ * methods of the {@link AsynchronousFileChannel} class.
*
* <p> A file-lock object is initially valid. It remains valid until the lock
* is released by invoking the {@link #release release} method, by closing the
@@ -70,8 +72,7 @@ import java.io.IOException;
* <p> File-lock objects are safe for use by multiple concurrent threads.
*
*
- * <a name="pdep">
- * <h4> Platform dependencies </h4>
+ * <a name="pdep"><h4> Platform dependencies </h4></a>
*
* <p> This file-locking API is intended to map directly to the native locking
* facility of the underlying operating system. Thus the locks held on a file
@@ -93,7 +94,7 @@ import java.io.IOException;
*
* <p> On some systems, acquiring a mandatory lock on a region of a file
* prevents that region from being {@link java.nio.channels.FileChannel#map
- * </code>mapped into memory<code>}, and vice versa. Programs that combine
+ * <i>mapped into memory</i>}, and vice versa. Programs that combine
* locking and mapping should be prepared for this combination to fail.
*
* <p> On some systems, closing a channel releases all locks held by the Java
@@ -113,11 +114,12 @@ import java.io.IOException;
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
+ * @updated 1.7
*/
public abstract class FileLock {
- private final FileChannel channel;
+ private final Channel channel;
private final long position;
private final long size;
private final boolean shared;
@@ -159,11 +161,66 @@ public abstract class FileLock {
}
/**
- * Returns the file channel upon whose file this lock is held. </p>
+ * {@note new} Initializes a new instance of this class.
+ *
+ * @param channel
+ * The channel upon whose file this lock is held
+ *
+ * @param position
+ * The position within the file at which the locked region starts;
+ * must be non-negative
+ *
+ * @param size
+ * The size of the locked region; must be non-negative, and the sum
+ * <tt>position</tt>&nbsp;+&nbsp;<tt>size</tt> must be non-negative
+ *
+ * @param shared
+ * <tt>true</tt> if this lock is shared,
+ * <tt>false</tt> if it is exclusive
+ *
+ * @throws IllegalArgumentException
+ * If the preconditions on the parameters do not hold
+ *
+ * @since 1.7
+ */
+ protected FileLock(AsynchronousFileChannel channel,
+ long position, long size, boolean shared)
+ {
+ if (position < 0)
+ throw new IllegalArgumentException("Negative position");
+ if (size < 0)
+ throw new IllegalArgumentException("Negative size");
+ if (position + size < 0)
+ throw new IllegalArgumentException("Negative position + size");
+ this.channel = channel;
+ this.position = position;
+ this.size = size;
+ this.shared = shared;
+ }
+
+ /**
+ * {@note revised}
+ * Returns the file channel upon whose file this lock was acquired.
*
- * @return The file channel
+ * <p> This method has been superseded by the {@link #acquiredBy acquiredBy}
+ * method.
+ *
+ * @return The file channel, or {@code null} if the file lock was not
+ * acquired by a file channel.
*/
public final FileChannel channel() {
+ return (channel instanceof FileChannel) ? (FileChannel)channel : null;
+ }
+
+ /**
+ * {@note new}
+ * Returns the channel upon whose file this lock was acquired.
+ *
+ * @return The channel upon whose file this lock was acquired.
+ *
+ * @since 1.7
+ */
+ public Channel acquiredBy() {
return channel;
}
diff --git a/src/share/classes/java/nio/channels/MembershipKey.java b/src/share/classes/java/nio/channels/MembershipKey.java
index 0d2fc52bc..804e6724a 100644
--- a/src/share/classes/java/nio/channels/MembershipKey.java
+++ b/src/share/classes/java/nio/channels/MembershipKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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
@@ -28,7 +28,6 @@ package java.nio.channels;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.io.IOException;
-import java.util.List;
/**
* A token representing the membership of an Internet Protocol (IP) multicast
@@ -38,7 +37,7 @@ import java.util.List;
* to the group, or it may be <em>source-specific</em>, meaning that it
* represents a membership that receives only datagrams from a specific source
* address. Whether or not a membership key is source-specific may be determined
- * by invoking its {@link #getSourceAddress() getSourceAddress} method.
+ * by invoking its {@link #sourceAddress() sourceAddress} method.
*
* <p> A membership key is valid upon creation and remains valid until the
* membership is dropped by invoking the {@link #drop() drop} method, or
@@ -93,11 +92,8 @@ public abstract class MembershipKey {
* If the multicast group membership is already invalid then invoking this
* method has no effect. Once a multicast group membership is invalid,
* it remains invalid forever.
- *
- * @throws IOException
- * If an I/O error occurs
*/
- public abstract void drop() throws IOException;
+ public abstract void drop();
/**
* Block multicast datagrams from the given source address.
@@ -140,10 +136,8 @@ public abstract class MembershipKey {
* @throws IllegalStateException
* If the given source address is not currently blocked or the
* membership key is no longer valid
- * @throws IOException
- * If an I/O error occurs
*/
- public abstract MembershipKey unblock(InetAddress source) throws IOException;
+ public abstract MembershipKey unblock(InetAddress source);
/**
* Returns the channel for which this membership key was created. This
@@ -152,7 +146,7 @@ public abstract class MembershipKey {
*
* @return the channel
*/
- public abstract MulticastChannel getChannel();
+ public abstract MulticastChannel channel();
/**
* Returns the multicast group for which this membership key was created.
@@ -161,7 +155,7 @@ public abstract class MembershipKey {
*
* @return the multicast group
*/
- public abstract InetAddress getGroup();
+ public abstract InetAddress group();
/**
* Returns the network interface for which this membership key was created.
@@ -170,7 +164,7 @@ public abstract class MembershipKey {
*
* @return the network interface
*/
- public abstract NetworkInterface getNetworkInterface();
+ public abstract NetworkInterface networkInterface();
/**
* Returns the source address if this membership key is source-specific,
@@ -179,5 +173,5 @@ public abstract class MembershipKey {
* @return The source address if this membership key is source-specific,
* otherwise {@code null}
*/
- public abstract InetAddress getSourceAddress();
+ public abstract InetAddress sourceAddress();
}
diff --git a/src/share/classes/java/nio/channels/MulticastChannel.java b/src/share/classes/java/nio/channels/MulticastChannel.java
index 440dd2a8b..1cacf98e6 100644
--- a/src/share/classes/java/nio/channels/MulticastChannel.java
+++ b/src/share/classes/java/nio/channels/MulticastChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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
@@ -123,6 +123,22 @@ public interface MulticastChannel
extends NetworkChannel
{
/**
+ * Closes this channel.
+ *
+ * <p> If the channel is a member of a multicast group then the membership
+ * is {@link MembershipKey#drop dropped}. Upon return, the {@link
+ * MembershipKey membership-key} will be {@link MembershipKey#isValid
+ * invalid}.
+ *
+ * <p> This method otherwise behaves exactly as specified by the {@link
+ * Channel} interface.
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ @Override void close() throws IOException;
+
+ /**
* Joins a multicast group to begin receiving all datagrams sent to the group,
* returning a membership key.
*
@@ -130,7 +146,7 @@ public interface MulticastChannel
* interface to receive all datagrams then the membership key, representing
* that membership, is returned. Otherwise this channel joins the group and
* the resulting new membership key is returned. The resulting membership key
- * is not {@link MembershipKey#getSourceAddress source-specific}.
+ * is not {@link MembershipKey#sourceAddress source-specific}.
*
* <p> A multicast channel may join several multicast groups, including
* the same group on more than one interface. An implementation may impose a
@@ -150,6 +166,8 @@ public interface MulticastChannel
* @throws IllegalStateException
* If the channel already has source-specific membership of the
* group on the interface
+ * @throws UnsupportedOperationException
+ * If the channel's socket is not an Internet Protocol socket
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
@@ -170,7 +188,7 @@ public interface MulticastChannel
* interface to receive datagrams from the given source address then the
* membership key, representing that membership, is returned. Otherwise this
* channel joins the group and the resulting new membership key is returned.
- * The resulting membership key is {@link MembershipKey#getSourceAddress
+ * The resulting membership key is {@link MembershipKey#sourceAddress
* source-specific}.
*
* <p> Membership is <em>cumulative</em> and this method may be invoked
@@ -196,7 +214,8 @@ public interface MulticastChannel
* If the channel is currently a member of the group on the given
* interface to receive all datagrams
* @throws UnsupportedOperationException
- * If the underlying operation system does not support source filtering
+ * If the channel's socket is not an Internet Protocol socket or
+ * source filtering is not supported
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
diff --git a/src/share/classes/java/nio/channels/NetworkChannel.java b/src/share/classes/java/nio/channels/NetworkChannel.java
index fae642fcb..103427759 100644
--- a/src/share/classes/java/nio/channels/NetworkChannel.java
+++ b/src/share/classes/java/nio/channels/NetworkChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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
@@ -95,9 +95,10 @@ public interface NetworkChannel
* java.net.InetSocketAddress}.
*
* @return The socket address that the socket is bound to, or {@code null}
- * if the channel is not {@link #isOpen open} or the channel's socket
- * is not bound
+ * if the channel's socket is not bound
*
+ * @throws ClosedChannelException
+ * If the channel is closed
* @throws IOException
* If an I/O error occurs
*/
@@ -114,9 +115,10 @@ public interface NetworkChannel
*
* @return This channel
*
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
* @throws IllegalArgumentException
- * If the socket option is not supported by this channel, or
- * the value is not a valid value for this socket option
+ * If the value is not a valid value for this socket option
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
@@ -135,7 +137,7 @@ public interface NetworkChannel
* @return The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
- * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
* If the socket option is not supported by this channel
* @throws ClosedChannelException
* If this channel is closed
@@ -154,5 +156,5 @@ public interface NetworkChannel
*
* @return A set of the socket options supported by this channel
*/
- Set<SocketOption<?>> options();
+ Set<SocketOption<?>> supportedOptions();
}
diff --git a/src/share/classes/java/nio/channels/SeekableByteChannel.java b/src/share/classes/java/nio/channels/SeekableByteChannel.java
new file mode 100644
index 000000000..33efc2488
--- /dev/null
+++ b/src/share/classes/java/nio/channels/SeekableByteChannel.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels;
+
+import java.nio.ByteBuffer;
+import java.io.IOException;
+
+/**
+ * A byte channel that maintains a current <i>position</i> and allows the
+ * position to be changed.
+ *
+ * <p> A seekable byte channel is connected to an entity, typically a file,
+ * that contains a variable-length sequence of bytes that can be read and
+ * written. The current position can be {@link #position() <i>queried</i>} and
+ * {@link #position(long) <i>modified</i>}. The channel also provides access to
+ * the current <i>size</i> of the entity to which the channel is connected. The
+ * size increases when bytes are written beyond its current size; the size
+ * decreases when it is {@link #truncate <i>truncated</i>}.
+ *
+ * <p> The {@link #position(long) position} and {@link #truncate truncate} methods
+ * which do not otherwise have a value to return are specified to return the
+ * channel upon which they are invoked. This allows method invocations to be
+ * chained. Implementations of this interface should specialize the return type
+ * so that method invocations on the implementation class can be chained.
+ *
+ * @since 1.7
+ * @see java.nio.file.FileRef#newByteChannel
+ */
+
+public interface SeekableByteChannel
+ extends ByteChannel
+{
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * <p> Bytes are read starting at this channel's current position, and
+ * then the position is updated with the number of bytes actually read.
+ * Otherwise this method behaves exactly as specified in the {@link
+ * ReadableByteChannel} interface.
+ */
+ @Override
+ int read(ByteBuffer dst) throws IOException;
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * <p> Bytes are written starting at this channel's current position, unless
+ * the channel is connected to an entity such as a file that is opened with
+ * the {@link java.nio.file.StandardOpenOption#APPEND APPEND} option, in
+ * which case the position is first advanced to the end. The entity to which
+ * the channel is connected is grown, if necessary, to accommodate the
+ * written bytes, and then the position is updated with the number of bytes
+ * actually written. Otherwise this method behaves exactly as specified by
+ * the {@link WritableByteChannel} interface.
+ */
+ @Override
+ int write(ByteBuffer src) throws IOException;
+
+ /**
+ * Returns this channel's position.
+ *
+ * @return This channel's position,
+ * a non-negative integer counting the number of bytes
+ * from the beginning of the entity to the current position
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ long position() throws IOException;
+
+ /**
+ * Sets this channel's position.
+ *
+ * <p> Setting the position to a value that is greater than the current size
+ * is legal but does not change the size of the entity. A later attempt to
+ * read bytes at such a position will immediately return an end-of-file
+ * indication. A later attempt to write bytes at such a position will cause
+ * the entity to grow to accommodate the new bytes; the values of any bytes
+ * between the previous end-of-file and the newly-written bytes are
+ * unspecified.
+ *
+ * <p> Setting the channel's position is not recommended when connected to
+ * an entity, typically a file, that is opened with the {@link
+ * java.nio.file.StandardOpenOption#APPEND APPEND} option. When opened for
+ * append, the position is first advanced to the end before writing.
+ *
+ * @param newPosition
+ * The new position, a non-negative integer counting
+ * the number of bytes from the beginning of the entity
+ *
+ * @return This channel
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IllegalArgumentException
+ * If the new position is negative
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ SeekableByteChannel position(long newPosition) throws IOException;
+
+ /**
+ * Returns the current size of entity to which this channel is connected.
+ *
+ * @return The current size, measured in bytes
+ *
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ long size() throws IOException;
+
+ /**
+ * Truncates the entity, to which this channel is connected, to the given
+ * size.
+ *
+ * <p> If the given size is less than the current size then the entity is
+ * truncated, discarding any bytes beyond the new end. If the given size is
+ * greater than or equal to the current size then the entity is not modified.
+ * In either case, if the current position is greater than the given size
+ * then it is set to that size.
+ *
+ * <p> An implementation of this interface may prohibit truncation when
+ * connected to an entity, typically a file, opened with the {@link
+ * java.nio.file.StandardOpenOption#APPEND APPEND} option.
+ *
+ * @param size
+ * The new size, a non-negative byte count
+ *
+ * @return This channel
+ *
+ * @throws NonWritableChannelException
+ * If this channel was not opened for writing
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IllegalArgumentException
+ * If the new size is negative
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ SeekableByteChannel truncate(long size) throws IOException;
+}
diff --git a/src/share/classes/java/nio/channels/ServerSocketChannel.java b/src/share/classes/java/nio/channels/ServerSocketChannel.java
index 84ea062c9..5be9bc7cb 100644
--- a/src/share/classes/java/nio/channels/ServerSocketChannel.java
+++ b/src/share/classes/java/nio/channels/ServerSocketChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -29,7 +29,8 @@ import java.io.IOException;
import java.net.ServerSocket;
import java.net.SocketOption;
import java.net.SocketAddress;
-import java.nio.channels.spi.*;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
/**
* A selectable channel for stream-oriented listening sockets.
@@ -195,6 +196,7 @@ public abstract class ServerSocketChannel
throws IOException;
/**
+ * @throws UnsupportedOperationException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
diff --git a/src/share/classes/java/nio/channels/SocketChannel.java b/src/share/classes/java/nio/channels/SocketChannel.java
index 2e96bd2e4..975048df0 100644
--- a/src/share/classes/java/nio/channels/SocketChannel.java
+++ b/src/share/classes/java/nio/channels/SocketChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -30,7 +30,8 @@ import java.net.Socket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
-import java.nio.channels.spi.*;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
/**
* A selectable channel for stream-oriented connecting sockets.
@@ -212,7 +213,7 @@ public abstract class SocketChannel
/**
* @throws ConnectionPendingException
- * If a non-blocking connection operation is already in progress on
+ * If a non-blocking connect operation is already in progress on
* this channel
* @throws AlreadyBoundException {@inheritDoc}
* @throws UnsupportedAddressTypeException {@inheritDoc}
@@ -226,6 +227,7 @@ public abstract class SocketChannel
throws IOException;
/**
+ * @throws UnsupportedOperationException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
@@ -432,15 +434,17 @@ public abstract class SocketChannel
* socket address then the return value from this method is of type {@link
* java.net.InetSocketAddress}.
*
- * @return The remote address; {@code null} if the channel is not {@link
- * #isOpen open} or the channel's socket is not connected
+ * @return The remote address; {@code null} if the channel's socket is not
+ * connected
*
+ * @throws ClosedChannelException
+ * If the channel is closed
* @throws IOException
* If an I/O error occurs
*
* @since 1.7
*/
- public abstract SocketAddress getConnectedAddress() throws IOException;
+ public abstract SocketAddress getRemoteAddress() throws IOException;
// -- ByteChannel operations --
diff --git a/src/share/classes/java/nio/channels/exceptions b/src/share/classes/java/nio/channels/exceptions
index 04cfbe03e..fed9f72ab 100644
--- a/src/share/classes/java/nio/channels/exceptions
+++ b/src/share/classes/java/nio/channels/exceptions
@@ -1,5 +1,5 @@
#
-# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -150,6 +150,21 @@ gen OverlappingFileLockException "
SINCE=1.7
+SUPER=java.io.IOException
+
+gen InterruptedByTimeoutException "
+ * Checked exception received by a thread when a timeout elapses before an
+ * asynchronous operation completes." \
+ -4268008601014042947L
+
+SUPER=IllegalArgumentException
+
+gen IllegalChannelGroupException "
+ * Unchecked exception thrown when an attempt is made to open a channel
+ * in a group that was not created by the same provider. " \
+ -2495041211157744253L
+
+
SUPER=IllegalStateException
gen AlreadyBoundException "
@@ -157,3 +172,23 @@ gen AlreadyBoundException "
* network oriented channel that is already bound." \
6796072983322737592L
+gen AcceptPendingException "
+ * Unchecked exception thrown when an attempt is made to initiate an accept
+ * operation on a channel and a previous accept operation has not completed." \
+ 2721339977965416421L
+
+gen ReadPendingException "
+ * Unchecked exception thrown when an attempt is made to read from an
+ * asynchronous socket channel and a previous read has not completed." \
+ 1986315242191227217L
+
+gen WritePendingException "
+ * Unchecked exception thrown when an attempt is made to write to an
+ * asynchronous socket channel and a previous write has not completed." \
+ 7031871839266032276L
+
+gen ShutdownChannelGroupException "
+ * Unchecked exception thrown when an attempt is made to construct a channel in
+ * a group that is shutdown or the completion handler for an I/O operation
+ * cannot be invoked because the channel group is shutdown." \
+ -3903801676350154157L
diff --git a/src/share/classes/java/nio/channels/package-info.java b/src/share/classes/java/nio/channels/package-info.java
index e8c2a929d..47a1cb5f9 100644
--- a/src/share/classes/java/nio/channels/package-info.java
+++ b/src/share/classes/java/nio/channels/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -46,6 +46,10 @@
* <td>Can read/write to/from a&nbsp;buffer</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.SeekableByteChannel}</i></tt></td>
* <td>A {@code ByteChannel} connected to an entity that contains a variable-length sequence of bytes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.AsynchronousChannel}</i></tt></td>
+ * <td>Supports asynchronous I/O operations.</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.AsynchronousByteChannel}</i></tt></td>
+ * <td>Can read and write bytes asynchronously</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.NetworkChannel}</i></tt></td>
* <td>A channel to a network socket</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.MulticastChannel}</i></tt></td>
@@ -218,12 +222,70 @@
* directly; custom channel classes should extend the appropriate {@link
* java.nio.channels.SelectableChannel} subclasses defined in this package.
*
+ * <a name="async"></a>
+ *
+ * <blockquote><table cellspacing=1 cellpadding=0 summary="Lists asynchronous channels and their descriptions">
+ * <tr><th><p align="left">Asynchronous I/O</p></th><th><p align="left">Description</p></th></tr>
+ * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousFileChannel}</tt></td>
+ * <td>An asynchronous channel for reading, writing, and manipulating a file</td></tr>
+ * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousSocketChannel}</tt></td>
+ * <td>An asynchronous channel to a stream-oriented connecting socket</td></tr>
+ * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousServerSocketChannel}&nbsp;&nbsp;</tt></td>
+ * <td>An asynchronous channel to a stream-oriented listening socket</td></tr>
+ * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousDatagramChannel}</tt></td>
+ * <td>An asynchronous channel to a datagram-oriented socket</td></tr>
+ * <tr><td valign=top><tt>{@link java.nio.channels.CompletionHandler}</tt></td>
+ * <td>A handler for consuming the result of an asynchronous operation</td></tr>
+ * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousChannelGroup}</tt></td>
+ * <td>A grouping of asynchronous channels for the purpose of resource sharing</td></tr>
+ * </table></blockquote>
+ *
+ * <p> {@link java.nio.channels.AsynchronousChannel Asynchronous channels} are a
+ * special type of channel capable of asynchronous I/O operations. Asynchronous
+ * channels are non-blocking and define methods to initiate asynchronous
+ * operations, returning a {@link java.util.concurrent.Future} representing the
+ * pending result of each operation. The {@code Future} can be used to poll or
+ * wait for the result of the operation. Asynchronous I/O operations can also
+ * specify a {@link java.nio.channels.CompletionHandler} to invoke when the
+ * operation completes. A completion handler is user provided code that is executed
+ * to consume the result of I/O operation.
+ *
+ * <p> This package defines asynchronous-channel classes that are connected to
+ * a stream-oriented connecting or listening socket, or a datagram-oriented socket.
+ * It also defines the {@link java.nio.channels.AsynchronousFileChannel} class
+ * for asynchronous reading, writing, and manipulating a file. As with the {@link
+ * java.nio.channels.FileChannel} it supports operations to truncate the file
+ * to a specific size, force updates to the file to be written to the storage
+ * device, or acquire locks on the whole file or on a specific region of the file.
+ * Unlike the {@code FileChannel} it does not define methods for mapping a
+ * region of the file directly into memory. Where memory mapped I/O is required,
+ * then a {@code FileChannel} can be used.
+ *
+ * <p> Asynchronous channels are bound to an asynchronous channel group for the
+ * purpose of resource sharing. A group has an associated {@link
+ * java.util.concurrent.ExecutorService} to which tasks are submitted to handle
+ * I/O events and dispatch to completion handlers that consume the result of
+ * asynchronous operations performed on channels in the group. The group can
+ * optionally be specified when creating the channel or the channel can be bound
+ * to a <em>default group</em>. Sophisticated users may wish to create their
+ * own asynchronous channel groups or configure the {@code ExecutorService}
+ * that will be used for the default group.
+ *
+ * <p> As with selectors, the implementatin of asynchronous channels can be
+ * replaced by "plugging in" an alternative definition or instance of the {@link
+ * java.nio.channels.spi.AsynchronousChannelProvider} class defined in the
+ * <tt>{@link java.nio.channels.spi}</tt> package. It is not expected that many
+ * developers will actually make use of this facility; it is provided primarily
+ * so that sophisticated users can take advantage of operating-system-specific
+ * asynchronous I/O mechanisms when very high performance is required.
+ *
* <hr width="80%">
* <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
* or method in any class or interface in this package will cause a {@link
* java.lang.NullPointerException NullPointerException} to be thrown.
*
* @since 1.4
+ * @updated 1.7
* @author Mark Reinhold
* @author JSR-51 Expert Group
*/
diff --git a/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java b/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java
new file mode 100644
index 000000000..941364876
--- /dev/null
+++ b/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.channels.spi;
+
+import java.nio.channels.*;
+import java.net.ProtocolFamily;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+import java.util.ServiceConfigurationError;
+import java.util.concurrent.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Service-provider class for asynchronous channels.
+ *
+ * <p> An asynchronous channel provider is a concrete subclass of this class that
+ * has a zero-argument constructor and implements the abstract methods specified
+ * below. A given invocation of the Java virtual machine maintains a single
+ * system-wide default provider instance, which is returned by the {@link
+ * #provider() provider} method. The first invocation of that method will locate
+ * the default provider as specified below.
+ *
+ * <p> All of the methods in this class are safe for use by multiple concurrent
+ * threads. </p>
+ *
+ * @since 1.7
+ */
+
+public abstract class AsynchronousChannelProvider {
+ private static Void checkPermission() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("asynchronousChannelProvider"));
+ return null;
+ }
+ private AsynchronousChannelProvider(Void ignore) { }
+
+ /**
+ * Initializes a new instance of this class.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link RuntimePermission}<tt>("asynchronousChannelProvider")</tt>
+ */
+ protected AsynchronousChannelProvider() {
+ this(checkPermission());
+ }
+
+ // lazy initialization of default provider
+ private static class ProviderHolder {
+ static final AsynchronousChannelProvider provider = load();
+
+ private static AsynchronousChannelProvider load() {
+ return AccessController
+ .doPrivileged(new PrivilegedAction<AsynchronousChannelProvider>() {
+ public AsynchronousChannelProvider run() {
+ AsynchronousChannelProvider p;
+ p = loadProviderFromProperty();
+ if (p != null)
+ return p;
+ p = loadProviderAsService();
+ if (p != null)
+ return p;
+ return sun.nio.ch.DefaultAsynchronousChannelProvider.create();
+ }});
+ }
+
+ private static AsynchronousChannelProvider loadProviderFromProperty() {
+ String cn = System.getProperty("java.nio.channels.spi.AsynchronousChannelProvider");
+ if (cn == null)
+ return null;
+ try {
+ Class<?> c = Class.forName(cn, true,
+ ClassLoader.getSystemClassLoader());
+ return (AsynchronousChannelProvider)c.newInstance();
+ } catch (ClassNotFoundException x) {
+ throw new ServiceConfigurationError(null, x);
+ } catch (IllegalAccessException x) {
+ throw new ServiceConfigurationError(null, x);
+ } catch (InstantiationException x) {
+ throw new ServiceConfigurationError(null, x);
+ } catch (SecurityException x) {
+ throw new ServiceConfigurationError(null, x);
+ }
+ }
+
+ private static AsynchronousChannelProvider loadProviderAsService() {
+ ServiceLoader<AsynchronousChannelProvider> sl =
+ ServiceLoader.load(AsynchronousChannelProvider.class,
+ ClassLoader.getSystemClassLoader());
+ Iterator<AsynchronousChannelProvider> i = sl.iterator();
+ for (;;) {
+ try {
+ return (i.hasNext()) ? i.next() : null;
+ } catch (ServiceConfigurationError sce) {
+ if (sce.getCause() instanceof SecurityException) {
+ // Ignore the security exception, try the next provider
+ continue;
+ }
+ throw sce;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the system-wide default asynchronous channel provider for this
+ * invocation of the Java virtual machine.
+ *
+ * <p> The first invocation of this method locates the default provider
+ * object as follows: </p>
+ *
+ * <ol>
+ *
+ * <li><p> If the system property
+ * <tt>java.nio.channels.spi.AsynchronousChannelProvider</tt> is defined
+ * then it is taken to be the fully-qualified name of a concrete provider class.
+ * The class is loaded and instantiated; if this process fails then an
+ * unspecified error is thrown. </p></li>
+ *
+ * <li><p> If a provider class has been installed in a jar file that is
+ * visible to the system class loader, and that jar file contains a
+ * provider-configuration file named
+ * <tt>java.nio.channels.spi.AsynchronousChannelProvider</tt> in the resource
+ * directory <tt>META-INF/services</tt>, then the first class name
+ * specified in that file is taken. The class is loaded and
+ * instantiated; if this process fails then an unspecified error is
+ * thrown. </p></li>
+ *
+ * <li><p> Finally, if no provider has been specified by any of the above
+ * means then the system-default provider class is instantiated and the
+ * result is returned. </p></li>
+ *
+ * </ol>
+ *
+ * <p> Subsequent invocations of this method return the provider that was
+ * returned by the first invocation. </p>
+ *
+ * @return The system-wide default AsynchronousChannel provider
+ */
+ public static AsynchronousChannelProvider provider() {
+ return ProviderHolder.provider;
+ }
+
+ /**
+ * Constructs a new asynchronous channel group with a fixed thread pool.
+ *
+ * @param nThreads
+ * The number of threads in the pool
+ * @param threadFactory
+ * The factory to use when creating new threads
+ *
+ * @throws IllegalArgumentException
+ * If {@code nThreads <= 0}
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see AsynchronousChannelGroup#withFixedThreadPool
+ */
+ public abstract AsynchronousChannelGroup
+ openAsynchronousChannelGroup(int nThreads, ThreadFactory threadFactory) throws IOException;
+
+ /**
+ * Constructs a new asynchronous channel group with the given thread pool.
+ *
+ * @param executor
+ * The thread pool
+ * @param initialSize
+ * A value {@code >=0} or a negative value for implementation
+ * specific default
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see AsynchronousChannelGroup#withCachedThreadPool
+ */
+ public abstract AsynchronousChannelGroup
+ openAsynchronousChannelGroup(ExecutorService executor, int initialSize) throws IOException;
+
+ /**
+ * Opens an asynchronous server-socket channel.
+ *
+ * @param group
+ * The group to which the channel is bound, or {@code null} to
+ * bind to the default group
+ *
+ * @return The new channel
+ *
+ * @throws IllegalChannelGroupException
+ * If the provider that created the group differs from this provider
+ * @throws ShutdownChannelGroupException
+ * The group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract AsynchronousServerSocketChannel openAsynchronousServerSocketChannel
+ (AsynchronousChannelGroup group) throws IOException;
+
+ /**
+ * Opens an asynchronous socket channel.
+ *
+ * @param group
+ * The group to which the channel is bound, or {@code null} to
+ * bind to the default group
+ *
+ * @return The new channel
+ *
+ * @throws IllegalChannelGroupException
+ * If the provider that created the group differs from this provider
+ * @throws ShutdownChannelGroupException
+ * The group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract AsynchronousSocketChannel openAsynchronousSocketChannel
+ (AsynchronousChannelGroup group) throws IOException;
+
+ /**
+ * Opens an asynchronous datagram channel.
+ *
+ * @param family
+ * The protocol family, or {@code null} for the default protocol
+ * family
+ * @param group
+ * The group to which the channel is bound, or {@code null} to
+ * bind to the default group
+ *
+ * @return The new channel
+ *
+ * @throws IllegalChannelGroupException
+ * If the provider that created the group differs from this provider
+ * @throws ShutdownChannelGroupException
+ * The group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract AsynchronousDatagramChannel openAsynchronousDatagramChannel
+ (ProtocolFamily family, AsynchronousChannelGroup group) throws IOException;
+}
diff --git a/src/share/classes/java/nio/channels/spi/SelectorProvider.java b/src/share/classes/java/nio/channels/spi/SelectorProvider.java
index dc61c9c27..d03800e22 100644
--- a/src/share/classes/java/nio/channels/spi/SelectorProvider.java
+++ b/src/share/classes/java/nio/channels/spi/SelectorProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -89,8 +89,8 @@ public abstract class SelectorProvider {
if (cn == null)
return false;
try {
- Class c = Class.forName(cn, true,
- ClassLoader.getSystemClassLoader());
+ Class<?> c = Class.forName(cn, true,
+ ClassLoader.getSystemClassLoader());
provider = (SelectorProvider)c.newInstance();
return true;
} catch (ClassNotFoundException x) {
diff --git a/src/share/classes/java/nio/channels/spi/package.html b/src/share/classes/java/nio/channels/spi/package.html
index 5b3ddd299..5960da38e 100644
--- a/src/share/classes/java/nio/channels/spi/package.html
+++ b/src/share/classes/java/nio/channels/spi/package.html
@@ -1,5 +1,5 @@
<!--
- Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -29,8 +29,8 @@
Service-provider classes for the <tt>{@link java.nio.channels}</tt> package.
-<p> Only developers who are defining new selector providers should need to make
-direct use of this package. </p>
+<p> Only developers who are defining new selector providers or asynchronous
+channel providers should need to make direct use of this package. </p>
<p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
or method in any class or interface in this package will cause a {@link
diff --git a/src/share/classes/java/nio/file/AccessDeniedException.java b/src/share/classes/java/nio/file/AccessDeniedException.java
new file mode 100644
index 000000000..82a4fd2d5
--- /dev/null
+++ b/src/share/classes/java/nio/file/AccessDeniedException.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when a file system operation is denied, typically
+ * due to a file permission or other access check.
+ *
+ * <p> This exception is not related to the {@link
+ * java.security.AccessControlException AccessControlException} or {@link
+ * SecurityException} thrown by access controllers or security managers when
+ * access to a file is denied.
+ *
+ * @since 1.7
+ */
+
+public class AccessDeniedException
+ extends FileSystemException
+{
+ private static final long serialVersionUID = 4943049599949219617L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ */
+ public AccessDeniedException(String file) {
+ super(file);
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ * @param other
+ * a string identifying the other file or {@code null} if not known
+ * @param reason
+ * a reason message with additional information or {@code null}
+ */
+ public AccessDeniedException(String file, String other, String reason) {
+ super(file, other, reason);
+ }
+}
diff --git a/src/share/classes/java/nio/file/AccessMode.java b/src/share/classes/java/nio/file/AccessMode.java
new file mode 100644
index 000000000..240837681
--- /dev/null
+++ b/src/share/classes/java/nio/file/AccessMode.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines access modes used to test the accessibility of a file.
+ *
+ * @since 1.7
+ *
+ * @see FileRef#checkAccess
+ */
+
+public enum AccessMode {
+ /**
+ * Test read access.
+ */
+ READ,
+ /**
+ * Test write access.
+ */
+ WRITE,
+ /**
+ * Test execute access.
+ */
+ EXECUTE;
+}
diff --git a/src/share/classes/java/nio/file/AtomicMoveNotSupportedException.java b/src/share/classes/java/nio/file/AtomicMoveNotSupportedException.java
new file mode 100644
index 000000000..503d24c2e
--- /dev/null
+++ b/src/share/classes/java/nio/file/AtomicMoveNotSupportedException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when a file cannot be moved as an atomic file system
+ * operation.
+ *
+ * @since 1.7
+ */
+
+public class AtomicMoveNotSupportedException
+ extends FileSystemException
+{
+ static final long serialVersionUID = 5402760225333135579L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param source
+ * a string identifying the source file or {@code null} if not known
+ * @param target
+ * a string identifying the target file or {@code null} if not known
+ * @param reason
+ * a reason message with additional information
+ */
+ public AtomicMoveNotSupportedException(String source,
+ String target,
+ String reason)
+ {
+ super(source, target, reason);
+ }
+}
diff --git a/src/windows/native/sun/windows/awt_Unicode.cpp b/src/share/classes/java/nio/file/ClosedDirectoryStreamException.java
index 669062e5d..f938776d1 100644
--- a/src/windows/native/sun/windows/awt_Unicode.cpp
+++ b/src/share/classes/java/nio/file/ClosedDirectoryStreamException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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
@@ -23,24 +23,23 @@
* have any questions.
*/
-#include "awt.h"
+package java.nio.file;
-LPWSTR J2WHelper1(LPWSTR lpw, LPWSTR lpj, int offset, int nChars) {
- memcpy(lpw, lpj + offset, nChars*2);
- lpw[nChars] = '\0';
- return lpw;
-}
-
-LPWSTR JNI_J2WHelper1(JNIEnv *env, LPWSTR lpwstr, jstring jstr) {
-
- int len = env->GetStringLength(jstr);
-
- env->GetStringRegion(jstr, 0, len, lpwstr);
- lpwstr[len] = '\0';
+/**
+ * Unchecked exception thrown when an attempt is made to invoke an operation on
+ * a directory stream that is closed.
+ *
+ * @since 1.7
+ */
- return lpwstr;
-}
+public class ClosedDirectoryStreamException
+ extends IllegalStateException
+{
+ static final long serialVersionUID = 4228386650900895400L;
-LPWSTR J2WHelper(LPWSTR lpw, LPWSTR lpj, int nChars) {
- return J2WHelper1(lpw, lpj, 0, nChars);
+ /**
+ * Constructs an instance of this class.
+ */
+ public ClosedDirectoryStreamException() {
+ }
}
diff --git a/src/share/classes/java/nio/file/ClosedFileSystemException.java b/src/share/classes/java/nio/file/ClosedFileSystemException.java
new file mode 100644
index 000000000..fcd720a86
--- /dev/null
+++ b/src/share/classes/java/nio/file/ClosedFileSystemException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Unchecked exception thrown when an attempt is made to invoke an operation on
+ * a file and the file system is closed.
+ */
+
+public class ClosedFileSystemException
+ extends IllegalStateException
+{
+ static final long serialVersionUID = -8158336077256193488L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public ClosedFileSystemException() {
+ }
+}
diff --git a/src/share/classes/java/nio/file/ClosedWatchServiceException.java b/src/share/classes/java/nio/file/ClosedWatchServiceException.java
new file mode 100644
index 000000000..662d0b6ac
--- /dev/null
+++ b/src/share/classes/java/nio/file/ClosedWatchServiceException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Unchecked exception thrown when an attempt is made to invoke an operation on
+ * a watch service that is closed.
+ */
+
+public class ClosedWatchServiceException
+ extends IllegalStateException
+{
+ static final long serialVersionUID = 1853336266231677732L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public ClosedWatchServiceException() {
+ }
+}
diff --git a/src/share/classes/java/nio/file/CopyOption.java b/src/share/classes/java/nio/file/CopyOption.java
new file mode 100644
index 000000000..24534741f
--- /dev/null
+++ b/src/share/classes/java/nio/file/CopyOption.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * An object that configures how to copy or move a file.
+ *
+ * <p> Objects of this type may be used with the {@link Path#copyTo copyTo} and
+ * {@link Path#moveTo moveTo} methods to configure how a file is copied or moved.
+ *
+ * <p> The {@link StandardCopyOption} enumeration type defines the
+ * <i>standard</i> options.
+ *
+ * @since 1.7
+ */
+
+public interface CopyOption {
+}
diff --git a/src/share/classes/java/nio/file/DirectoryNotEmptyException.java b/src/share/classes/java/nio/file/DirectoryNotEmptyException.java
new file mode 100644
index 000000000..482b47641
--- /dev/null
+++ b/src/share/classes/java/nio/file/DirectoryNotEmptyException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when a file system operation fails because a
+ * directory is not empty.
+ *
+ * @since 1.7
+ */
+
+public class DirectoryNotEmptyException
+ extends FileSystemException
+{
+ static final long serialVersionUID = 3056667871802779003L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param dir
+ * a string identifying the directory or {@code null} if not known
+ */
+ public DirectoryNotEmptyException(String dir) {
+ super(dir);
+ }
+}
diff --git a/src/share/classes/java/nio/file/DirectoryStream.java b/src/share/classes/java/nio/file/DirectoryStream.java
new file mode 100644
index 000000000..589d7b4f5
--- /dev/null
+++ b/src/share/classes/java/nio/file/DirectoryStream.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.util.Iterator;
+import java.io.Closeable;
+
+/**
+ * An object to iterate over the entries in a directory. A directory stream
+ * allows for convenient use of the for-each construct:
+ * <pre>
+ * Path dir = ...
+ * DirectoryStream&lt;Path&gt; stream = dir.newDirectoryStream();
+ * try {
+ * for (Path entry: stream) {
+ * ..
+ * }
+ * } finally {
+ * stream.close();
+ * }
+ * </pre>
+ *
+ * <p><b> A {@code DirectoryStream} is not a general-purpose {@code Iterable}.
+ * While this interface extends {@code Iterable}, the {@code iterator} method
+ * may only be invoked once to obtain the iterator; a second, or subsequent,
+ * call to the {@code iterator} method throws {@code IllegalStateException}. </b>
+ *
+ * <p> A {@code DirectoryStream} is opened upon creation and is closed by
+ * invoking the {@link #close close} method. Closing the directory stream
+ * releases any resources associated with the stream. The {@link
+ * Files#withDirectory Files.withDirectory} utility method is useful for cases
+ * where a task is performed on entries in a directory. This method automatically
+ * closes the directory stream when iteration is complete (or an error occurs).
+ * Once a directory stream is closed, all further method invocations on the
+ * iterator throw {@link java.util.concurrent.ConcurrentModificationException}
+ * with cause {@link ClosedDirectoryStreamException}.
+ *
+ * <p> A directory stream is not required to be <i>asynchronously closeable</i>.
+ * If a thread is blocked on the directory stream's iterator reading from the
+ * directory, and another thread invokes the {@code close} method, then the
+ * second thread may block until the read operation is complete.
+ *
+ * <p> The {@link Iterator#hasNext() hasNext} and {@link Iterator#next() next}
+ * methods can encounter an I/O error when iterating over the directory in which
+ * case {@code ConcurrentModificationException} is thrown with cause
+ * {@link java.io.IOException}. The {@code hasNext} method is guaranteed to
+ * read-ahead by at least one element. This means that if the {@code hasNext}
+ * method returns {@code true} and is followed by a call to the {@code next}
+ * method then it is guaranteed not to fail with a {@code
+ * ConcurrentModificationException}.
+ *
+ * <p> The elements returned by the iterator are in no specific order. Some file
+ * systems maintain special links to the directory itself and the directory's
+ * parent directory. Entries representing these links are not returned by the
+ * iterator.
+ *
+ * <p> The iterator's {@link Iterator#remove() remove} method removes the
+ * directory entry for the last element returned by the iterator, as if by
+ * invoking the {@link FileRef#delete delete} method. If an I/O error or
+ * security exception occurs then {@code ConcurrentModificationException} is
+ * thrown with the cause.
+ *
+ * <p> The iterator is <i>weakly consistent</i>. It is thread safe but does not
+ * freeze the directory while iterating, so it may (or may not) reflect updates
+ * to the directory that occur after the {@code DirectoryStream} is created.
+ *
+ * @param <T> The type of element returned by the iterator
+ *
+ * @since 1.7
+ *
+ * @see Path#newDirectoryStream
+ */
+
+public interface DirectoryStream<T>
+ extends Closeable, Iterable<T>
+{
+ /**
+ * An interface that is implemented by objects that decide if a directory
+ * entry should be accepted or filtered. A {@code Filter} is passed as the
+ * parameter to the {@link Path#newDirectoryStream(DirectoryStream.Filter)
+ * newDirectoryStream} method when opening a directory to iterate over the
+ * entries in the directory.
+ *
+ * <p> The {@link DirectoryStreamFilters} class defines factory methods to
+ * create filters for a number of common usages and also methods to combine
+ * filters.
+ *
+ * @param <T> the type of the directory entry
+ *
+ * @since 1.7
+ */
+ public static interface Filter<T> {
+ /**
+ * Decides if the given directory entry should be accepted or filtered.
+ *
+ * @param entry
+ * the directory entry to be tested
+ *
+ * @return {@code true} if the directory entry should be accepted
+ */
+ boolean accept(T entry);
+ }
+
+ /**
+ * Returns the iterator associated with this {@code DirectoryStream}.
+ *
+ * @return the iterator associated with this {@code DirectoryStream}
+ *
+ * @throws IllegalStateException
+ * if this directory stream is closed or the iterator has already
+ * been returned
+ */
+ @Override
+ Iterator<T> iterator();
+}
diff --git a/src/share/classes/java/nio/file/DirectoryStreamFilters.java b/src/share/classes/java/nio/file/DirectoryStreamFilters.java
new file mode 100644
index 000000000..b582bbf93
--- /dev/null
+++ b/src/share/classes/java/nio/file/DirectoryStreamFilters.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.io.IOException;
+import java.io.IOError;
+import sun.nio.fs.MimeType;
+
+/**
+ * This class consists exclusively of static methods that construct or combine
+ * filters.
+ *
+ * @since 1.7
+ */
+
+public final class DirectoryStreamFilters {
+ private DirectoryStreamFilters() { }
+
+ /**
+ * Constructs a directory stream filter that filters directory entries by
+ * their <a href="http://www.ietf.org/rfc/rfc2045.txt">MIME</a> content
+ * type. The directory stream filter's {@link
+ * java.nio.file.DirectoryStream.Filter#accept accept} method returns {@code
+ * true} if the content type of the directory entry can be determined by
+ * invoking the {@link Files#probeContentType probeContentType} method, and
+ * the content type matches the given content type.
+ *
+ * <p> The {@code type} parameter is the value of a Multipurpose Internet
+ * Mail Extension (MIME) content type as defined by <a
+ * href="http://www.ietf.org/rfc/rfc2045.txt"><i>RFC&nbsp;2045: Multipurpose
+ * Internet Mail Extensions (MIME) Part One: Format of Internet Message
+ * Bodies</i></a>. It is parsable according to the grammar in the RFC. Any
+ * space characters (<code>'&#92;u0020'</code>) surrounding its components are
+ * ignored. The {@code type} parameter is parsed into its primary and subtype
+ * components which are used to match the primary and subtype components of
+ * each directory entry's content type. Parameters are not allowed. The
+ * primary type matches if it has value {@code '*'} or is equal to the
+ * primary type of the directory entry's content type without regard to
+ * case. The subtype matches if has the value {@code '*'} or is equal to the
+ * subtype of the directory entry's content type without regard to case. If
+ * both the primary and subtype match then the filter's {@code accept} method
+ * returns {@code true}. If the content type of a directory entry cannot be
+ * determined then the entry is filtered.
+ *
+ * <p> The {@code accept} method of the resulting directory stream filter
+ * throws {@link IOError} if the probing of the content type fails by
+ * throwing an {@link IOException}. Security exceptions are also propogated
+ * to the caller of the {@code accept} method.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we require to list only the HTML files in a directory.
+ * <pre>
+ * DirectoryStream.Filter&lt;FileRef&gt; filter =
+ * DirectoryStreamFilters.newContentTypeFilter("text/html");
+ * </pre>
+ *
+ * @param type
+ * the content type
+ *
+ * @return a new directory stream filter
+ *
+ * @throws IllegalArgumentException
+ * if the {@code type} parameter cannot be parsed as a MIME type
+ * or it has parameters
+ */
+ public static <T extends FileRef> DirectoryStream.Filter<T>
+ newContentTypeFilter(String type)
+ {
+ final MimeType matchType = MimeType.parse(type);
+ if (matchType.hasParameters())
+ throw new IllegalArgumentException("Parameters not allowed");
+ return new DirectoryStream.Filter<T>() {
+ @Override
+ public boolean accept(T entry) {
+ String fileType;
+ try {
+ fileType = Files.probeContentType(entry);
+ } catch (IOException x) {
+ throw new IOError(x);
+ }
+ if (fileType != null) {
+ return matchType.match(fileType);
+ }
+ return false;
+ }
+ };
+ }
+
+ /**
+ * Returns a directory stream filter that {@link DirectoryStream.Filter#accept
+ * accepts} a directory entry if the entry is accepted by all of the given
+ * filters.
+ *
+ * <p> This method returns a filter that invokes, in iterator order, the
+ * {@code accept} method of each of the filters. If {@code false} is returned
+ * by any of the filters then the directory entry is filtered. If the
+ * directory entry is not filtered then the resulting filter accepts the
+ * entry. If the iterator returns zero elements then the resulting filter
+ * accepts all directory entries.
+ *
+ * <p> <b>Usage Example:</b>
+ * <pre>
+ * List&lt;DirectoryStream.Filter&lt;? super Path&gt;&gt; filters = ...
+ * DirectoryStream.Filter&lt;Path&gt; filter = DirectoryStreamFilters.allOf(filters);
+ * </pre>
+ *
+ * @param filters
+ * the sequence of filters
+ *
+ * @return the resulting filter
+ */
+ public static <T> DirectoryStream.Filter<T>
+ allOf(final Iterable<? extends DirectoryStream.Filter<? super T>> filters)
+ {
+ if (filters == null)
+ throw new NullPointerException("'filters' is null");
+ return new DirectoryStream.Filter<T>() {
+ @Override
+ public boolean accept(T entry) {
+ for (DirectoryStream.Filter<? super T> filter: filters) {
+ if (!filter.accept(entry))
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ /**
+ * Returns a directory stream filter that {@link DirectoryStream.Filter#accept
+ * accepts} a directory entry if the entry is accepted by one or more of
+ * the given filters.
+ *
+ * <p> This method returns a filter that invokes, in iteration order, the
+ * {@code accept} method of each of filter. If {@code true} is returned by
+ * any of the filters then the directory entry is accepted. If none of the
+ * filters accepts the directory entry then it is filtered. If the iterator
+ * returns zero elements then the resulting filter filters all directory
+ * entries.
+ *
+ * @param filters
+ * the sequence of filters
+ *
+ * @return the resulting filter
+ */
+ public static <T> DirectoryStream.Filter<T>
+ anyOf(final Iterable<? extends DirectoryStream.Filter<? super T>> filters)
+ {
+ if (filters == null)
+ throw new NullPointerException("'filters' is null");
+ return new DirectoryStream.Filter<T>() {
+ @Override
+ public boolean accept(T entry) {
+ for (DirectoryStream.Filter<? super T> filter: filters) {
+ if (filter.accept(entry))
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+
+ /**
+ * Returns a directory stream filter that is the <em>complement</em> of the
+ * given filter. The resulting filter {@link
+ * java.nio.file.DirectoryStream.Filter#accept accepts} a directory entry
+ * if filtered by the given filter, and filters any entries that are accepted
+ * by the given filter.
+ *
+ * @param filter
+ * the given filter
+ *
+ * @return the resulting filter that is the complement of the given filter
+ */
+ public static <T> DirectoryStream.Filter<T>
+ complementOf(final DirectoryStream.Filter<T> filter)
+ {
+ if (filter == null)
+ throw new NullPointerException("'filter' is null");
+ return new DirectoryStream.Filter<T>() {
+ @Override
+ public boolean accept(T entry) {
+ return !filter.accept(entry);
+ }
+ };
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileAction.java b/src/share/classes/java/nio/file/FileAction.java
new file mode 100644
index 000000000..58088c2f8
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileAction.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.io.IOException;
+
+/**
+ * An interface that is implemented by objects that operate on a file. An
+ * implementation of this interface is provided to the {@link Files#withDirectory
+ * withDirectory} utility method so that the file action is {@link #invoke
+ * invoked} for all accepted entries in the directory, after which, the directory
+ * is automatically closed.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we require to perform a task on all class files in a directory:
+ * <pre>
+ * Path dir = ...
+ * Files.withDirectory(dir, "*.class", new FileAction&lt;Path&gt;() {
+ * public void invoke(Path entry) {
+ * :
+ * }
+ * });
+ * </pre>
+ *
+ * @param <T> the type of file reference
+ *
+ * @since 1.7
+ */
+
+public interface FileAction<T extends FileRef> {
+ /**
+ * Invoked for a file.
+ *
+ * @param file
+ * the file
+ *
+ * @throws IOException
+ * if the block terminates due an uncaught I/O exception
+ */
+ void invoke(T file) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/FileAlreadyExistsException.java b/src/share/classes/java/nio/file/FileAlreadyExistsException.java
new file mode 100644
index 000000000..ff60e6371
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileAlreadyExistsException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when an attempt is made to create a file or
+ * directory and a file of that name already exists.
+ *
+ * @since 1.7
+ */
+
+public class FileAlreadyExistsException
+ extends FileSystemException
+{
+ static final long serialVersionUID = 7579540934498831181L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ */
+ public FileAlreadyExistsException(String file) {
+ super(file);
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ * @param other
+ * a string identifying the other file or {@code null} if not known
+ * @param reason
+ * a reason message with additional information or {@code null}
+ */
+ public FileAlreadyExistsException(String file, String other, String reason) {
+ super(file, other, reason);
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileRef.java b/src/share/classes/java/nio/file/FileRef.java
new file mode 100644
index 000000000..2606a879c
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileRef.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.*;
+import java.nio.channels.SeekableByteChannel;
+import java.io.IOException;
+
+/**
+ * A reference to a file.
+ *
+ * <p> A {@code FileRef} is an object that locates a file and defines methods to
+ * access the file. The means by which the file is located depends on the
+ * implementation. In many cases, a file is located by a {@link Path} but it may
+ * be located by other means such as a file-system identifier.
+ *
+ * <p> This interface defines the following operations:
+ * <ul>
+ * <li><p> The {@link #newByteChannel newByteChannel} method
+ * may be used to open a file and obtain a byte channel for reading or
+ * writing. </p></li>
+ * <li><p> The {@link #delete delete} method may be used to delete a file.
+ * </p></li>
+ * <li><p> The {@link #checkAccess checkAccess} method may be used to check
+ * the existence or accessibility of a file. </p></li>
+ * <li><p> The {@link #isSameFile isSameFile} method may be used to test if
+ * two file references locate the same file. </p></li>
+ * <li><p> The {@link #getFileStore getFileStore} method may be used to
+ * obtain the {@link FileStore} representing the storage where a file is
+ * located. </p></li>
+ * </ul>
+ *
+ * <p> Access to associated metadata or file attributes requires an appropriate
+ * {@link FileAttributeView FileAttributeView}. The {@link
+ * #getFileAttributeView(Class,LinkOption[]) getFileAttributeView(Class,LinkOption[])}
+ * method may be used to obtain a file attribute view that defines type-safe
+ * methods to read or update file attributes. The {@link
+ * #getFileAttributeView(String,LinkOption[]) getFileAttributeView(String,LinkOption[])}
+ * method may be used to obtain a file attribute view where dynamic access to
+ * file attributes where required.
+ *
+ * <p> A {@code FileRef} is immutable and safe for use by multiple concurrent
+ * threads.
+ *
+ * @since 1.7
+ */
+
+public interface FileRef {
+
+ /**
+ * Opens the file referenced by this object, returning a seekable byte
+ * channel to access the file.
+ *
+ * <p> The {@code options} parameter determines how the file is opened.
+ * The {@link StandardOpenOption#READ READ} and {@link StandardOpenOption#WRITE
+ * WRITE} options determine if the file should be opened for reading and/or
+ * writing. If neither option (or the {@link StandardOpenOption#APPEND APPEND}
+ * option) is contained in the array then the file is opened for reading.
+ * By default reading or writing commences at the beginning of the file.
+ *
+ * <p> In the addition to {@code READ} and {@code WRITE}, the following
+ * options may be present:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Option</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#APPEND APPEND} </td>
+ * <td> If this option is present then the file is opened for writing and
+ * each invocation of the channel's {@code write} method first advances
+ * the position to the end of the file and then writes the requested
+ * data. Whether the advancement of the position and the writing of the
+ * data are done in a single atomic operation is system-dependent and
+ * therefore unspecified. This option may not be used in conjunction
+ * with the {@code READ} or {@code TRUNCATE_EXISTING} options. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} </td>
+ * <td> If this option is present then the existing file is truncated to
+ * a size of 0 bytes. This option is ignored when the file is opened only
+ * for reading. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#SYNC SYNC} </td>
+ * <td> Requires that every update to the file's content or metadata be
+ * written synchronously to the underlying storage device. (see <a
+ * href="package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#DSYNC DSYNC} </td>
+ * <td> Requires that every update to the file's content be written
+ * synchronously to the underlying storage device. (see <a
+ * href="package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * </tr>
+ * </table>
+ *
+ * <p> An implementation of this interface may support additional options
+ * defined by the {@link StandardOpenOption} enumeration type or other
+ * implementation specific options.
+ *
+ * <p> The {@link java.nio.channels.Channels} utility classes defines methods
+ * to construct input and output streams where inter-operation with the
+ * {@link java.io} package is required.
+ *
+ * @param options
+ * Options specifying how the file is opened
+ *
+ * @return a new seekable byte channel
+ *
+ * @throws IllegalArgumentException
+ * If an invalid combination of options is specified
+ * @throws UnsupportedOperationException
+ * If an unsupported open option is specified
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the path if the file is
+ * opened for reading. The {@link SecurityManager#checkWrite(String)
+ * checkWrite} method is invoked to check write access to the path
+ * if the file is opened for writing.
+ */
+ SeekableByteChannel newByteChannel(OpenOption... options)
+ throws IOException;
+
+ /**
+ * Returns the {@link FileStore} representing the file store where the file
+ * referenced by this object is stored.
+ *
+ * <p> Once a reference to the {@code FileStore} is obtained it is
+ * implementation specific if operations on the returned {@code FileStore},
+ * or {@link FileStoreAttributeView} objects obtained from it, continue
+ * to depend on the existence of the file. In particular the behavior is not
+ * defined for the case that the file is deleted or moved to a different
+ * file store.
+ *
+ * @return The file store where the file is stored
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file, and in
+ * addition it checks {@link RuntimePermission}<tt>
+ * ("getFileStoreAttributes")</tt>
+ */
+ FileStore getFileStore() throws IOException;
+
+ /**
+ * Checks the existence and optionally the accessibility of the file
+ * referenced by this object.
+ *
+ * <p> This method checks the existence of a file and that this Java virtual
+ * machine has appropriate privileges that would allow it access the file
+ * according to all of access modes specified in the {@code modes} parameter
+ * as follows:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Value</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link AccessMode#READ READ} </td>
+ * <td> Checks that the file exists and that the Java virtual machine has
+ * permission to read the file. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link AccessMode#WRITE WRITE} </td>
+ * <td> Checks that the file exists and that the Java virtual machine has
+ * permission to write to the file, </td>
+ * </tr>
+ * <tr>
+ * <td> {@link AccessMode#EXECUTE EXECUTE} </td>
+ * <td> Checks that the file exists and that the Java virtual machine has
+ * permission to {@link Runtime#exec execute} the file. The semantics
+ * may differ when checking access to a directory. For example, on UNIX
+ * systems, checking for {@code EXECUTE} access checks that the Java
+ * virtual machine has permission to search the directory in order to
+ * access file or subdirectories. </td>
+ * </tr>
+ * </table>
+ *
+ * <p> If the {@code modes} parameter is of length zero, then the existence
+ * of the file is checked.
+ *
+ * <p> This method follows symbolic links if the file referenced by this
+ * object is a symbolic link. Depending on the implementation, this method
+ * may require to read file permissions, access control lists, or other
+ * file attributes in order to check the effective access to the file. To
+ * determine the effective access to a file may require access to several
+ * attributes and so in some implementations this method may not be atomic
+ * with respect to other file system operations. Furthermore, as the result
+ * of this method is immediately outdated, there is no guarantee that a
+ * subsequence access will succeed (or even that it will access the same
+ * file). Care should be taken when using this method in security sensitive
+ * applications.
+ *
+ * @param modes
+ * The access modes to check; may have zero elements
+ *
+ * @throws UnsupportedOperationException
+ * An implementation is required to support checking for
+ * {@code READ}, {@code WRITE}, and {@code EXECUTE} access. This
+ * exception is specified to allow for the {@code Access} enum to
+ * be extended in future releases.
+ * @throws NoSuchFileException
+ * If a file does not exist <i>(optional specific exception)</i>
+ * @throws AccessDeniedException
+ * The requested access would be denied or the access cannot be
+ * determined because the Java virtual machine has insufficient
+ * privileges or other reasons. <i>(optional specific exception)</i>
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * is invoked when checking read access to the file or only the
+ * existence of the file, the {@link SecurityManager#checkWrite(String)
+ * checkWrite} is invoked when checking write access to the file,
+ * and {@link SecurityManager#checkExec(String) checkExec} is invoked
+ * when checking execute access.
+ */
+ void checkAccess(AccessMode... modes) throws IOException;
+
+ /**
+ * Returns a file attribute view of a given type.
+ *
+ * <p> A file attribute view provides a read-only or updatable view of a
+ * set of file attributes. This method is intended to be used where the file
+ * attribute view defines type-safe methods to read or update the file
+ * attributes. The {@code type} parameter is the type of the attribute view
+ * required and the method returns an instance of that type if supported.
+ * The {@link BasicFileAttributeView} type supports access to the basic
+ * attributes of a file. Invoking this method to select a file attribute
+ * view of that type will always return an instance of that class.
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled by the resulting file attribute view for the case that the
+ * file is a symbolic link. By default, symbolic links are followed. If the
+ * option {@link LinkOption#NOFOLLOW_LINKS NOFOLLOW_LINKS} is present then
+ * symbolic links are not followed. This option is ignored by implementations
+ * that do not support symbolic links.
+ *
+ * @param type
+ * The {@code Class} object corresponding to the file attribute view
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return A file attribute view of the specified type, or {@code null} if
+ * the attribute view type is not available
+ *
+ * @throws UnsupportedOperationException
+ * If options contains an unsupported option. This exception is
+ * specified to allow the {@code LinkOption} enum be extended
+ * in future releases.
+ *
+ * @see Attributes#readBasicFileAttributes
+ */
+ <V extends FileAttributeView> V getFileAttributeView(Class<V> type, LinkOption... options);
+
+ /**
+ * Returns a file attribute view of the given name.
+ *
+ * <p> A file attribute view provides a read-only or updatable view of a
+ * set of file attributes. This method is intended to be used where
+ * <em>dynamic access</em> to the file attributes is required. The {@code
+ * name} parameter specifies the {@link FileAttributeView#name name} of the
+ * file attribute view and this method returns an instance of that view if
+ * supported. The {@link BasicFileAttributeView} type supports access to the
+ * basic attributes of a file and is name {@code "basic"}. Invoking this
+ * method to select a file attribute view named {@code "basic"} will always
+ * return an instance of that class.
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled by the resulting file attribute view for the case that the
+ * file is a symbolic link. By default, symbolic links are followed. If the
+ * option {@link LinkOption#NOFOLLOW_LINKS NOFOLLOW_LINKS} is present then
+ * symbolic links are not followed. This option is ignored by implementations
+ * that do not support symbolic links.
+ *
+ * @param name
+ * The name of the file attribute view
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return A file attribute view of the given name, or {@code null} if
+ * the attribute view is not available
+ *
+ * @throws UnsupportedOperationException
+ * If options contains an unsupported option. This exception is
+ * specified to allow the {@code LinkOption} enum be extended
+ * in future releases.
+ */
+ FileAttributeView getFileAttributeView(String name, LinkOption... options);
+
+ /**
+ * Tests if the file referenced by this object is the same file referenced
+ * by another object.
+ *
+ * <p> If this {@code FileRef} and the given {@code FileRef} are {@link
+ * #equals(Object) equal} then this method returns {@code true} without checking
+ * if the file exists. If the {@code FileRef} and the given {@code FileRef}
+ * are associated with different providers, or the given {@code FileRef} is
+ * {@code null} then this method returns {@code false}. Otherwise, this method
+ * checks if both {@code FileRefs} locate the same file, and depending on the
+ * implementation, may require to open or access both files.
+ *
+ * <p> If the file system and files remain static, then this method implements
+ * an equivalence relation for non-null {@code FileRefs}.
+ * <ul>
+ * <li>It is <i>reflexive</i>: for a non-null {@code FileRef} {@code f},
+ * {@code f.isSameFile(f)} should return {@code true}.
+ * <li>It is <i>symmetric</i>: for two non-null {@code FileRefs}
+ * {@code f} and {@code g}, {@code f.isSameFile(g)} will equal
+ * {@code g.isSameFile(f)}.
+ * <li>It is <i>transitive</i>: for three {@code FileRefs}
+ * {@code f}, {@code g}, and {@code h}, if {@code f.isSameFile(g)} returns
+ * {@code true} and {@code g.isSameFile(h)} returns {@code true}, then
+ * {@code f.isSameFile(h)} will return return {@code true}.
+ * </ul>
+ *
+ * @param other
+ * The other file reference
+ *
+ * @return {@code true} if, and only if, this object and the given object
+ * locate the same file
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to both files.
+ *
+ * @see java.nio.file.attribute.BasicFileAttributes#fileKey
+ */
+ boolean isSameFile(FileRef other) throws IOException;
+
+ /**
+ * Deletes the file referenced by this object.
+ *
+ * <p> An implementation may require to examine the file to determine if the
+ * file is a directory. Consequently this method may not be atomic with respect
+ * to other file system operations. If the file is a symbolic-link then the
+ * link is deleted and not the final target of the link.
+ *
+ * <p> If the file is a directory then the directory must be empty. In some
+ * implementations a directory has entries for special files or links that
+ * are created when the directory is created. In such implementations a
+ * directory is considered empty when only the special entries exist.
+ *
+ * <p> On some operating systems it may not be possible to remove a file when
+ * it is open and in use by this Java virtual machine or other programs.
+ *
+ * @throws NoSuchFileException
+ * If the file does not exist <i>(optional specific exception)</i>
+ * @throws DirectoryNotEmptyException
+ * If the file is a directory and could not otherwise be deleted
+ * because the directory is not empty <i>(optional specific
+ * exception)</i>
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkDelete(String)} method
+ * is invoked to check delete access to the file
+ */
+ void delete() throws IOException;
+
+ /**
+ * Tests this object for equality with another object.
+ *
+ * <p> If the given object is not a {@code FileRef} then this method
+ * immediately returns {@code false}.
+ *
+ * <p> For two file references to be considered equal requires that they
+ * are both the same type of {@code FileRef} and encapsulate components
+ * to locate the same file. This method does not access the file system and
+ * the file may not exist.
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * java.lang.Object#equals(Object) Object.equals} method. </p>
+ *
+ * @param ob The object to which this object is to be compared
+ *
+ * @return {@code true} if, and only if, the given object is a {@code FileRef}
+ * that is identical to this {@code FileRef}
+ *
+ * @see #isSameFile
+ */
+ boolean equals(Object ob);
+
+ /**
+ * Returns the hash-code value for this object.
+ *
+ * <p> This method satisfies the general contract of the
+ * {@link java.lang.Object#hashCode() Object.hashCode} method.
+ */
+ int hashCode();
+}
diff --git a/src/share/classes/java/nio/file/FileStore.java b/src/share/classes/java/nio/file/FileStore.java
new file mode 100644
index 000000000..3f8df1031
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileStore.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.*;
+
+/**
+ * Storage for files. A {@code FileStore} represents a storage pool, device,
+ * partition, volume, concrete file system or other implementation specific means
+ * of file storage. The {@code FileStore} for where a file is stored is obtained
+ * by invoking the {@link FileRef#getFileStore getFileStore} method, or all file
+ * stores can be enumerated by invoking the {@link FileSystem#getFileStores
+ * getFileStores} method.
+ *
+ * <p> In addition to the methods defined by this class, a file store may support
+ * one or more {@link FileStoreAttributeView FileStoreAttributeView} classes
+ * that provide a read-only or updatable view of a set of file store attributes.
+ * File stores associated with the default provider support the {@link
+ * FileStoreSpaceAttributeView} to read the space related attributes of the
+ * file store.
+ *
+ * @since 1.7
+ */
+
+public abstract class FileStore {
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected FileStore() {
+ }
+
+ /**
+ * Returns the name of this file store. The format of the name is highly
+ * implementation specific. It will typically be the name of the storage
+ * pool or volume.
+ *
+ * <p> The string returned by this method may differ from the string
+ * returned by the {@link Object#toString() toString} method.
+ *
+ * @return the name of this file store
+ */
+ public abstract String name();
+
+ /**
+ * Returns the <em>type</em> of this file store. The format of the string
+ * returned by this method is highly implementation specific. It may
+ * indicate, for example, the format used or if the file store is local
+ * or remote.
+ *
+ * @return a string representing the type of this file store
+ */
+ public abstract String type();
+
+ /**
+ * Tells whether this file store is read-only. A file store is read-only if
+ * it does not support write operations or other changes to files. Any
+ * attempt to create a file, open an existing file for writing etc. causes
+ * an {@code IOException} to be thrown.
+ *
+ * @return {@code true} if, and only if, this file store is read-only
+ */
+ public abstract boolean isReadOnly();
+
+ /**
+ * Tells whether or not this file store supports the file attributes
+ * identified by the given file attribute view.
+ *
+ * <p> Invoking this method to test if the file store supports {@link
+ * BasicFileAttributeView} will always return {@code true}. In the case of
+ * the default provider, this method cannot guarantee to give the correct
+ * result when the file store is not a local storage device. The reasons for
+ * this are implementation specific and therefore unspecified.
+ *
+ * @param type
+ * the file attribute view type
+ *
+ * @return {@code true} if, and only if, the file attribute view is
+ * supported
+ */
+ public abstract boolean supportsFileAttributeView(Class<? extends FileAttributeView> type);
+
+ /**
+ * Tells whether or not this file store supports the file attributes
+ * identified by the given file attribute view.
+ *
+ * <p> Invoking this method to test if the file store supports {@link
+ * BasicFileAttributeView}, identified by the name "{@code basic}" will
+ * always return {@code true}. In the case of the default provider, this
+ * method cannot guarantee to give the correct result when the file store is
+ * not a local storage device. The reasons for this are implementation
+ * specific and therefore unspecified.
+ *
+ * @param name
+ * the {@link FileAttributeView#name name} of file attribute view
+ *
+ * @return {@code true} if, and only if, the file attribute view is
+ * supported
+ */
+ public abstract boolean supportsFileAttributeView(String name);
+
+ /**
+ * Returns a {@code FileStoreAttributeView} of the given type.
+ *
+ * <p> This method is intended to be used where the file store attribute
+ * view defines type-safe methods to read or update the file store attributes.
+ * The {@code type} parameter is the type of the attribute view required and
+ * the method returns an instance of that type if supported.
+ *
+ * <p> For {@code FileStore} objects created by the default provider, then
+ * the file stores support the {@link FileStoreSpaceAttributeView} that
+ * provides access to space attributes. In that case invoking this method
+ * with a parameter value of {@code FileStoreSpaceAttributeView.class} will
+ * always return an instance of that class.
+ *
+ * @param type
+ * the {@code Class} object corresponding to the attribute view
+ *
+ * @return a file store attribute view of the specified type or
+ * {@code null} if the attribute view is not available
+ */
+ public abstract <V extends FileStoreAttributeView> V
+ getFileStoreAttributeView(Class<V> type);
+
+ /**
+ * Returns a {@code FileStoreAttributeView} of the given name.
+ *
+ * <p> This method is intended to be used where <em>dynamic access</em> to
+ * file store attributes is required. The {@code name} parameter specifies
+ * the {@link FileAttributeView#name name} of the file store attribute view
+ * and this method returns an instance of that view if supported.
+ *
+ * <p> For {@code FileStore} objects created by the default provider, then
+ * the file stores support the {@link FileStoreSpaceAttributeView} that
+ * provides access to space attributes. In that case invoking this method
+ * with a parameter value of {@code "space"} will always return an instance
+ * of that class.
+ *
+ * @param name
+ * the name of the attribute view
+ *
+ * @return a file store attribute view of the given name, or {@code null}
+ * if the attribute view is not available
+ */
+ public abstract FileStoreAttributeView getFileStoreAttributeView(String name);
+}
diff --git a/src/share/classes/java/nio/file/FileSystem.java b/src/share/classes/java/nio/file/FileSystem.java
new file mode 100644
index 000000000..b7cb06325
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileSystem.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.*;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Set;
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Provides an interface to a file system and is the factory for objects to
+ * access files and other objects in the file system.
+ *
+ * <p> The default file system, obtained by invoking the {@link FileSystems#getDefault
+ * FileSystems.getDefault} method, provides access to the file system that is
+ * accessible to the Java virtual machine. The {@link FileSystems} class defines
+ * methods to create file systems that provide access to other types of file
+ * systems.
+ *
+ * <p> A file system is the factory for several types of objects:
+ *
+ * <ul>
+ * <li><p> The {@link #getPath getPath} method converts a system dependent
+ * <em>path string</em>, returning a {@link Path} object that may be used
+ * to locate and access a file. </p></li>
+ * <li><p> The {@link #getPathMatcher getPathMatcher} method is used
+ * to create a {@link PathMatcher} that performs match operations on
+ * paths. </p></li>
+ * <li><p> The {@link #getFileStores getFileStores} method returns an iterator
+ * over the underlying {@link FileStore file-stores}. </p></li>
+ * <li><p> The {@link #getUserPrincipalLookupService getUserPrincipalLookupService}
+ * method returns the {@link UserPrincipalLookupService} to lookup users or
+ * groups by name. </p></li>
+ * <li><p> The {@link #newWatchService newWatchService} method creates a
+ * {@link WatchService} that may be used to watch objects for changes and
+ * events. </p></li>
+ * </ul>
+ *
+ * <p> File systems vary greatly. In some cases the file system is a single
+ * hierarchy of files with one top-level root directory. In other cases it may
+ * have several distinct file hierarchies, each with its own top-level root
+ * directory. The {@link #getRootDirectories getRootDirectories} method may be
+ * used to iterate over the root directories in the file system. A file system
+ * is typically composed of one or more underlying {@link FileStore file-stores}
+ * that provide the storage for the files. Theses file stores can also vary in
+ * the features they support, and the file attributes or <em>meta-data</em> that
+ * they associate with files.
+ *
+ * <p> A file system is open upon creation and can be closed by invoking its
+ * {@link #close() close} method. Once closed, any further attempt to access
+ * objects in the file system cause {@link ClosedFileSystemException} to be
+ * thrown. File systems created by the default {@link FileSystemProvider provider}
+ * cannot be closed.
+ *
+ * <p> A {@code FileSystem} can provide read-only or read-write access to the
+ * file system. Whether or not a file system provides read-only access is
+ * established when the {@code FileSystem} is created and can be tested by invoking
+ * its {@link #isReadOnly() isReadOnly} method. Attempts to write to file stores
+ * by means of an object associated with a read-only file system throws {@link
+ * ReadOnlyFileSystemException}.
+ *
+ * <p> File systems are safe for use by multiple concurrent threads. The {@link
+ * #close close} method may be invoked at any time to close a file system but
+ * whether a file system is <i>asynchronously closeable</i> is provider specific
+ * and therefore unspecified. In other words, if a thread is accessing an
+ * object in a file system, and another thread invokes the {@code close} method
+ * then it may require to block until the first operation is complete. Closing
+ * a file system causes all open channels, watch services, and other {@link
+ * Closeable closeable} objects associated with the file system to be closed.
+ *
+ * @since 1.7
+ */
+
+public abstract class FileSystem
+ implements Closeable
+{
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected FileSystem() {
+ }
+
+ /**
+ * Returns the provider that created this file system.
+ *
+ * @return The provider that created this file system.
+ */
+ public abstract FileSystemProvider provider();
+
+ /**
+ * Closes this file system.
+ *
+ * <p> After a file system is closed then all subsequent access to the file
+ * system, either by methods defined by this class or on objects associated
+ * with this file system, throw {@link ClosedFileSystemException}. If the
+ * file system is already closed then invoking this method has no effect.
+ *
+ * <p> Closing a file system will close all open {@link
+ * java.nio.channels.Channel channels}, {@link DirectoryStream directory-streams},
+ * {@link WatchService watch-service}, and other closeable objects associated
+ * with this file system. The {@link FileSystems#getDefault default} file
+ * system cannot be closed.
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws UnsupportedOperationException
+ * Thrown in the case of the default file system
+ */
+ @Override
+ public abstract void close() throws IOException;
+
+ /**
+ * Tells whether or not this file system is open.
+ *
+ * <p> File systems created by the default provider are always open.
+ *
+ * @return {@code true} if, and only if, this file system is open
+ */
+ public abstract boolean isOpen();
+
+ /**
+ * Tells whether or not this file system allows only read-only access to
+ * its file stores.
+ *
+ * @return {@code true} if, and only if, this file system provides
+ * read-only access
+ */
+ public abstract boolean isReadOnly();
+
+ /**
+ * Returns the name separator, represented as a string.
+ *
+ * <p> The name separator is used to separate names in a path string. An
+ * implementation may support multiple name separators in which case this
+ * method returns an implementation specific <em>default</em> name separator.
+ * This separator is used when creating path strings by invoking the {@link
+ * Path#toString() toString()} method.
+ *
+ * <p> In the case of the default provider, this method returns the same
+ * separator as {@link java.io.File#separator}.
+ *
+ * @return The name separator
+ */
+ public abstract String getSeparator();
+
+ /**
+ * Returns an object to iterate over the paths of the root directories.
+ *
+ * <p> A file system provides access to a file store that may be composed
+ * of a number of distinct file hierarchies, each with its own top-level
+ * root directory. Unless denied by the security manager, each element in
+ * the returned iterator corresponds to the root directory of a distinct
+ * file hierarchy. The order of the elements is not defined. The file
+ * hierarchies may change during the lifetime of the Java virtual machine.
+ * For example, in some implementations, the insertion of removable media
+ * may result in the creation of a new file hierarchy with its own
+ * top-level directory.
+ *
+ * <p> When a security manager is installed, it is invoked to check access
+ * to the each root directory. If denied, the root directory is not returned
+ * by the iterator. In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check read access
+ * to each root directory. It is system dependent if the permission checks
+ * are done when the iterator is obtained or during iteration.
+ *
+ * @return An object to iterate over the root directories
+ */
+ public abstract Iterable<Path> getRootDirectories();
+
+ /**
+ * Returns an object to iterate over the underlying file stores.
+ *
+ * <p> The elements of the returned iterator are the {@link
+ * FileStore FileStores} for this file system. The order of the elements is
+ * not defined and the file stores may change during the lifetime of the
+ * Java virtual machine. When an I/O error occurs, perhaps because a file
+ * store is not accessible, then it is not returned by the iterator.
+ *
+ * <p> In the case of the default provider, and a security manager is
+ * installed, the security manager is invoked to check {@link
+ * RuntimePermission}<tt>("getFileStoreAttributes")</tt>. If denied, then
+ * no file stores are returned by the iterator. In addition, the security
+ * manager's {@link SecurityManager#checkRead(String)} method is invoked to
+ * check read access to the file store's <em>top-most</em> directory. If
+ * denied, the file store is not returned by the iterator. It is system
+ * dependent if the permission checks are done when the iterator is obtained
+ * or during iteration.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we want to print the space usage for all file stores:
+ * <pre>
+ * for (FileStore store: FileSystems.getDefault().getFileStores()) {
+ * FileStoreSpaceAttributes attrs = Attributes.readFileStoreSpaceAttributes(store);
+ * long total = attrs.totalSpace() / 1024;
+ * long used = (attrs.totalSpace() - attrs.unallocatedSpace()) / 1024;
+ * long avail = attrs.usableSpace() / 1024;
+ * System.out.format("%-20s %12d %12d %12d%n", store, total, used, avail);
+ * }
+ * </pre>
+ *
+ * @return An object to iterate over the backing file stores
+ */
+ public abstract Iterable<FileStore> getFileStores();
+
+ /**
+ * Returns the set of the {@link FileAttributeView#name names} of the file
+ * attribute views supported by this {@code FileSystem}.
+ *
+ * <p> The {@link BasicFileAttributeView} is required to be supported and
+ * therefore the set contains at least one element, "basic".
+ *
+ * <p> The {@link FileStore#supportsFileAttributeView(String)
+ * supportsFileAttributeView(String)} method may be used to test if an
+ * underlying {@link FileStore} supports the file attributes identified by a
+ * file attribute view.
+ *
+ * @return An unmodifiable set of the names of the supported file attribute
+ * views
+ */
+ public abstract Set<String> supportedFileAttributeViews();
+
+ /**
+ * Converts a path string to a {@code Path}.
+ *
+ * <p> The parsing and conversion to a path object is inherently
+ * implementation dependent. In the simplest case, the path string is rejected,
+ * and {@link InvalidPathException} thrown, if the path string contains
+ * characters that cannot be converted to characters that are <em>legal</em>
+ * to the file store. For example, on UNIX systems, the NUL (&#92;u0000)
+ * character is not allowed to be present in a path. An implementation may
+ * choose to reject path strings that contain names that are longer than those
+ * allowed by any file store, and where an implementation supports a complex
+ * path syntax, it may choose to reject path strings that are <em>badly
+ * formed</em>.
+ *
+ * <p> In the case of the default provider, path strings are parsed based
+ * on the definition of paths at the platform or virtual file system level.
+ * For example, an operating system may not allow specific characters to be
+ * present in a file name, but a specific underlying file store may impose
+ * different or additional restrictions on the set of legal
+ * characters.
+ *
+ * <p> This method throws {@link InvalidPathException} when the path string
+ * cannot be converted to a path. Where possible, and where applicable,
+ * the exception is created with an {@link InvalidPathException#getIndex
+ * index} value indicating the first position in the {@code path} parameter
+ * that caused the path string to be rejected.
+ *
+ * <p> Invoking this method with an empty path string throws
+ * {@code InvalidPathException}.
+ *
+ * @param path
+ * The path string
+ *
+ * @return A {@code Path} object
+ *
+ * @throws InvalidPathException
+ * If the path string cannot be converted
+ */
+ public abstract Path getPath(String path);
+
+ /**
+ * Returns a {@code PathMatcher} that performs match operations on the
+ * {@code String} representation of {@link Path} objects by interpreting a
+ * given pattern.
+ *
+ * The {@code syntaxAndPattern} parameter identifies the syntax and the
+ * pattern and takes the form:
+ * <blockquote>
+ * <i>syntax</i><b>:</b><i>pattern</i>
+ * </blockquote>
+ * where {@code ':'} stands for itself.
+ *
+ * <p> A {@code FileSystem} implementation supports the "{@code glob}" and
+ * "{@code regex}" syntaxes, and may support others. The value of the syntax
+ * component is compared without regard to case.
+ *
+ * <p> When the syntax is "{@code glob}" then the {@code String}
+ * representation of the path is matched using a limited pattern language
+ * that resembles regular expressions but with a simpler syntax. For example:
+ *
+ * <blockquote>
+ * <table border="0">
+ * <tr>
+ * <td>{@code *.java}</td>
+ * <td>Matches a path that represents a file name ending in {@code .java}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code *.*}</td>
+ * <td>Matches file names containing a dot</td>
+ * </tr>
+ * <tr>
+ * <tr>
+ * <td>{@code *.{java,class}}</td>
+ * <td>Matches file names ending with {@code .java} or {@code .class}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code foo.?}</td>
+ * <td>Matches file names starting with {@code foo.} and a single
+ * character extension</td>
+ * </tr>
+ * <tr>
+ * <td><tt>&#47;home&#47;*&#47;*</tt>
+ * <td>Matches <tt>&#47;home&#47;gus&#47;data</tt> on UNIX platforms</td>
+ * </tr>
+ * <tr>
+ * <td><tt>&#47;home&#47;**</tt>
+ * <td>Matches <tt>&#47;home&#47;gus</tt> and
+ * <tt>&#47;home&#47;gus&#47;data</tt> on UNIX platforms</td>
+ * </tr>
+ * <tr>
+ * <td><tt>C:&#92;&#92;*</tt>
+ * <td>Matches <tt>C:&#92;foo</tt> and <tt>C:&#92;bar</tt> on the Windows
+ * platform (note that the backslash is escaped; as a string literal in the
+ * Java Language the pattern would be <tt>"C:&#92;&#92;&#92;&#92*"</tt>) </td>
+ * </tr>
+ *
+ * </table>
+ * </blockquote>
+ *
+ * <p> The following rules are used to interpret glob patterns:
+ *
+ * <p> <ul>
+ * <li><p> The {@code *} character matches zero or more {@link Character
+ * characters} of a {@link Path#getName(int) name} component without
+ * crossing directory boundaries. </p></li>
+ *
+ * <li><p> The {@code **} characters matches zero or more {@link Character
+ * characters} crossing directory boundaries. </p></li>
+ *
+ * <li><p> The {@code ?} character matches exactly one character of a
+ * name component.</p></li>
+ *
+ * <li><p> The backslash character ({@code \}) is used to escape characters
+ * that would otherwise be interpreted as special characters. The expression
+ * {@code \\} matches a single backslash and "\{" matches a left brace
+ * for example. </p></li>
+ *
+ * <li><p> The {@code [ ]} characters are a <i>bracket expression</i> that
+ * match a single character of a name component out of a set of characters.
+ * For example, {@code [abc]} matches {@code "a"}, {@code "b"}, or {@code "c"}.
+ * The hyphen ({@code -}) may be used to specify a range so {@code [a-z]}
+ * specifies a range that matches from {@code "a"} to {@code "z"} (inclusive).
+ * These forms can be mixed so [abce-g] matches {@code "a"}, {@code "b"},
+ * {@code "c"}, {@code "e"}, {@code "f"} or {@code "g"}. If the character
+ * after the {@code [} is a {@code !} then it is used for negation so {@code
+ * [!a-c]} matches any character except {@code "a"}, {@code "b"}, or {@code
+ * "c"}.
+ * <p> Within a bracket expression the {@code *}, {@code ?} and {@code \}
+ * characters match themselves. The ({@code -}) character matches itself if
+ * it is the first character within the brackets, or the first character
+ * after the {@code !} if negating.</p></li>
+ *
+ * <li><p> The {@code { }} characters are a group of subpatterns, where
+ * the group matches if any subpattern in the group matches. The {@code ","}
+ * character is used to separate the subpatterns. Groups cannot be nested.
+ * </p></li>
+ *
+ * <li><p> All other characters match themselves in an implementation
+ * dependent manner. This includes characters representing any {@link
+ * FileSystem#getSeparator name-separators}. </p></li>
+ *
+ * <li><p> The matching of {@link Path#getRoot root} components is highly
+ * implementation-dependent and is not specified. </p></li>
+ *
+ * </ul>
+ *
+ * <p> When the syntax is "{@code regex}" then the pattern component is a
+ * regular expression as defined by the {@link java.util.regex.Pattern}
+ * class.
+ *
+ * <p> For both the glob and regex syntaxes, the matching details, such as
+ * whether the matching is case sensitive, are implementation-dependent
+ * and therefore not specified.
+ *
+ * @param syntaxAndPattern
+ * The syntax and pattern
+ *
+ * @return A path matcher that may be used to match paths against the pattern
+ *
+ * @throws IllegalArgumentException
+ * If the parameter does not take the form: {@code syntax:pattern}
+ * @throws java.util.regex.PatternSyntaxException
+ * If the pattern is invalid
+ * @throws UnsupportedOperationException
+ * If the pattern syntax is not known to the implementation
+ *
+ * @see Path#newDirectoryStream(String)
+ */
+ public abstract PathMatcher getPathMatcher(String syntaxAndPattern);
+
+ /**
+ * Returns the {@code UserPrincipalLookupService} for this file system
+ * <i>(optional operation)</i>. The resulting lookup service may be used to
+ * lookup user or group names.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we want to make "joe" the owner of a file:
+ * <pre>
+ * Path file = ...
+ * UserPrincipal joe = file.getFileSystem().getUserPrincipalLookupService()
+ * .lookupPrincipalByName("joe");
+ * Attributes.setOwner(file, joe);
+ * </pre>
+ *
+ * @throws UnsupportedOperationException
+ * If this {@code FileSystem} does not does have a lookup service
+ *
+ * @return The {@code UserPrincipalLookupService} for this file system
+ */
+ public abstract UserPrincipalLookupService getUserPrincipalLookupService();
+
+ /**
+ * Constructs a new {@link WatchService} <i>(optional operation)</i>.
+ *
+ * <p> This method constructs a new watch service that may be used to watch
+ * registered objects for changes and events.
+ *
+ * @return a new watch service
+ *
+ * @throws UnsupportedOperationException
+ * If this {@code FileSystem} does not support watching file system
+ * objects for changes and events. This exception is not thrown
+ * by {@code FileSystems} created by the default provider.
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public abstract WatchService newWatchService() throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/FileSystemAlreadyExistsException.java b/src/share/classes/java/nio/file/FileSystemAlreadyExistsException.java
new file mode 100644
index 000000000..68c4e153b
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileSystemAlreadyExistsException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Runtime exception thrown when an attempt is made to create a file system that
+ * already exists.
+ */
+
+public class FileSystemAlreadyExistsException
+ extends RuntimeException
+{
+ static final long serialVersionUID = -5438419127181131148L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public FileSystemAlreadyExistsException() {
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param msg
+ * the detail message
+ */
+ public FileSystemAlreadyExistsException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileSystemException.java b/src/share/classes/java/nio/file/FileSystemException.java
new file mode 100644
index 000000000..8735dd879
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileSystemException.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a file system operation fails on one or two files. This class is
+ * the general class for file system exceptions.
+ *
+ * @since 1.7
+ */
+
+public class FileSystemException
+ extends IOException
+{
+ static final long serialVersionUID = -3055425747967319812L;
+
+ private final String file;
+ private final String other;
+
+ /**
+ * Constructs an instance of this class. This constructor should be used
+ * when an operation involving one file fails and there isn't any additional
+ * information to explain the reason.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known.
+ */
+ public FileSystemException(String file) {
+ super((String)null);
+ this.file = file;
+ this.other = null;
+ }
+
+ /**
+ * Constructs an instance of this class. This constructor should be used
+ * when an operation involving two files fails, or there is additional
+ * information to explain the reason.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known.
+ * @param other
+ * a string identifying the other file or {@code null} if there
+ * isn't another file or if not known
+ * @param reason
+ * a reason message with additional information or {@code null}
+ */
+ public FileSystemException(String file, String other, String reason) {
+ super(reason);
+ this.file = file;
+ this.other = other;
+ }
+
+ /**
+ * Returns the file used to create this exception.
+ *
+ * @return the file (can be {@code null})
+ */
+ public String getFile() {
+ return file;
+ }
+
+ /**
+ * Returns the other file used to create this exception.
+ *
+ * @return the other file (can be {@code null})
+ */
+ public String getOtherFile() {
+ return other;
+ }
+
+ /**
+ * Returns the string explaining why the file system operation failed.
+ *
+ * @return the string explaining why the file system operation failed
+ */
+ public String getReason() {
+ return super.getMessage();
+ }
+
+ /**
+ * Returns the detail message string.
+ */
+ @Override
+ public String getMessage() {
+ if (file == null && other == null)
+ return getReason();
+ StringBuilder sb = new StringBuilder();
+ if (file != null)
+ sb.append(file);
+ if (other != null) {
+ sb.append(" -> ");
+ sb.append(other);
+ }
+ if (getReason() != null) {
+ sb.append(": ");
+ sb.append(getReason());
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileSystemNotFoundException.java b/src/share/classes/java/nio/file/FileSystemNotFoundException.java
new file mode 100644
index 000000000..7cb58cada
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileSystemNotFoundException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Runtime exception thrown when a file system cannot be found.
+ */
+
+public class FileSystemNotFoundException
+ extends RuntimeException
+{
+ static final long serialVersionUID = 7999581764446402397L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public FileSystemNotFoundException() {
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param msg
+ * the detail message
+ */
+ public FileSystemNotFoundException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileSystems.java b/src/share/classes/java/nio/file/FileSystems.java
new file mode 100644
index 000000000..28885f8af
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileSystems.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.spi.FileSystemProvider;
+import java.net.URI;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.lang.reflect.Constructor;
+
+/**
+ * Factory methods for file systems. This class defines the {@link #getDefault
+ * getDefault} method to get the default file system and factory methods to
+ * construct other types of file systems.
+ *
+ * <p> The first invocation of any of the methods defined by this class causes
+ * the default {@link FileSystemProvider provider} to be loaded. The default
+ * provider, identified by the URI scheme "file", creates the {@link FileSystem}
+ * that provides access to the file systems accessible to the Java virtual
+ * machine. If the process of loading or initializing the default provider fails
+ * then an unspecified error is thrown.
+ *
+ * <p> The first invocation of the {@link FileSystemProvider#installedProviders
+ * installedProviders} method, by way of invoking any of the {@code
+ * newFileSystem} methods defined by this class, locates and loads all
+ * installed file system providers. Installed providers are loaded using the
+ * service-provider loading facility defined by the {@link ServiceLoader} class.
+ * Installed providers are loaded using the system class loader. If the
+ * system class loader cannot be found then the extension class loader is used;
+ * if there is no extension class loader then the bootstrap class loader is used.
+ * Providers are typically installed by placing them in a JAR file on the
+ * application class path or in the extension directory, the JAR file contains a
+ * provider-configuration file named {@code java.nio.file.spi.FileSystemProvider}
+ * in the resource directory {@code META-INF/services}, and the file lists one or
+ * more fully-qualified names of concrete subclass of {@link FileSystemProvider}
+ * that have a zero argument constructor.
+ * The ordering that installed providers are located is implementation specific.
+ * If a provider is instantiated and its {@link FileSystemProvider#getScheme()
+ * getScheme} returns the same URI scheme of a provider that was previously
+ * instantiated then the most recently instantiated duplicate is discarded. URI
+ * schemes are compared without regard to case. During construction a provider
+ * may safely access files associated with the default provider but care needs
+ * to be taken to avoid circular loading of other installed providers. If
+ * circular loading of installed providers is detected then an unspecified error
+ * is thrown.
+ *
+ * <p> This class also defines factory methods that allow a {@link ClassLoader}
+ * to be specified when locating a provider. As with installed providers, the
+ * provider classes are identified by placing the provider configuration file
+ * in the resource directory {@code META-INF/services}.
+ *
+ * <p> If a thread initiates the loading of the installed file system providers
+ * and another thread invokes a method that also attempts to load the providers
+ * then the method will block until the loading completes.
+ *
+ * @since 1.7
+ */
+
+public final class FileSystems {
+ private FileSystems() {
+ }
+
+ // lazy initialization of default file system
+ private static class DefaultFileSystemHolder {
+ static final FileSystem defaultFileSystem = defaultFileSystem();
+
+ // returns default file system
+ private static FileSystem defaultFileSystem() {
+ // load default provider
+ FileSystemProvider provider = AccessController
+ .doPrivileged(new PrivilegedAction<FileSystemProvider>() {
+ public FileSystemProvider run() {
+ return getDefaultProvider();
+ }
+ });
+
+ // return file system
+ return provider.getFileSystem(URI.create("file:///"));
+ }
+
+ // returns default provider
+ private static FileSystemProvider getDefaultProvider() {
+ FileSystemProvider provider = sun.nio.fs.DefaultFileSystemProvider.create();
+
+ // if the property java.nio.file.spi.DefaultFileSystemProvider is
+ // set then its value is the name of the default provider (or a list)
+ String propValue = System
+ .getProperty("java.nio.file.spi.DefaultFileSystemProvider");
+ if (propValue != null) {
+ for (String cn: propValue.split(",")) {
+ try {
+ Class<?> c = Class
+ .forName(cn, true, ClassLoader.getSystemClassLoader());
+ Constructor<?> ctor = c
+ .getDeclaredConstructor(FileSystemProvider.class);
+ provider = (FileSystemProvider)ctor.newInstance(provider);
+
+ // must be "file"
+ if (!provider.getScheme().equals("file"))
+ throw new Error("Default provider must use scheme 'file'");
+
+ } catch (Exception x) {
+ throw new Error(x);
+ }
+ }
+ }
+ return provider;
+ }
+ }
+
+ /**
+ * Returns the default {@code FileSystem}. The default file system creates
+ * objects that provide access to the file systems accessible to the Java
+ * virtual machine. The <em>working directory</em> of the file system is
+ * the current user directory, named by the system property {@code user.dir}.
+ * This allows for interoperability with the {@link java.io.File java.io.File}
+ * class.
+ *
+ * <p> The first invocation of any of the methods defined by this class
+ * locates the default {@link FileSystemProvider provider} object. Where the
+ * system property {@code java.nio.file.spi.DefaultFileSystemProvider} is
+ * not defined then the default provider is a system-default provider that
+ * is invoked to create the default file system.
+ *
+ * <p> If the system property {@code java.nio.file.spi.DefaultFileSystemProvider}
+ * is defined then it is taken to be a list of one or more fully-qualified
+ * names of concrete provider classes identified by the URI scheme
+ * {@code "file"}. Where the property is a list of more than one name then
+ * the names are separated by a comma. Each class is loaded, using the system
+ * class loader, and instantiated by invoking a one argument constructor
+ * whose formal parameter type is {@code FileSystemProvider}. The providers
+ * are loaded and instantiated in the order they are listed in the property.
+ * If this process fails or a provider's scheme is not equal to {@code "file"}
+ * then an unspecified error is thrown. URI schemes are normally compared
+ * without regard to case but for the default provider, the scheme is
+ * required to be {@code "file"}. The first provider class is instantiated
+ * by invoking it with a reference to the system-default provider.
+ * The second provider class is instantiated by invoking it with a reference
+ * to the first provider instance. The third provider class is instantiated
+ * by invoking it with a reference to the second instance, and so on. The
+ * last provider to be instantiated becomes the default provider; its {@code
+ * getFileSystem} method is invoked with the URI {@code "file:///"} to create
+ * the default file system.
+ *
+ * <p> Subsequent invocations of this method return the file system that was
+ * returned by the first invocation.
+ *
+ * @return the default file system
+ */
+ public static FileSystem getDefault() {
+ return DefaultFileSystemHolder.defaultFileSystem;
+ }
+
+ /**
+ * Returns a reference to an existing {@code FileSystem}.
+ *
+ * <p> This method iterates over the {@link FileSystemProvider#installedProviders()
+ * installed} providers to locate the provider that is identified by the URI
+ * {@link URI#getScheme scheme} of the given URI. URI schemes are compared
+ * without regard to case. The exact form of the URI is highly provider
+ * dependent. If found, the provider's {@link FileSystemProvider#getFileSystem
+ * getFileSystem} method is invoked to obtain a reference to the {@code
+ * FileSystem}.
+ *
+ * <p> Once a file system created by this provider is {@link FileSystem#close
+ * closed} it is provider-dependent if this method returns a reference to
+ * the closed file system or throws {@link FileSystemNotFoundException}.
+ * If the provider allows a new file system to be created with the same URI
+ * as a file system it previously created then this method throws the
+ * exception if invoked after the file system is closed (and before a new
+ * instance is created by the {@link #newFileSystem newFileSystem} method).
+ *
+ * <p> If a security manager is installed then a provider implementation
+ * may require to check a permission before returning a reference to an
+ * existing file system. In the case of the {@link FileSystems#getDefault
+ * default} file system, no permission check is required.
+ *
+ * @throws IllegalArgumentException
+ * if the pre-conditions for the {@code uri} parameter are not met
+ * @throws FileSystemNotFoundException
+ * if the file system, identified by the URI, does not exist
+ * @throws ProviderNotFoundException
+ * if a provider supporting the URI scheme is not installed
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission
+ */
+ public static FileSystem getFileSystem(URI uri) {
+ String scheme = uri.getScheme();
+ for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
+ if (scheme.equalsIgnoreCase(provider.getScheme())) {
+ return provider.getFileSystem(uri);
+ }
+ }
+ throw new ProviderNotFoundException("Provider \"" + scheme + "\" not found");
+ }
+
+ /**
+ * Constructs a new file system that is identified by a {@link URI}
+ *
+ * <p> This method iterates over the {@link FileSystemProvider#installedProviders()
+ * installed} providers to locate the provider that is identified by the URI
+ * {@link URI#getScheme scheme} of the given URI. URI schemes are compared
+ * without regard to case. The exact form of the URI is highly provider
+ * dependent. If found, the provider's {@link FileSystemProvider#newFileSystem(URI,Map)
+ * newFileSystem(URI,Map)} method is invoked to construct the new file system.
+ *
+ * <p> Once a file system is {@link FileSystem#close closed} it is
+ * provider-dependent if the provider allows a new file system to be created
+ * with the same URI as a file system it previously created.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose there is a provider identified by the scheme {@code "memory"}
+ * installed:
+ * <pre>
+ * Map&lt;String,String&gt; env = new HashMap&lt;String,String&gt;();
+ * env.put("capacity", "16G");
+ * env.put("blockSize", "4k");
+ * FileSystem fs = FileSystems.newFileSystem(URI.create("memory:///?name=logfs"), env);
+ * </pre>
+ *
+ * @param uri
+ * the URI identifying the file system
+ * @param env
+ * a map of provider specific properties to configure the file system;
+ * may be empty
+ *
+ * @return a new file system
+ *
+ * @throws IllegalArgumentException
+ * if the pre-conditions for the {@code uri} parameter are not met,
+ * or the {@code env} parameter does not contain properties required
+ * by the provider, or a property value is invalid
+ * @throws FileSystemAlreadyExistsException
+ * if the file system has already been created
+ * @throws ProviderNotFoundException
+ * if a provider supporting the URI scheme is not installed
+ * @throws IOException
+ * if an I/O error occurs creating the file system
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission required by the file system provider implementation
+ */
+ public static FileSystem newFileSystem(URI uri, Map<String,?> env)
+ throws IOException
+ {
+ return newFileSystem(uri, env, null);
+ }
+
+ /**
+ * Constructs a new file system that is identified by a {@link URI}
+ *
+ * <p> This method first attempts to locate an installed provider in exactly
+ * the same manner as the {@link #newFileSystem(URI,Map) newFileSystem(URI,Map)}
+ * method. If none of the installed providers support the URI scheme then an
+ * attempt is made to locate the provider using the given class loader. If a
+ * provider supporting the URI scheme is located then its {@link
+ * FileSystemProvider#newFileSystem(URI,Map) newFileSystem(URI,Map)} is
+ * invoked to construct the new file system.
+ *
+ * @param uri
+ * the URI identifying the file system
+ * @param env
+ * a map of provider specific properties to configure the file system;
+ * may be empty
+ * @param loader
+ * the class loader to locate the provider or {@code null} to only
+ * attempt to locate an installed provider
+ *
+ * @return a new file system
+ *
+ * @throws IllegalArgumentException
+ * if the pre-conditions for the {@code uri} parameter are not met,
+ * or the {@code env} parameter does not contain properties required
+ * by the provider, or a property value is invalid
+ * @throws FileSystemAlreadyExistsException
+ * if the URI scheme identifies an installed provider and the file
+ * system has already been created
+ * @throws ProviderNotFoundException
+ * if a provider supporting the URI scheme is not found
+ * @throws ServiceConfigurationError
+ * when an error occurs while loading a service provider
+ * @throws IOException
+ * an I/O error occurs creating the file system
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission required by the file system provider implementation
+ */
+ public static FileSystem newFileSystem(URI uri, Map<String,?> env, ClassLoader loader)
+ throws IOException
+ {
+ String scheme = uri.getScheme();
+
+ // check installed providers
+ for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
+ if (scheme.equalsIgnoreCase(provider.getScheme())) {
+ return provider.newFileSystem(uri, env);
+ }
+ }
+
+ // if not found, use service-provider loading facility
+ if (loader != null) {
+ ServiceLoader<FileSystemProvider> sl = ServiceLoader
+ .load(FileSystemProvider.class, loader);
+ for (FileSystemProvider provider: sl) {
+ if (scheme.equalsIgnoreCase(provider.getScheme())) {
+ return provider.newFileSystem(uri, env);
+ }
+ }
+ }
+
+ throw new ProviderNotFoundException("Provider \"" + scheme + "\" not found");
+ }
+
+ /**
+ * Constructs a new {@code FileSystem} to access the contents of a file as a
+ * file system.
+ *
+ * <p> This method makes use of specialized providers that create pseudo file
+ * systems where the contents of one or more files is treated as a file
+ * system. The {@code file} parameter is a reference to an existing file
+ * and the {@code env} parameter is a map of provider specific properties to
+ * configure the file system.
+ *
+ * <p> This method iterates over the {@link FileSystemProvider#installedProviders()
+ * installed} providers. It invokes, in turn, each provider's {@link
+ * FileSystemProvider#newFileSystem(FileRef,Map) newFileSystem(FileRef,Map)} method.
+ * If a provider returns a file system then the iteration terminates
+ * and the file system is returned. If none of the installed providers return
+ * a {@code FileSystem} then an attempt is made to locate the provider using
+ * the given class loader. If a provider returns a file system then the lookup
+ * terminates and the file system is returned.
+ *
+ * @param file
+ * a reference to a file
+ * @param env
+ * a map of provider specific properties to configure the file system;
+ * may be empty
+ * @param loader
+ * the class loader to locate the provider or {@code null} to only
+ * attempt to locate an installed provider
+ *
+ * @return a new file system
+ *
+ * @throws IllegalArgumentException
+ * if the {@code env} parameter does not contain properties required
+ * by the provider, or a property value is invalid
+ * @throws ProviderNotFoundException
+ * if a provider supporting this file type cannot be located
+ * @throws ServiceConfigurationError
+ * when an error occurs while loading a service provider
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission
+ */
+ public static FileSystem newFileSystem(FileRef file,
+ Map<String,?> env,
+ ClassLoader loader)
+ throws IOException
+ {
+ if (file == null)
+ throw new NullPointerException();
+
+ // check installed providers
+ for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
+ try {
+ return provider.newFileSystem(file, env);
+ } catch (UnsupportedOperationException uoe) {
+ }
+ }
+
+ // if not found, use service-provider loading facility
+ if (loader != null) {
+ ServiceLoader<FileSystemProvider> sl = ServiceLoader
+ .load(FileSystemProvider.class, loader);
+ for (FileSystemProvider provider: sl) {
+ try {
+ return provider.newFileSystem(file, env);
+ } catch (UnsupportedOperationException uoe) {
+ }
+ }
+ }
+
+ throw new ProviderNotFoundException("Provider not found");
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileTreeWalker.java b/src/share/classes/java/nio/file/FileTreeWalker.java
new file mode 100644
index 000000000..95148a5b5
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileTreeWalker.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import sun.nio.fs.BasicFileAttributesHolder;
+
+/**
+ * Simple file tree walker that works in a similar manner to nftw(3C).
+ *
+ * @see Files#walkFileTree
+ */
+
+class FileTreeWalker {
+ private final boolean followLinks;
+ private final boolean detectCycles;
+ private final LinkOption[] linkOptions;
+ private final FileVisitor<? super Path> visitor;
+
+ FileTreeWalker(Set<FileVisitOption> options, FileVisitor<? super Path> visitor) {
+ boolean fl = false;
+ boolean dc = false;
+ for (FileVisitOption option: options) {
+ switch (option) {
+ case FOLLOW_LINKS : fl = true; break;
+ case DETECT_CYCLES : dc = true; break;
+ default:
+ if (option == null)
+ throw new NullPointerException("Visit options contains 'null'");
+ throw new AssertionError("Should not get here");
+ }
+ }
+ this.followLinks = fl;
+ this.detectCycles = fl | dc;
+ this.linkOptions = (fl) ? new LinkOption[0] :
+ new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+ this.visitor = visitor;
+ }
+
+ /**
+ * Walk file tree starting at the given file
+ */
+ void walk(Path start, int maxDepth) {
+ // don't use attributes of starting file as they may be stale
+ if (start instanceof BasicFileAttributesHolder) {
+ ((BasicFileAttributesHolder)start).invalidate();
+ }
+ FileVisitResult result = walk(start,
+ maxDepth,
+ new ArrayList<AncestorDirectory>());
+ if (result == null) {
+ throw new NullPointerException("Visitor returned 'null'");
+ }
+ }
+
+ /**
+ * @param file
+ * the directory to visit
+ * @param depth
+ * depth remaining
+ * @param ancestors
+ * use when cycle detection is enabled
+ */
+ private FileVisitResult walk(Path file,
+ int depth,
+ List<AncestorDirectory> ancestors)
+ {
+ // depth check
+ if (depth-- < 0)
+ return FileVisitResult.CONTINUE;
+
+ // if attributes are cached then use them if possible
+ BasicFileAttributes attrs = null;
+ if (file instanceof BasicFileAttributesHolder) {
+ BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
+ if (!followLinks || !cached.isSymbolicLink())
+ attrs = cached;
+ }
+ IOException exc = null;
+
+ // attempt to get attributes of file. If fails and we are following
+ // links then a link target might not exist so get attributes of link
+ if (attrs == null) {
+ try {
+ try {
+ attrs = Attributes.readBasicFileAttributes(file, linkOptions);
+ } catch (IOException x1) {
+ if (followLinks) {
+ try {
+ attrs = Attributes
+ .readBasicFileAttributes(file, LinkOption.NOFOLLOW_LINKS);
+ } catch (IOException x2) {
+ exc = x2;
+ }
+ } else {
+ exc = x1;
+ }
+ }
+ } catch (SecurityException x) {
+ return FileVisitResult.CONTINUE;
+ }
+ }
+
+ // unable to get attributes of file
+ if (exc != null) {
+ return visitor.visitFileFailed(file, exc);
+ }
+
+ // file is not a directory so invoke visitFile method
+ if (!attrs.isDirectory()) {
+ return visitor.visitFile(file, attrs);
+ }
+
+ // check for cycles
+ if (detectCycles) {
+ Object key = attrs.fileKey();
+
+ // if this directory and ancestor has a file key then we compare
+ // them; otherwise we use less efficient isSameFile test.
+ for (AncestorDirectory ancestor: ancestors) {
+ Object ancestorKey = ancestor.fileKey();
+ if (key != null && ancestorKey != null) {
+ if (key.equals(ancestorKey)) {
+ // cycle detected
+ return visitor.visitFile(file, attrs);
+ }
+ } else {
+ try {
+ if (file.isSameFile(ancestor.file())) {
+ // cycle detected
+ return visitor.visitFile(file, attrs);
+ }
+ } catch (IOException x) {
+ // ignore
+ } catch (SecurityException x) {
+ // ignore
+ }
+ }
+ }
+
+ ancestors.add(new AncestorDirectory(file, key));
+ }
+
+ // visit directory
+ try {
+ DirectoryStream<Path> stream = null;
+ FileVisitResult result;
+
+ // open the directory
+ try {
+ stream = file.newDirectoryStream();
+ } catch (IOException x) {
+ return visitor.preVisitDirectoryFailed(file, x);
+ } catch (SecurityException x) {
+ // ignore, as per spec
+ return FileVisitResult.CONTINUE;
+ }
+
+ // the exception notified to the postVisitDirectory method
+ IOException ioe = null;
+
+ // invoke preVisitDirectory and then visit each entry
+ try {
+ result = visitor.preVisitDirectory(file);
+ if (result != FileVisitResult.CONTINUE) {
+ return result;
+ }
+
+ // if an I/O occurs during iteration then a CME is thrown. We
+ // need to distinguish this from a CME thrown by the visitor.
+ boolean inAction = false;
+
+ try {
+ for (Path entry: stream) {
+ inAction = true;
+ result = walk(entry, depth, ancestors);
+ inAction = false;
+
+ // returning null will cause NPE to be thrown
+ if (result == null || result == FileVisitResult.TERMINATE)
+ return result;
+
+ // skip remaining siblings in this directory
+ if (result == FileVisitResult.SKIP_SIBLINGS)
+ break;
+ }
+ } catch (ConcurrentModificationException x) {
+ // if CME thrown because the iteration failed then remember
+ // the IOException so that it is notified to postVisitDirectory
+ if (!inAction) {
+ // iteration failed
+ Throwable t = x.getCause();
+ if (t instanceof IOException)
+ ioe = (IOException)t;
+ }
+ if (ioe == null)
+ throw x;
+ }
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException x) { }
+ }
+
+ // invoke postVisitDirectory last
+ return visitor.postVisitDirectory(file, ioe);
+
+ } finally {
+ // remove key from trail if doing cycle detection
+ if (detectCycles) {
+ ancestors.remove(ancestors.size()-1);
+ }
+ }
+ }
+
+ private static class AncestorDirectory {
+ private final FileRef dir;
+ private final Object key;
+ AncestorDirectory(FileRef dir, Object key) {
+ this.dir = dir;
+ this.key = key;
+ }
+ FileRef file() {
+ return dir;
+ }
+ Object fileKey() {
+ return key;
+ }
+ }
+}
diff --git a/src/share/classes/java/nio/file/FileVisitOption.java b/src/share/classes/java/nio/file/FileVisitOption.java
new file mode 100644
index 000000000..c02e3e2d1
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileVisitOption.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines the file tree traversal options.
+ *
+ * @since 1.7
+ *
+ * @see Files#walkFileTree
+ */
+
+public enum FileVisitOption {
+ /**
+ * Follow symbolic links.
+ */
+ FOLLOW_LINKS,
+ /**
+ * Detect cycles in the file tree.
+ */
+ DETECT_CYCLES;
+}
diff --git a/src/share/classes/java/nio/file/FileVisitResult.java b/src/share/classes/java/nio/file/FileVisitResult.java
new file mode 100644
index 000000000..28e628bc8
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileVisitResult.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * The result type of a {@link FileVisitor FileVisitor}.
+ *
+ * @since 1.7
+ *
+ * @see Files#walkFileTree
+ */
+
+public enum FileVisitResult {
+ /**
+ * Continue. When returned from a {@link FileVisitor#preVisitDirectory
+ * preVisitDirectory} method then the entries in the directory should also
+ * be visited.
+ */
+ CONTINUE,
+ /**
+ * Terminate.
+ */
+ TERMINATE,
+ /**
+ * Continue without visiting the entries in this directory. This result
+ * is only meaningful when returned from the {@link
+ * FileVisitor#preVisitDirectory preVisitDirectory} method; otherwise
+ * this result type is the same as returning {@link #CONTINUE}.
+ */
+ SKIP_SUBTREE,
+ /**
+ * Continue without visiting the <em>siblings</em> of this file or directory.
+ * If returned from the {@link FileVisitor#preVisitDirectory
+ * preVisitDirectory} method then the entries in the directory are also
+ * skipped and the {@link FileVisitor#postVisitDirectory postVisitDirectory}
+ * method is not invoked.
+ */
+ SKIP_SIBLINGS;
+}
diff --git a/src/share/classes/java/nio/file/FileVisitor.java b/src/share/classes/java/nio/file/FileVisitor.java
new file mode 100644
index 000000000..6d65eba27
--- /dev/null
+++ b/src/share/classes/java/nio/file/FileVisitor.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.BasicFileAttributes;
+import java.io.IOException;
+
+/**
+ * A visitor of files. An implementation of this interface is provided to the
+ * {@link Files#walkFileTree walkFileTree} utility method to visit each file
+ * in a tree.
+ *
+ * <p> <b>Usage Examples:</b>
+ * Suppose we want to delete a file tree. In that case, each directory should
+ * be deleted after the entries in the directory are deleted.
+ * <pre>
+ * Path start = ...
+ * Files.walkFileTree(start, new SimpleFileVisitor&lt;Path&gt;() {
+ * &#64;Override
+ * public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ * try {
+ * file.delete(false);
+ * } catch (IOException exc) {
+ * // failed to delete
+ * }
+ * return FileVisitResult.CONTINUE;
+ * }
+ * &#64;Override
+ * public FileVisitResult postVisitDirectory(Path dir, IOException e) {
+ * if (e == null) {
+ * try {
+ * dir.delete(false);
+ * } catch (IOException exc) {
+ * // failed to delete
+ * }
+ * } else {
+ * // directory iteration failed
+ * }
+ * return FileVisitResult.CONTINUE;
+ * }
+ * });
+ * </pre>
+ * <p> Furthermore, suppose we want to copy a file tree rooted at a source
+ * directory to a target location. In that case, symbolic links should be
+ * followed and the target directory should be created before the entries in
+ * the directory are copied.
+ * <pre>
+ * final Path source = ...
+ * final Path target = ...
+ *
+ * Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
+ * new SimpleFileVisitor&lt;Path&gt;() {
+ * &#64;Override
+ * public FileVisitResult preVisitDirectory(Path dir) {
+ * try {
+ * dir.copyTo(target.resolve(source.relativize(dir)));
+ * } catch (FileAlreadyExistsException e) {
+ * // ignore
+ * } catch (IOException e) {
+ * // copy failed, skip rest of directory and descendants
+ * return SKIP_SUBTREE;
+ * }
+ * return CONTINUE;
+ * }
+ * &#64;Override
+ * public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ * try {
+ * file.copyTo(target.resolve(source.relativize(file)));
+ * } catch (IOException e) {
+ * // copy failed
+ * }
+ * return CONTINUE;
+ * }
+ * });
+ * </pre>
+ *
+ * @since 1.7
+ */
+
+public interface FileVisitor<T extends FileRef> {
+
+ /**
+ * Invoked for a directory before entries in the directory are visited.
+ *
+ * <p> If this method returns {@link FileVisitResult#CONTINUE CONTINUE},
+ * then entries in the directory are visited. If this method returns {@link
+ * FileVisitResult#SKIP_SUBTREE SKIP_SUBTREE} or {@link
+ * FileVisitResult#SKIP_SIBLINGS SKIP_SIBLINGS} then entries in the
+ * directory (and any descendants) will not be visited.
+ *
+ * @param dir
+ * a reference to the directory
+ *
+ * @return the visit result
+ */
+ FileVisitResult preVisitDirectory(T dir);
+
+ /**
+ * Invoked for a directory that could not be opened.
+ *
+ * @param dir
+ * a reference to the directory
+ * @param exc
+ * the I/O exception thrown from the attempt to open the directory
+ *
+ * @return the visit result
+ */
+ FileVisitResult preVisitDirectoryFailed(T dir, IOException exc);
+
+ /**
+ * Invoked for a file in a directory.
+ *
+ * @param file
+ * a reference to the file
+ * @param attrs
+ * the file's basic attributes
+ *
+ * @return the visit result
+ */
+ FileVisitResult visitFile(T file, BasicFileAttributes attrs);
+
+ /**
+ * Invoked for a file when its basic file attributes could not be read.
+ *
+ * @param file
+ * a reference to the file
+ * @param exc
+ * the I/O exception thrown from the attempt to read the file
+ * attributes
+ *
+ * @return the visit result
+ */
+ FileVisitResult visitFileFailed(T file, IOException exc);
+
+ /**
+ * Invoked for a directory after entries in the directory, and all of their
+ * descendants, have been visited. This method is also invoked when iteration
+ * of the directory completes prematurely (by a {@link #visitFile visitFile}
+ * method returning {@link FileVisitResult#SKIP_SIBLINGS SKIP_SIBLINGS},
+ * or an I/O error when iterating over the directory).
+ *
+ * @param dir
+ * a reference to the directory
+ * @param exc
+ * {@code null} if the iteration of the directory completes without
+ * an error; otherwise the I/O exception that caused the iteration
+ * of the directory to complete prematurely
+ *
+ * @return the visit result
+ */
+ FileVisitResult postVisitDirectory(T dir, IOException exc);
+}
diff --git a/src/share/classes/java/nio/file/Files.java b/src/share/classes/java/nio/file/Files.java
new file mode 100644
index 000000000..42fdad823
--- /dev/null
+++ b/src/share/classes/java/nio/file/Files.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.spi.FileTypeDetector;
+import java.io.IOException;
+import java.util.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Utility methods for files and directories.
+ *
+ * @since 1.7
+ */
+
+public final class Files {
+ private Files() { }
+
+ // lazy loading of default and installed file type detectors
+ private static class DefaultFileTypeDetectorHolder {
+ static final FileTypeDetector defaultFileTypeDetector =
+ sun.nio.fs.DefaultFileTypeDetector.create();
+ static final List<FileTypeDetector> installeDetectors =
+ loadInstalledDetectors();
+
+ // loads all installed file type detectors
+ private static List<FileTypeDetector> loadInstalledDetectors() {
+ return AccessController
+ .doPrivileged(new PrivilegedAction<List<FileTypeDetector>>() {
+ @Override public List<FileTypeDetector> run() {
+ List<FileTypeDetector> list = new ArrayList<FileTypeDetector>();
+ ServiceLoader<FileTypeDetector> loader = ServiceLoader
+ .load(FileTypeDetector.class, ClassLoader.getSystemClassLoader());
+ for (FileTypeDetector detector: loader) {
+ list.add(detector);
+ }
+ return list;
+ }});
+ }
+ }
+
+ /**
+ * Probes the content type of a file.
+ *
+ * <p> This method uses the installed {@link FileTypeDetector} implementations
+ * to probe the given file to determine its content type. Each file type
+ * detector's {@link FileTypeDetector#probeContentType probeContentType} is
+ * invoked, in turn, to probe the file type. If the file is recognized then
+ * the content type is returned. If the file is not recognized by any of the
+ * installed file type detectors then a system-default file type detector is
+ * invoked to guess the content type.
+ *
+ * <p> A given invocation of the Java virtual machine maintains a system-wide
+ * list of file type detectors. Installed file type detectors are loaded
+ * using the service-provider loading facility defined by the {@link ServiceLoader}
+ * class. Installed file type detectors are loaded using the system class
+ * loader. If the system class loader cannot be found then the extension class
+ * loader is used; If the extension class loader cannot be found then the
+ * bootstrap class loader is used. File type detectors are typically installed
+ * by placing them in a JAR file on the application class path or in the
+ * extension directory, the JAR file contains a provider-configuration file
+ * named {@code java.nio.file.spi.FileTypeDetector} in the resource directory
+ * {@code META-INF/services}, and the file lists one or more fully-qualified
+ * names of concrete subclass of {@code FileTypeDetector } that have a zero
+ * argument constructor. If the process of locating or instantiating the
+ * installed file type detectors fails then an unspecified error is thrown.
+ * The ordering that installed providers are located is implementation
+ * specific.
+ *
+ * <p> The return value of this method is the string form of the value of a
+ * Multipurpose Internet Mail Extension (MIME) content type as
+ * defined by <a href="http://www.ietf.org/rfc/rfc2045.txt"><i>RFC&nbsp;2045:
+ * Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet
+ * Message Bodies</i></a>. The string is guaranteed to be parsable according
+ * to the grammar in the RFC.
+ *
+ * @param file
+ * The file reference
+ *
+ * @return The content type of the file, or {@code null} if the content
+ * type cannot be determined
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an unspecified
+ * permission required by a file type detector implementation.
+ *
+ * @see DirectoryStreamFilters#newContentTypeFilter
+ */
+ public static String probeContentType(FileRef file)
+ throws IOException
+ {
+ // try installed file type detectors
+ for (FileTypeDetector detector: DefaultFileTypeDetectorHolder.installeDetectors) {
+ String result = detector.probeContentType(file);
+ if (result != null)
+ return result;
+ }
+
+ // fallback to default
+ return DefaultFileTypeDetectorHolder.defaultFileTypeDetector
+ .probeContentType(file);
+ }
+
+ /**
+ * Invokes a {@link FileAction} for each entry in a directory accepted
+ * by a given {@link java.nio.file.DirectoryStream.Filter filter}.
+ *
+ * <p> This method opens the given directory and invokes the file action's
+ * {@link FileAction#invoke invoke} method for each entry accepted by the
+ * filter. When iteration is completed then the directory is closed. If the
+ * {@link DirectoryStream#close close} method throws an {@code IOException}
+ * then it is silently ignored.
+ *
+ * <p> If the {@code FileAction}'s {@code invoke} method terminates due
+ * to an uncaught {@link IOException}, {@code Error} or {@code RuntimeException}
+ * then the exception is propagated by this method after closing the
+ * directory.
+ *
+ * @param dir
+ * The directory
+ * @param filter
+ * The filter
+ * @param action
+ * The {@code FileAction} to invoke for each accepted entry
+ *
+ * @throws NotDirectoryException
+ * If the {@code dir} parameter is not a directory <i>(optional
+ * specific exception)</i>
+ * @throws IOException
+ * If an I/O error occurs or the {@code invoke} method terminates
+ * due to an uncaught {@code IOException}
+ * @throws SecurityException
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to the directory.
+ */
+ public static void withDirectory(Path dir,
+ DirectoryStream.Filter<? super Path> filter,
+ FileAction<? super Path> action)
+ throws IOException
+ {
+ // explicit null check required in case directory is empty
+ if (action == null)
+ throw new NullPointerException();
+
+ DirectoryStream<Path> stream = dir.newDirectoryStream(filter);
+ try {
+ // set to true when invoking the action so as to distinguish a
+ // CME thrown by the iteration from a CME thrown by the invoke
+ boolean inAction = false;
+ try {
+ for (Path entry: stream) {
+ inAction = true;
+ action.invoke(entry);
+ inAction = false;
+ }
+ } catch (ConcurrentModificationException cme) {
+ if (!inAction) {
+ Throwable cause = cme.getCause();
+ if (cause instanceof IOException)
+ throw (IOException)cause;
+ }
+ throw cme;
+ }
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException x) { }
+ }
+ }
+
+ /**
+ * Invokes a {@link FileAction} for each entry in a directory with a
+ * file name that matches a given pattern.
+ *
+ * <p> This method opens the given directory and invokes the file action's
+ * {@link FileAction#invoke invoke} method for each entry that matches the
+ * given pattern. When iteration is completed then the directory is closed.
+ * If the {@link DirectoryStream#close close} method throws an {@code
+ * IOException} then it is silently ignored.
+ *
+ * <p> If the {@code FileAction}'s {@code invoke} method terminates due
+ * to an uncaught {@link IOException}, {@code Error} or {@code RuntimeException}
+ * then the exception is propagated by this method after closing the
+ * directory.
+ *
+ * <p> The globbing pattern language supported by this method is as
+ * specified by the {@link FileSystem#getPathMatcher getPathMatcher} method.
+ *
+ * @param dir
+ * The directory
+ * @param glob
+ * The globbing pattern
+ * @param action
+ * The {@code FileAction} to invoke for each entry
+ *
+ * @throws NotDirectoryException
+ * If the {@code dir} parameter is not a directory <i>(optional
+ * specific exception)</i>
+ * @throws IOException
+ * If an I/O error occurs or the {@code invoke} method terminates
+ * due to an uncaught {@code IOException}
+ * @throws SecurityException
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to the directory.
+ */
+ public static void withDirectory(Path dir,
+ String glob,
+ FileAction<? super Path> action)
+ throws IOException
+ {
+ if (glob == null)
+ throw new NullPointerException("'glob' is null");
+ final PathMatcher matcher = dir.getFileSystem().getPathMatcher("glob:" + glob);
+ DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
+ @Override
+ public boolean accept(Path entry) {
+ return matcher.matches(entry.getName());
+ }
+ };
+ withDirectory(dir, filter, action);
+ }
+
+ /**
+ * Invokes a {@link FileAction} for all entries in a directory.
+ *
+ * <p> This method works as if invoking it were equivalent to evaluating the
+ * expression:
+ * <blockquote><pre>
+ * withDirectory(dir, "*", action)
+ * </pre></blockquote>
+ *
+ * @param dir
+ * The directory
+ * @param action
+ * The {@code FileAction} to invoke for each entry
+ *
+ * @throws NotDirectoryException
+ * If the {@code dir} parameter is not a directory <i>(optional
+ * specific exception)</i>
+ * @throws IOException
+ * If an I/O error occurs or the {@code invoke} method terminates
+ * due to an uncaught {@code IOException}
+ * @throws SecurityException
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to the directory.
+ */
+ public static void withDirectory(Path dir, FileAction<? super Path> action)
+ throws IOException
+ {
+ withDirectory(dir, "*", action);
+ }
+
+ /**
+ * Walks a file tree.
+ *
+ * <p> This method walks a file tree rooted at a given starting file. The
+ * file tree traversal is <em>depth-first</em> with the given {@link
+ * FileVisitor} invoked for each file encountered. File tree traversal
+ * completes when all accessible files in the tree have been visited, a
+ * visitor returns a result of {@link FileVisitResult#TERMINATE TERMINATE},
+ * or the visitor terminates due to an uncaught {@code Error} or {@code
+ * RuntimeException}.
+ *
+ * <p> For each file encountered this method attempts to gets its {@link
+ * java.nio.file.attribute.BasicFileAttributes}. If the file is not a
+ * directory then the {@link FileVisitor#visitFile visitFile} method is
+ * invoked with the file attributes. If the file attributes cannot be read,
+ * due to an I/O exception, then the {@link FileVisitor#visitFileFailed
+ * visitFileFailed} method is invoked with the I/O exception.
+ *
+ * <p> Where the file is a directory, this method attempts to open it by
+ * invoking its {@link Path#newDirectoryStream newDirectoryStream} method.
+ * Where the directory could not be opened, due to an {@code IOException},
+ * then the {@link FileVisitor#preVisitDirectoryFailed preVisitDirectoryFailed}
+ * method is invoked with the I/O exception, after which, the file tree walk
+ * continues, by default, at the next <em>sibling</em> of the directory.
+ *
+ * <p> Where the directory is opened successfully, then the entries in the
+ * directory, and their <em>descendants</em> are visited. When all entries
+ * have been visited, or an I/O error occurs during iteration of the
+ * directory, then the directory is closed and the visitor's {@link
+ * FileVisitor#postVisitDirectory postVisitDirectory} method is invoked.
+ * The file tree walk then continues, by default, at the next <em>sibling</em>
+ * of the directory.
+ *
+ * <p> By default, symbolic links are not automatically followed by this
+ * method. If the {@code options} parameter contains the {@link
+ * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then symbolic links are
+ * followed. When following links, and the attributes of the target cannot
+ * be read, then this method attempts to get the {@code BasicFileAttributes}
+ * of the link. If they can be read then the {@code visitFile} method is
+ * invoked with the attributes of the link (otherwise the {@code visitFileFailed}
+ * method is invoked as specified above).
+ *
+ * <p> If the {@code options} parameter contains the {@link
+ * FileVisitOption#DETECT_CYCLES DETECT_CYCLES} or {@link
+ * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} options then this method keeps
+ * track of directories visited so that cycles can be detected. A cycle
+ * arises when there is an entry in a directory that is an ancestor of the
+ * directory. Cycle detection is done by recording the {@link
+ * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories,
+ * or if file keys are not available, by invoking the {@link FileRef#isSameFile
+ * isSameFile} method to test if a directory is the same file as an
+ * ancestor. When a cycle is detected the {@link FileVisitor#visitFile
+ * visitFile} is invoked with the attributes of the directory. The {@link
+ * java.nio.file.attribute.BasicFileAttributes#isDirectory isDirectory}
+ * method may be used to test if the file is a directory and that a cycle is
+ * detected. The {@code preVisitDirectory} and {@code postVisitDirectory}
+ * methods are not invoked.
+ *
+ * <p> The {@code maxDepth} parameter is the maximum number of levels of
+ * directories to visit. A value of {@code 0} means that only the starting
+ * file is visited, unless denied by the security manager. A value of
+ * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all
+ * levels should be visited.
+ *
+ * <p> If a visitor returns a result of {@code null} then {@code
+ * NullPointerException} is thrown.
+ *
+ * <p> When a security manager is installed and it denies access to a file
+ * (or directory), then it is ignored and the visitor is not invoked for
+ * that file (or directory).
+ *
+ * @param start
+ * The starting file
+ * @param options
+ * Options to configure the traversal
+ * @param maxDepth
+ * The maximum number of directory levels to visit
+ * @param visitor
+ * The file visitor to invoke for each file
+ *
+ * @throws IllegalArgumentException
+ * If the {@code maxDepth} parameter is negative
+ * @throws SecurityException
+ * If the security manager denies access to the starting file.
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to the directory.
+ */
+ public static void walkFileTree(Path start,
+ Set<FileVisitOption> options,
+ int maxDepth,
+ FileVisitor<? super Path> visitor)
+ {
+ if (maxDepth < 0)
+ throw new IllegalArgumentException("'maxDepth' is negative");
+ new FileTreeWalker(options, visitor).walk(start, maxDepth);
+ }
+
+ /**
+ * Walks a file tree.
+ *
+ * <p> This method works as if invoking it were equivalent to evaluating the
+ * expression:
+ * <blockquote><pre>
+ * walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, visitor)
+ * </pre></blockquote>
+ *
+ * @param start
+ * The starting file
+ * @param visitor
+ * The file visitor to invoke for each file
+ *
+ * @throws SecurityException
+ * If the security manager denies access to the starting file.
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to the directory.
+ */
+ public static void walkFileTree(Path start, FileVisitor<? super Path> visitor) {
+ walkFileTree(start,
+ EnumSet.noneOf(FileVisitOption.class),
+ Integer.MAX_VALUE,
+ visitor);
+ }
+}
diff --git a/src/share/classes/java/nio/file/InvalidPathException.java b/src/share/classes/java/nio/file/InvalidPathException.java
new file mode 100644
index 000000000..37f23202c
--- /dev/null
+++ b/src/share/classes/java/nio/file/InvalidPathException.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Unchecked exception thrown when path string cannot be converted into a
+ * {@link Path} because the path string contains invalid characters, or
+ * the path string is invalid for other file system specific reasons.
+ */
+
+public class InvalidPathException
+ extends IllegalArgumentException
+{
+ static final long serialVersionUID = 4355821422286746137L;
+
+ private String input;
+ private int index;
+
+ /**
+ * Constructs an instance from the given input string, reason, and error
+ * index.
+ *
+ * @param input the input string
+ * @param reason a string explaining why the input was rejected
+ * @param index the index at which the error occurred,
+ * or <tt>-1</tt> if the index is not known
+ *
+ * @throws NullPointerException
+ * if either the input or reason strings are <tt>null</tt>
+ *
+ * @throws IllegalArgumentException
+ * if the error index is less than <tt>-1</tt>
+ */
+ public InvalidPathException(String input, String reason, int index) {
+ super(reason);
+ if ((input == null) || (reason == null))
+ throw new NullPointerException();
+ if (index < -1)
+ throw new IllegalArgumentException();
+ this.input = input;
+ this.index = index;
+ }
+
+ /**
+ * Constructs an instance from the given input string and reason. The
+ * resulting object will have an error index of <tt>-1</tt>.
+ *
+ * @param input the input string
+ * @param reason a string explaining why the input was rejected
+ *
+ * @throws NullPointerException
+ * if either the input or reason strings are <tt>null</tt>
+ */
+ public InvalidPathException(String input, String reason) {
+ this(input, reason, -1);
+ }
+
+ /**
+ * Returns the input string.
+ *
+ * @return the input string
+ */
+ public String getInput() {
+ return input;
+ }
+
+ /**
+ * Returns a string explaining why the input string was rejected.
+ *
+ * @return the reason string
+ */
+ public String getReason() {
+ return super.getMessage();
+ }
+
+ /**
+ * Returns an index into the input string of the position at which the
+ * error occurred, or <tt>-1</tt> if this position is not known.
+ *
+ * @return the error index
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Returns a string describing the error. The resulting string
+ * consists of the reason string followed by a colon character
+ * (<tt>':'</tt>), a space, and the input string. If the error index is
+ * defined then the string <tt>" at index "</tt> followed by the index, in
+ * decimal, is inserted after the reason string and before the colon
+ * character.
+ *
+ * @return a string describing the error
+ */
+ public String getMessage() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getReason());
+ if (index > -1) {
+ sb.append(" at index ");
+ sb.append(index);
+ }
+ sb.append(": ");
+ sb.append(input);
+ return sb.toString();
+ }
+}
diff --git a/src/share/classes/java/nio/file/LinkOption.java b/src/share/classes/java/nio/file/LinkOption.java
new file mode 100644
index 000000000..08e5c1e1d
--- /dev/null
+++ b/src/share/classes/java/nio/file/LinkOption.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines the options as to how symbolic links are handled.
+ *
+ * @since 1.7
+ */
+
+public enum LinkOption implements OpenOption, CopyOption {
+ /**
+ * Do not follow symbolic links.
+ *
+ * @see FileRef#getFileAttributeView(Class,LinkOption[])
+ * @see Path#copyTo
+ * @see SecureDirectoryStream#newByteChannel
+ */
+ NOFOLLOW_LINKS;
+}
diff --git a/src/share/classes/java/nio/file/LinkPermission.java b/src/share/classes/java/nio/file/LinkPermission.java
new file mode 100644
index 000000000..d17788fd0
--- /dev/null
+++ b/src/share/classes/java/nio/file/LinkPermission.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.security.BasicPermission;
+
+/**
+ * The {@code Permission} class for link creation operations.
+ *
+ * <p> The following table provides a summary description of what the permission
+ * allows, and discusses the risks of granting code the permission.
+ *
+ * <table border=1 cellpadding=5
+ * summary="Table shows permission target name, what the permission allows, and associated risks">
+ * <tr>
+ * <th>Permission Target Name</th>
+ * <th>What the Permission Allows</th>
+ * <th>Risks of Allowing this Permission</th>
+ * </tr>
+ * <tr>
+ * <td>hard</td>
+ * <td> Ability to add an existing file to a directory. This is sometimes
+ * known as creating a link, or hard link. </td>
+ * <td> Extreme care should be taken when granting this permission. It allows
+ * linking to any file or directory in the file system thus allowing the
+ * attacker to access to all files. </td>
+ * </tr>
+ * <tr>
+ * <td>symbolic</td>
+ * <td> Ability to create symbolic links. </td>
+ * <td> Extreme care should be taken when granting this permission. It allows
+ * linking to any file or directory in the file system thus allowing the
+ * attacker to access to all files. </td>
+ * </tr>
+ * </table>
+ *
+ * @since 1.7
+ *
+ * @see Path#createLink
+ * @see Path#createSymbolicLink
+ */
+public final class LinkPermission extends BasicPermission {
+ static final long serialVersionUID = -1441492453772213220L;
+
+ private void checkName(String name) {
+ if (!name.equals("hard") && !name.equals("symbolic")) {
+ throw new IllegalArgumentException("name: " + name);
+ }
+ }
+
+ /**
+ * Constructs a {@code LinkPermission} with the specified name.
+ *
+ * @param name
+ * the name of the permission. It must be "hard" or "symbolic".
+ *
+ * @throws IllegalArgumentException
+ * if name is empty or invalid
+ */
+ public LinkPermission(String name) {
+ super(name);
+ checkName(name);
+ }
+
+ /**
+ * Constructs a {@code LinkPermission} with the specified name.
+ *
+ * @param name
+ * the name of the permission; must be "hard" or "symbolic".
+ * @param actions
+ * the actions for the permission; must be the empty string or
+ * {@code null}
+ *
+ * @throws IllegalArgumentException
+ * if name is empty or invalid
+ */
+ public LinkPermission(String name, String actions) {
+ super(name);
+ checkName(name);
+ if (actions != null && actions.length() > 0) {
+ throw new IllegalArgumentException("actions: " + actions);
+ }
+ }
+}
diff --git a/src/share/classes/java/nio/file/NoSuchFileException.java b/src/share/classes/java/nio/file/NoSuchFileException.java
new file mode 100644
index 000000000..ccde9ae95
--- /dev/null
+++ b/src/share/classes/java/nio/file/NoSuchFileException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when an attempt is made to access a file that does
+ * not exist.
+ *
+ * @since 1.7
+ */
+
+public class NoSuchFileException
+ extends FileSystemException
+{
+ static final long serialVersionUID = -1390291775875351931L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known.
+ */
+ public NoSuchFileException(String file) {
+ super(file);
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known.
+ * @param other
+ * a string identifying the other file or {@code null} if not known.
+ * @param reason
+ * a reason message with additional information or {@code null}
+ */
+ public NoSuchFileException(String file, String other, String reason) {
+ super(file, other, reason);
+ }
+}
diff --git a/src/share/classes/java/nio/file/NotDirectoryException.java b/src/share/classes/java/nio/file/NotDirectoryException.java
new file mode 100644
index 000000000..684bd2df4
--- /dev/null
+++ b/src/share/classes/java/nio/file/NotDirectoryException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when a file system operation, intended for a
+ * directory, fails because the file is not a directory.
+ *
+ * @since 1.7
+ */
+
+public class NotDirectoryException
+ extends FileSystemException
+{
+ private static final long serialVersionUID = -9011457427178200199L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ */
+ public NotDirectoryException(String file) {
+ super(file);
+ }
+}
diff --git a/src/share/classes/java/nio/file/NotLinkException.java b/src/share/classes/java/nio/file/NotLinkException.java
new file mode 100644
index 000000000..bdc1fc354
--- /dev/null
+++ b/src/share/classes/java/nio/file/NotLinkException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Checked exception thrown when a file system operation fails because a file
+ * is not a link.
+ *
+ * @since 1.7
+ */
+
+public class NotLinkException
+ extends FileSystemException
+{
+ static final long serialVersionUID = -388655596416518021L;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ */
+ public NotLinkException(String file) {
+ super(file);
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param file
+ * a string identifying the file or {@code null} if not known
+ * @param other
+ * a string identifying the other file or {@code null} if not known
+ * @param reason
+ * a reason message with additional information or {@code null}
+ */
+ public NotLinkException(String file, String other, String reason) {
+ super(file, other, reason);
+ }
+}
diff --git a/src/share/classes/java/nio/file/OpenOption.java b/src/share/classes/java/nio/file/OpenOption.java
new file mode 100644
index 000000000..c525307e7
--- /dev/null
+++ b/src/share/classes/java/nio/file/OpenOption.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * An object that configures how to open or create a file.
+ *
+ * <p> Objects of this type are used by methods such as {@link
+ * Path#newOutputStream(OpenOption[]) newOutputStream}, {@link
+ * FileRef#newByteChannel newByteChannel}, {@link
+ * java.nio.channels.FileChannel#open FileChannel.open}, and {@link
+ * java.nio.channels.AsynchronousFileChannel#open AsynchronousFileChannel.open}
+ * when opening or creating a file.
+ *
+ * <p> The {@link StandardOpenOption} enumeration type defines the
+ * <i>standard</i> options.
+ *
+ * @since 1.7
+ */
+
+public interface OpenOption {
+}
diff --git a/src/share/classes/java/nio/file/Path.java b/src/share/classes/java/nio/file/Path.java
new file mode 100644
index 000000000..55bf8fd26
--- /dev/null
+++ b/src/share/classes/java/nio/file/Path.java
@@ -0,0 +1,1613 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.*;
+import java.nio.channels.*;
+import java.io.*;
+import java.net.URI;
+import java.util.*;
+
+/**
+ * A file reference that locates a file using a system dependent path. The file
+ * is not required to exist.
+ *
+ * <p> On many platforms a <em>path</em> is the means to locate and access files
+ * in a file system. A path is hierarchical and composed of a sequence of
+ * directory and file name elements separated by a special separator or
+ * delimiter.
+ *
+ * <h4>Path operations</h4>
+ *
+ * <p> A system dependent path represented by this class is conceptually a
+ * sequence of name elements and optionally a <em>root component</em>. The name
+ * that is <em>farthest</em> from the root of the directory hierarchy is the
+ * name of a file or directory. The other elements are directory names. The root
+ * component typically identifies a file system hierarchy. A {@code Path} can
+ * represent a root, a root and a sequence of names, or simply one or more name
+ * elements. It defines the {@link #getName() getName}, {@link #getParent
+ * getParent}, {@link #getRoot getRoot}, and {@link #subpath subpath} methods
+ * to access the components or a subsequence of its name elements.
+ *
+ * <p> In addition to accessing the components of a path, a {@code Path} also
+ * defines {@link #resolve(Path) resolve} and {@link #relativize relativize}
+ * operations. Paths can also be {@link #compareTo compared}, and tested
+ * against each other using using the {@link #startsWith startsWith} and {@link
+ * #endsWith endWith} methods.
+ *
+ * <h4>File operations</h4>
+ *
+ * <p> A {@code Path} is either <em>absolute</em> or <em>relative</em>. An
+ * absolute path is complete in that does not need to be combined with another
+ * path in order to locate a file. All operations on relative paths are first
+ * resolved against a file system's default directory as if by invoking the
+ * {@link #toAbsolutePath toAbsolutePath} method.
+ *
+ * <p> In addition to the operations defined by the {@link FileRef} interface,
+ * this class defines the following operations:
+ *
+ * <ul>
+ * <li><p> Files may be {@link #createFile(FileAttribute[]) created}, or
+ * directories may be {@link #createDirectory(FileAttribute[]) created}.
+ * </p></li>
+ * <li><p> Directories can be {@link #newDirectoryStream opened} so as to
+ * iterate over the entries in the directory. </p></li>
+ * <li><p> Files can be {@link #copyTo(Path,CopyOption[]) copied} or
+ * {@link #moveTo(Path,CopyOption[]) moved}. </p></li>
+ * <li><p> Symbolic-links may be {@link #createSymbolicLink created}, or the
+ * target of a link may be {@link #readSymbolicLink read}. </p></li>
+ * <li><p> {@link #newInputStream InputStream} or {@link #newOutputStream
+ * OutputStream} streams can be created to allow for interoperation with the
+ * <a href="../../../java/io/package-summary.html">{@code java.io}</a> package
+ * where required. </li></p>
+ * <li><p> The {@link #toRealPath real} path of an existing file may be
+ * obtained. </li></p>
+ * </ul>
+ *
+ * <p> This class implements {@link Watchable} interface so that a directory
+ * located by a path can be {@link #register registered} with a {@link WatchService}.
+ * and entries in the directory watched.
+ *
+ * <h4>File attributes</h4>
+ *
+ * The <a href="attribute/package-summary.html">{@code java.nio.file.attribute}</a>
+ * package provides access to file attributes or <em>meta-data</em> associated
+ * with files. The {@link Attributes Attributes} class defines methods that
+ * operate on or return file attributes. For example, the file type, size,
+ * timestamps, and other <em>basic</em> meta-data are obtained, in bulk, by
+ * invoking the {@link Attributes#readBasicFileAttributes
+ * Attributes.readBasicFileAttributes} method:
+ * <pre>
+ * Path file = ...
+ * BasicFileAttributes attrs = Attributes.readBasicFileAttributes(file);
+ * </pre>
+ *
+ * <a name="interop"><h4>Interoperability</h4></a>
+ *
+ * <p> Paths created by file systems associated with the default {@link
+ * java.nio.file.spi.FileSystemProvider provider} are generally interoperable
+ * with the {@link java.io.File java.io.File} class. Paths created by other
+ * providers are unlikely to be interoperable with the abstract path names
+ * represented by {@code java.io.File}. The {@link java.io.File#toPath
+ * File.toPath} method may be used to obtain a {@code Path} from the abstract
+ * path name represented by a {@code java.io.File java.io.File} object. The
+ * resulting {@code Path} can be used to operate on the same file as the {@code
+ * java.io.File} object.
+ *
+ * <p> Path objects created by file systems associated with the default
+ * provider are interoperable with objects created by other file systems created
+ * by the same provider. Path objects created by file systems associated with
+ * other providers may not be interoperable with other file systems created by
+ * the same provider. The reasons for this are provider specific.
+ *
+ * <h4>Concurrency</h4></a>
+ *
+ * <p> Instances of this class are immutable and safe for use by multiple concurrent
+ * threads.
+ *
+ * @since 1.7
+ */
+
+public abstract class Path
+ implements FileRef, Comparable<Path>, Iterable<Path>, Watchable
+{
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected Path() { }
+
+ /**
+ * Returns the file system that created this object.
+ *
+ * @return the file system that created this object
+ */
+ public abstract FileSystem getFileSystem();
+
+ /**
+ * Tells whether or not this path is absolute.
+ *
+ * <p> An absolute path is complete in that it doesn't need to be
+ * combined with other path information in order to locate a file.
+ *
+ * @return {@code true} if, and only if, this path is absolute
+ */
+ public abstract boolean isAbsolute();
+
+ /**
+ * Returns the root component of this path as a {@code Path} object,
+ * or {@code null} if this path does not have a root component.
+ *
+ * @return a path representing the root component of this path,
+ * or {@code null}
+ */
+ public abstract Path getRoot();
+
+ /**
+ * Returns the name of the file or directory denoted by this path. The
+ * file name is the <em>farthest</em> element from the root in the directory
+ * hierarchy.
+ *
+ * @return a path representing the name of the file or directory, or
+ * {@code null} if this path has zero elements
+ */
+ public abstract Path getName();
+
+ /**
+ * Returns the <em>parent path</em>, or {@code null} if this path does not
+ * have a parent.
+ *
+ * <p> The parent of this path object consists of this path's root
+ * component, if any, and each element in the path except for the
+ * <em>farthest</em> from the root in the directory hierarchy. This method
+ * does not access the file system; the path or its parent may not exist.
+ * Furthermore, this method does not eliminate special names such as "."
+ * and ".." that may be used in some implementations. On UNIX for example,
+ * the parent of "{@code /a/b/c}" is "{@code /a/b}", and the parent of
+ * {@code "x/y/.}" is "{@code x/y}". This method may be used with the {@link
+ * #normalize normalize} method, to eliminate redundant names, for cases where
+ * <em>shell-like</em> navigation is required.
+ *
+ * <p> If this path has one or more elements, and no root component, then
+ * this method is equivalent to evaluating the expression:
+ * <blockquote><pre>
+ * subpath(0,&nbsp;getNameCount()-1);
+ * </pre></blockquote>
+ *
+ * @return a path representing the path's parent
+ */
+ public abstract Path getParent();
+
+ /**
+ * Returns the number of name elements in the path.
+ *
+ * @return the number of elements in the path, or {@code 0} if this path
+ * only represents a root component
+ */
+ public abstract int getNameCount();
+
+ /**
+ * Returns a name element of this path.
+ *
+ * <p> The {@code index} parameter is the index of the name element to return.
+ * The element that is <em>closest</em> to the root in the directory hierarchy
+ * has index {@code 0}. The element that is <em>farthest</em> from the root
+ * has index {@link #getNameCount count}{@code -1}.
+ *
+ * @param index
+ * the index of the element
+ *
+ * @return the name element
+ *
+ * @throws IllegalArgumentException
+ * if {@code index} is negative, {@code index} is greater than or
+ * equal to the number of elements, or this path has zero name
+ * elements
+ */
+ public abstract Path getName(int index);
+
+ /**
+ * Returns a relative {@code Path} that is a subsequence of the name
+ * elements of this path.
+ *
+ * <p> The {@code beginIndex} and {@code endIndex} parameters specify the
+ * subsequence of name elements. The name that is <em>closest</em> to the root
+ * in the directory hierarchy has index {@code 0}. The name that is
+ * <em>farthest</em> from the root has index {@link #getNameCount
+ * count}{@code -1}. The returned {@code Path} object has the name elements
+ * that begin at {@code beginIndex} and extend to the element at index {@code
+ * endIndex-1}.
+ *
+ * @param beginIndex
+ * the index of the first element, inclusive
+ * @param endIndex
+ * the index of the last element, exclusive
+ *
+ * @return a new {@code Path} object that is a subsequence of the name
+ * elements in this {@code Path}
+ *
+ * @throws IllegalArgumentException
+ * if {@code beginIndex} is negative, or greater than or equal to
+ * the number of elements. If {@code endIndex} is less than or
+ * equal to {@code beginIndex}, or larger than the number of elements.
+ */
+ public abstract Path subpath(int beginIndex, int endIndex);
+
+ /**
+ * Tests if this path starts with the given path.
+ *
+ * <p> This path <em>starts</em> with the given path if this path's root
+ * component <em>starts</em> with the root component of the given path,
+ * and this path starts with the same name elements as the given path.
+ * If the given path has more name elements than this path then {@code false}
+ * is returned.
+ *
+ * <p> Whether or not the root component of this path starts with the root
+ * component of the given path is file system specific. If this path does
+ * not have a root component and the given path has a root component then
+ * this path does not start with the given path.
+ *
+ * @param other
+ * the given path
+ *
+ * @return {@code true} if this path starts with the given path; otherwise
+ * {@code false}
+ */
+ public abstract boolean startsWith(Path other);
+
+ /**
+ * Tests if this path ends with the given path.
+ *
+ * <p> If the given path has <em>N</em> elements, and no root component,
+ * and this path has <em>N</em> or more elements, then this path ends with
+ * the given path if the last <em>N</em> elements of each path, starting at
+ * the element farthest from the root, are equal.
+ *
+ * <p> If the given path has a root component then this path ends with the
+ * given path if the root component of this path <em>ends with</em> the root
+ * component of the given path, and the corresponding elements of both paths
+ * are equal. Whether or not the root component of this path ends with the
+ * root component of the given path is file system specific. If this path
+ * does not have a root component and the given path has a root component
+ * then this path does not end with the given path.
+ *
+ * @param other
+ * the given path
+ *
+ * @return {@code true} if this path ends with the given path; otherwise
+ * {@code false}
+ */
+ public abstract boolean endsWith(Path other);
+
+ /**
+ * Returns a path that is this path with redundant name elements eliminated.
+ *
+ * <p> The precise definition of this method is implementation dependent but
+ * in general it derives from this path, a path that does not contain
+ * <em>redundant</em> name elements. In many file systems, the "{@code .}"
+ * and "{@code ..}" are special names used to indicate the current directory
+ * and parent directory. In such file systems all occurrences of "{@code .}"
+ * are considered redundant. If a "{@code ..}" is preceded by a
+ * non-"{@code ..}" name then both names are considered redundant (the
+ * process to identify such names is repeated until is it no longer
+ * applicable).
+ *
+ * <p> This method does not access the file system; the path may not locate
+ * a file that exists. Eliminating "{@code ..}" and a preceding name from a
+ * path may result in the path that locates a different file than the original
+ * path. This can arise when the preceding name is a symbolic link.
+ *
+ * @return the resulting path, or this path if it does not contain
+ * redundant name elements, or {@code null} if this path does not
+ * have a root component and all name elements are redundant
+ *
+ * @see #getParent
+ * @see #toRealPath
+ */
+ public abstract Path normalize();
+
+ // -- resolution and relativization --
+
+ /**
+ * Resolve the given path against this path.
+ *
+ * <p> If the {@code other} parameter is an {@link #isAbsolute() absolute}
+ * path then this method trivially returns {@code other}. If {@code other}
+ * is {@code null} then this path is returned. Otherwise this method
+ * considers this path to be a directory and resolves the given path
+ * against this path. In the simplest case, the given path does not have
+ * a {@link #getRoot root} component, in which case this method <em>joins</em>
+ * the given path to this path and returns a resulting path that {@link
+ * #endsWith ends} with the given path. Where the given path has a root
+ * component then resolution is highly implementation dependent and therefore
+ * unspecified.
+ *
+ * @param other
+ * the path to resolve against this path; can be {@code null}
+ *
+ * @return the resulting path
+ *
+ * @see #relativize
+ */
+ public abstract Path resolve(Path other);
+
+ /**
+ * Converts a given path string to a {@code Path} and resolves it against
+ * this {@code Path} in exactly the manner specified by the {@link
+ * #resolve(Path) resolve} method.
+ *
+ * @param other
+ * the path string to resolve against this path
+ *
+ * @return the resulting path
+ *
+ * @throws InvalidPathException
+ * If the path string cannot be converted to a Path.
+ *
+ * @see FileSystem#getPath
+ */
+ public abstract Path resolve(String other);
+
+ /**
+ * Constructs a relative path between this path and a given path.
+ *
+ * <p> Relativization is the inverse of {@link #resolve(Path) resolution}.
+ * This method attempts to construct a {@link #isAbsolute relative} path
+ * that when {@link #resolve(Path) resolved} against this path, yields a
+ * path that locates the same file as the given path. For example, on UNIX,
+ * if this path is {@code "/a/b"} and the given path is {@code "/a/b/c/d"}
+ * then the resulting relative path would be {@code "c/d"}. Where this
+ * path and the given path do not have a {@link #getRoot root} component,
+ * then a relative path can be constructed. A relative path cannot be
+ * constructed if only one of the paths have a root component. Where both
+ * paths have a root component then it is implementation dependent if a
+ * relative path can be constructed. If this path and the given path are
+ * {@link #equals equal} then {@code null} is returned.
+ *
+ * <p> For any two paths <i>p</i> and <i>q</i>, where <i>q</i> does not have
+ * a root component,
+ * <blockquote>
+ * <i>p</i><tt>.relativize(</tt><i>p</i><tt>.resolve(</tt><i>q</i><tt>)).equals(</tt><i>q</i><tt>)</tt>
+ * </blockquote>
+ *
+ * <p> When symbolic-links are supported, then whether the resulting path,
+ * when resolved against this path, yields a path that can be used to locate
+ * the {@link #isSameFile same} file as {@code other} is implementation
+ * dependent. For example, if this path is {@code "/a/b"} and the given
+ * path is {@code "/a/x"} then the resulting relative path may be {@code
+ * "../x"}. If {@code "b"} is a symbolic-link then is implementation
+ * dependent if {@code "a/b/../x"} would locate the same file as {@code "/a/x"}.
+ *
+ * @param other
+ * the resulting path
+ *
+ * @return the resulting relative path, or {@code null} if both paths are
+ * equal
+ *
+ * @throws IllegalArgumentException
+ * if {@code other} is not a {@code Path} that can be relativized
+ * against this path
+ */
+ public abstract Path relativize(Path other);
+
+ // -- file operations --
+
+ /**
+ * Deletes the file located by this path.
+ *
+ * <p> The {@code failIfNotExists} parameter determines how the method
+ * behaves when the file does not exist. When {@code true}, and the file
+ * does not exist, then the method fails. When {@code false} then the method
+ * does not fail.
+ *
+ * <p> As with the {@link FileRef#delete delete()} method, an implementation
+ * may require to examine the file to determine if the file is a directory.
+ * Consequently this method may not be atomic with respect to other file
+ * system operations. If the file is a symbolic-link then the link is
+ * deleted and not the final target of the link.
+ *
+ * <p> If the file is a directory then the directory must be empty. In some
+ * implementations a directory has entries for special files or links that
+ * are created when the directory is created. In such implementations a
+ * directory is considered empty when only the special entries exist.
+ *
+ * <p> On some operating systems it may not be possible to remove a file when
+ * it is open and in use by this Java virtual machine or other programs.
+ *
+ * @param failIfNotExists
+ * {@code true} if the method should fail when the file does not
+ * exist
+ *
+ * @throws NoSuchFileException
+ * if the value of the {@code failIfNotExists} is {@code true} and
+ * the file does not exist <i>(optional specific exception)</i>
+ * @throws DirectoryNotEmptyException
+ * if the file is a directory and could not otherwise be deleted
+ * because the directory is not empty <i>(optional specific
+ * exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkDelete(String)} method
+ * is invoked to check delete access to the file.
+ */
+ public abstract void delete(boolean failIfNotExists) throws IOException;
+
+ /**
+ * Creates a symbolic link to a target <i>(optional operation)</i>.
+ *
+ * <p> The {@code target} parameter is the target of the link. It may be an
+ * {@link Path#isAbsolute absolute} or relative path and may not exist. When
+ * the target is a relative path then file system operations on the resulting
+ * link are relative to the path of the link.
+ *
+ * <p> The {@code attrs} parameter is an optional array of {@link FileAttribute
+ * attributes} to set atomically when creating the link. Each attribute is
+ * identified by its {@link FileAttribute#name name}. If more than one attribute
+ * of the same name is included in the array then all but the last occurrence
+ * is ignored.
+ *
+ * <p> Where symbolic links are supported, but the underlying {@link FileStore}
+ * does not support symbolic links, then this may fail with an {@link
+ * IOException}. Additionally, some operating systems may require that the
+ * Java virtual machine be started with implementation specific privileges to
+ * create symbolic links, in which case this method may throw {@code IOException}.
+ *
+ * @param target
+ * the target of the link
+ * @param attrs
+ * the array of attributes to set atomically when creating the
+ * symbolic link
+ *
+ * @return this path
+ *
+ * @throws UnsupportedOperationException
+ * if the implementation does not support symbolic links or the
+ * array contains an attribute that cannot be set atomically when
+ * creating the symbolic link
+ * @throws FileAlreadyExistsException
+ * if a file with the name already exists <i>(optional specific
+ * exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the the default provider, and a security manager
+ * is installed, it denies {@link LinkPermission}<tt>("symbolic")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the path of the symbolic link.
+ */
+ public abstract Path createSymbolicLink(Path target, FileAttribute<?>... attrs)
+ throws IOException;
+
+ /**
+ * Creates a new link (directory entry) for an existing file <i>(optional
+ * operation)</i>.
+ *
+ * <p> This path locates the directory entry to create. The {@code existing}
+ * parameter is the path to an existing file. This method creates a new
+ * directory entry for the file so that it can be accessed using this path.
+ * On some file systems this is known as creating a "hard link". Whether the
+ * file attributes are maintained for the file or for each directory entry
+ * is file system specific and therefore not specified. Typically, a file
+ * system requires that all links (directory entries) for a file be on the
+ * same file system. Furthermore, on some platforms, the Java virtual machine
+ * may require to be started with implementation specific privileges to
+ * create hard links or to create links to directories.
+ *
+ * @param existing
+ * a reference to an existing file
+ *
+ * @return this path
+ *
+ * @throws UnsupportedOperationException
+ * if the implementation does not support adding an existing file
+ * to a directory
+ * @throws FileAlreadyExistsException
+ * if the entry could not otherwise be created because a file of
+ * that name already exists <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the the default provider, and a security manager
+ * is installed, it denies {@link LinkPermission}<tt>("hard")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to both this path and the path of the
+ * existing file.
+ *
+ * @see BasicFileAttributes#linkCount
+ */
+ public abstract Path createLink(Path existing) throws IOException;
+
+ /**
+ * Reads the target of a symbolic link <i>(optional operation)</i>.
+ *
+ * <p> If the file system supports <a href="package-summary.html#links">symbolic
+ * links</a> then this method is used read the target of the link, failing
+ * if the file is not a link. The target of the link need not exist. The
+ * returned {@code Path} object will be associated with the same file
+ * system as this {@code Path}.
+ *
+ * @return a {@code Path} object representing the target of the link
+ *
+ * @throws UnsupportedOperationException
+ * if the implementation does not support symbolic links
+ * @throws NotLinkException
+ * if the target could otherwise not be read because the file
+ * is not a link <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the the default provider, and a security manager
+ * is installed, it checks that {@code FilePermission} has been
+ * granted with the "{@code readlink}" action to read the link.
+ */
+ public abstract Path readSymbolicLink() throws IOException;
+
+ /**
+ * Returns a URI to represent this path.
+ *
+ * <p> This method constructs a hierarchical {@link URI} that is absolute
+ * with a non-empty path component. Its {@link URI#getScheme() scheme} is
+ * equal to the URI scheme that identifies the provider. The exact form of
+ * the other URI components is highly provider dependent. In particular, it
+ * is implementation dependent if its query, fragment, and authority
+ * components are defined or undefined.
+ *
+ * <p> For the default provider the {@link URI#getPath() path} component
+ * will represent the {@link #toAbsolutePath absolute} path; the query,
+ * fragment components are undefined. Whether the authority component is
+ * defined or not is implementation dependent. There is no guarantee that
+ * the {@code URI} may be used to construct a {@link java.io.File java.io.File}.
+ * In particular, if this path represents a Universal Naming Convention (UNC)
+ * path, then the UNC server name may be encoded in the authority component
+ * of the resulting URI. In the case of the default provider, and the file
+ * exists, and it can be determined that the file is a directory, then the
+ * resulting {@code URI} will end with a slash.
+ *
+ * <p> The default provider provides a similar <em>round-trip</em> guarantee
+ * to the {@link java.io.File} class. For a given {@code Path} <i>p</i> it
+ * is guaranteed that
+ * <blockquote><tt>
+ * {@link Paths#get(URI) Paths.get}(</tt><i>p</i><tt>.toUri()).equals(</tt><i>p</i>
+ * <tt>.{@link #toAbsolutePath() toAbsolutePath}())</tt>
+ * </blockquote>
+ * so long as the original {@code Path}, the {@code URI}, and the new {@code
+ * Path} are all created in (possibly different invocations of) the same
+ * Java virtual machine. Whether other providers make any guarantees is
+ * provider specific and therefore unspecified.
+ *
+ * <p> When a file system is constructed to access the contents of a file
+ * as a file system then it is highly implementation specific if the returned
+ * URI represents the given path in the file system or it represents a
+ * <em>compound</em> URI that encodes the URI of the enclosing file system.
+ * A format for compound URIs is not defined in this release; such a scheme
+ * may be added in a future release.
+ *
+ * @return an absolute, hierarchical URI with a non-empty path component
+ *
+ * @throws IOError
+ * if an I/O error occurs obtaining the absolute path, or where a
+ * file system is constructed to access the contents of a file as
+ * a file system, and the URI of the enclosing file system cannot be
+ * obtained
+ *
+ * @throws SecurityException
+ * In the case of the the default provider, and a security manager
+ * is installed, the {@link #toAbsolutePath toAbsolutePath} method
+ * throws a security exception.
+ */
+ public abstract URI toUri();
+
+ /**
+ * Returns a {@code Path} object representing the absolute path of this
+ * path.
+ *
+ * <p> If this path is already {@link Path#isAbsolute absolute} then this
+ * method simply returns this path. Otherwise, this method resolves the path
+ * in an implementation dependent manner, typically by resolving the path
+ * against a file system default directory. Depending on the implementation,
+ * this method may throw an I/O error if the file system is not accessible.
+ *
+ * @return a {@code Path} object representing the absolute path
+ *
+ * @throws IOError
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the the default provider, and a security manager
+ * is installed, its {@link SecurityManager#checkPropertyAccess(String)
+ * checkPropertyAccess} method is invoked to check access to the
+ * system property {@code user.dir}
+ */
+ public abstract Path toAbsolutePath();
+
+ /**
+ * Returns the <em>real</em> path of an existing file.
+ *
+ * <p> The precise definition of this method is implementation dependent but
+ * in general it derives from this path, an {@link #isAbsolute absolute}
+ * path that locates the {@link #isSameFile same} file as this path, but
+ * with name elements that represent the actual name of the directories
+ * and the file. For example, where filename comparisons on a file system
+ * are case insensitive then the name elements represent the names in their
+ * actual case. Additionally, the resulting path has redundant name
+ * elements removed.
+ *
+ * <p> If this path is relative then its absolute path is first obtained,
+ * as if by invoking the {@link #toAbsolutePath toAbsolutePath} method.
+ *
+ * <p> The {@code resolveLinks} parameter specifies if symbolic links
+ * should be resolved. This parameter is ignored when symbolic links are
+ * not supported. Where supported, and the parameter has the value {@code
+ * true} then symbolic links are resolved to their final target. Where the
+ * parameter has the value {@code false} then this method does not resolve
+ * symbolic links. Some implementations allow special names such as
+ * "{@code ..}" to refer to the parent directory. When deriving the <em>real
+ * path</em>, and a "{@code ..}" (or equivalent) is preceded by a
+ * non-"{@code ..}" name then an implementation will typically causes both
+ * names to be removed. When not resolving symbolic links and the preceding
+ * name is a symbolic link then the names are only removed if it guaranteed
+ * that the resulting path will locate the same file as this path.
+ *
+ * @return an absolute path represent the <em>real</em> path of the file
+ * located by this object
+ *
+ * @throws IOException
+ * if the file does not exist or an I/O error occurs
+ * @throws SecurityException
+ * In the case of the the default provider, and a security manager
+ * is installed, its {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file, and where
+ * this path is not absolute, its {@link SecurityManager#checkPropertyAccess(String)
+ * checkPropertyAccess} method is invoked to check access to the
+ * system property {@code user.dir}
+ */
+ public abstract Path toRealPath(boolean resolveLinks) throws IOException;
+
+ /**
+ * Copy the file located by this path to a target location.
+ *
+ * <p> This method copies the file located by this {@code Path} to the
+ * target location with the {@code options} parameter specifying how the
+ * copy is performed. By default, the copy fails if the target file already
+ * exists, except if the source and target are the {@link #isSameFile same}
+ * file, in which case this method has no effect. File attributes are not
+ * required to be copied to the target file. If symbolic links are supported,
+ * and the file is a link, then the final target of the link is copied. If
+ * the file is a directory then it creates an empty directory in the target
+ * location (entries in the directory are not copied). This method can be
+ * used with the {@link Files#walkFileTree Files.walkFileTree} utility
+ * method to copy a directory and all entries in the directory, or an entire
+ * <i>file-tree</i> where required.
+ *
+ * <p> The {@code options} parameter is an array of options and may contain
+ * any of the following:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Option</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link StandardCopyOption#REPLACE_EXISTING REPLACE_EXISTING} </td>
+ * <td> If the target file exists, then the target file is replaced if it
+ * is not a non-empty directory. If the target file exists and is a
+ * symbolic-link then the symbolic-link is replaced (not the target of
+ * the link. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardCopyOption#COPY_ATTRIBUTES COPY_ATTRIBUTES} </td>
+ * <td> Attempts to copy the file attributes associated with this file to
+ * the target file. The exact file attributes that are copied is platform
+ * and file system dependent and therefore unspecified. Minimally, the
+ * {@link BasicFileAttributes#lastModifiedTime last-modified-time} is
+ * copied to the target file. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link LinkOption#NOFOLLOW_LINKS NOFOLLOW_LINKS} </td>
+ * <td> Symbolic-links are not followed. If the file, located by this path,
+ * is a symbolic-link then the link is copied rather than the target of
+ * the link. It is implementation specific if file attributes can be
+ * copied to the new link. In other words, the {@code COPY_ATTRIBUTES}
+ * option may be ignored when copying a link. </td>
+ * </tr>
+ * </table>
+ *
+ * <p> An implementation of this interface may support additional
+ * implementation specific options.
+ *
+ * <p> Copying a file is not an atomic operation. If an {@link IOException}
+ * is thrown then it possible that the target file is incomplete or some of
+ * its file attributes have not been copied from the source file. When the
+ * {@code REPLACE_EXISTING} option is specified and the target file exists,
+ * then the target file is replaced. The check for the existence of the file
+ * and the creation of the new file may not be atomic with respect to other
+ * file system activities.
+ *
+ * @param target
+ * the target location
+ * @param options
+ * options specifying how the copy should be done
+ *
+ * @return the target
+ *
+ * @throws UnsupportedOperationException
+ * if the array contains a copy option that is not supported
+ * @throws FileAlreadyExistsException
+ * if the target file exists and cannot be replaced because the
+ * {@code REPLACE_EXISTING} option is not specified, or the target
+ * file is a non-empty directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the source file, the
+ * {@link SecurityManager#checkWrite(String) checkWrite} is invoked
+ * to check write access to the target file. If a symbolic link is
+ * copied the security manager is invoked to check {@link
+ * LinkPermission}{@code ("symbolic")}.
+ */
+ public abstract Path copyTo(Path target, CopyOption... options)
+ throws IOException;
+
+ /**
+ * Move or rename the file located by this path to a target location.
+ *
+ * <p> By default, this method attempts to move the file to the target
+ * location, failing if the target file exists except if the source and
+ * target are the {@link #isSameFile same} file, in which case this method
+ * has no effect. If the file is a symbolic link then the link is moved and
+ * not the target of the link. This method may be invoked to move an empty
+ * directory. In some implementations a directory has entries for special
+ * files or links that are created when the directory is created. In such
+ * implementations a directory is considered empty when only the special
+ * entries exist. When invoked to move a directory that is not empty then the
+ * directory is moved if it does not require moving the entries in the directory.
+ * For example, renaming a directory on the same {@link FileStore} will usually
+ * not require moving the entries in the directory. When moving a directory
+ * requires that its entries be moved then this method fails (by throwing
+ * an {@code IOException}). To move a <i>file tree</i> may involve copying
+ * rather than moving directories and this can be done using the {@link
+ * #copyTo copyTo} method in conjunction with the {@link
+ * Files#walkFileTree Files.walkFileTree} utility method.
+ *
+ * <p> The {@code options} parameter is an array of options and may contain
+ * any of the following:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Option</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link StandardCopyOption#REPLACE_EXISTING REPLACE_EXISTING} </td>
+ * <td> If the target file exists, then the target file is replaced if it
+ * is not a non-empty directory. If the target file exists and is a
+ * symbolic-link then the symbolic-link is replaced and not the target of
+ * the link. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardCopyOption#ATOMIC_MOVE ATOMIC_MOVE} </td>
+ * <td> The move is performed as an atomic file system operation and all
+ * other options are ignored. If the target file exists then it is
+ * implementation specific if the existing file is replaced or this method
+ * fails by throwing an {@link IOException}. If the move cannot be
+ * performed as an atomic file system operation then {@link
+ * AtomicMoveNotSupportedException} is thrown. This can arise, for
+ * example, when the target location is on a different {@code FileStore}
+ * and would require that the file be copied, or target location is
+ * associated with a different provider to this object. </td>
+ * </table>
+ *
+ * <p> An implementation of this interface may support additional
+ * implementation specific options.
+ *
+ * <p> Where the move requires that the file be copied then the {@link
+ * BasicFileAttributes#lastModifiedTime last-modified-time} is copied to the
+ * new file. An implementation may also attempt to copy other file
+ * attributes but is not required to fail if the file attributes cannot be
+ * copied. When the move is performed as a non-atomic operation, and a {@code
+ * IOException} is thrown, then the state of the files is not defined. The
+ * original file and the target file may both exist, the target file may be
+ * incomplete or some of its file attributes may not been copied from the
+ * original file.
+ *
+ * @param target
+ * the target location
+ * @param options
+ * options specifying how the move should be done
+ *
+ * @return the target
+ *
+ * @throws UnsupportedOperationException
+ * if the array contains a copy option that is not supported
+ * @throws FileAlreadyExistsException
+ * if the target file exists and cannot be replaced because the
+ * {@code REPLACE_EXISTING} option is not specified, or the target
+ * file is a non-empty directory
+ * @throws AtomicMoveNotSupportedException
+ * if the options array contains the {@code ATOMIC_MOVE} option but
+ * the file cannot be moved as an atomic file system operation.
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to both the source and
+ * target file.
+ */
+ public abstract Path moveTo(Path target, CopyOption... options)
+ throws IOException;
+
+ /**
+ * Opens the directory referenced by this object, returning a {@code
+ * DirectoryStream} to iterate over all entries in the directory. The
+ * elements returned by the directory stream's {@link DirectoryStream#iterator
+ * iterator} are of type {@code Path}, each one representing an entry in the
+ * directory. The {@code Path} objects are obtained as if by {@link
+ * #resolve(Path) resolving} the name of the directory entry against this
+ * path.
+ *
+ * <p> The directory stream's {@code close} method should be invoked after
+ * iteration is completed so as to free any resources held for the open
+ * directory. The {@link Files#withDirectory Files.withDirectory} utility
+ * method is useful for cases where a task is performed on each accepted
+ * entry in a directory. This method closes the directory when iteration is
+ * complete (or an error occurs).
+ *
+ * <p> When an implementation supports operations on entries in the
+ * directory that execute in a race-free manner then the returned directory
+ * stream is a {@link SecureDirectoryStream}.
+ *
+ * @return a new and open {@code DirectoryStream} object
+ *
+ * @throws NotDirectoryException
+ * if the file could not otherwise be opened because it is not
+ * a directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the directory.
+ */
+ public abstract DirectoryStream<Path> newDirectoryStream()
+ throws IOException;
+
+ /**
+ * Opens the directory referenced by this object, returning a {@code
+ * DirectoryStream} to iterate over the entries in the directory. The
+ * elements returned by the directory stream's {@link DirectoryStream#iterator
+ * iterator} are of type {@code Path}, each one representing an entry in the
+ * directory. The {@code Path} objects are obtained as if by {@link
+ * #resolve(Path) resolving} the name of the directory entry against this
+ * path. The entries returned by the iterator are filtered by matching the
+ * {@code String} representation of their file names against the given
+ * <em>globbing</em> pattern.
+ *
+ * <p> For example, suppose we want to iterate over the files ending with
+ * ".java" in a directory:
+ * <pre>
+ * Path dir = ...
+ * DirectoryStream&lt;Path&gt; stream = dir.newDirectoryStream("*.java");
+ * </pre>
+ *
+ * <p> The globbing pattern is specified by the {@link
+ * FileSystem#getPathMatcher getPathMatcher} method.
+ *
+ * <p> The directory stream's {@code close} method should be invoked after
+ * iteration is completed so as to free any resources held for the open
+ * directory.
+ *
+ * <p> When an implementation supports operations on entries in the
+ * directory that execute in a race-free manner then the returned directory
+ * stream is a {@link SecureDirectoryStream}.
+ *
+ * @param glob
+ * the glob pattern
+ *
+ * @return a new and open {@code DirectoryStream} object
+ *
+ * @throws java.util.regex.PatternSyntaxException
+ * if the pattern is invalid
+ * @throws UnsupportedOperationException
+ * if the pattern syntax is not known to the implementation
+ * @throws NotDirectoryException
+ * if the file could not otherwise be opened because it is not
+ * a directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the directory.
+ */
+ public abstract DirectoryStream<Path> newDirectoryStream(String glob)
+ throws IOException;
+
+ /**
+ * Opens the directory referenced by this object, returning a {@code
+ * DirectoryStream} to iterate over the entries in the directory. The
+ * elements returned by the directory stream's {@link DirectoryStream#iterator
+ * iterator} are of type {@code Path}, each one representing an entry in the
+ * directory. The {@code Path} objects are obtained as if by {@link
+ * #resolve(Path) resolving} the name of the directory entry against this
+ * path. The entries returned by the iterator are filtered by the given
+ * {@link DirectoryStream.Filter filter}. The {@link DirectoryStreamFilters}
+ * class defines factory methods that create useful filters.
+ *
+ * <p> The directory stream's {@code close} method should be invoked after
+ * iteration is completed so as to free any resources held for the open
+ * directory. The {@link Files#withDirectory Files.withDirectory} utility
+ * method is useful for cases where a task is performed on each accepted
+ * entry in a directory. This method closes the directory when iteration is
+ * complete (or an error occurs).
+ *
+ * <p> Where the filter terminates due to an uncaught error or runtime
+ * exception then it propogated to the caller of the iterator's {@link
+ * Iterator#hasNext() hasNext} or {@link Iterator#next() next} methods.
+ *
+ * <p> When an implementation supports operations on entries in the
+ * directory that execute in a race-free manner then the returned directory
+ * stream is a {@link SecureDirectoryStream}.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we want to iterate over the files in a directory that are
+ * larger than 8K.
+ * <pre>
+ * DirectoryStream.Filter&lt;Path&gt; filter = new DirectoryStream.Filter&lt;Path&gt;() {
+ * public boolean accept(Path file) {
+ * try {
+ * long size = Attributes.readBasicFileAttributes(file).size();
+ * return (size > 8192L);
+ * } catch (IOException e) {
+ * // failed to get size
+ * return false;
+ * }
+ * }
+ * };
+ * Path dir = ...
+ * DirectoryStream&lt;Path&gt; stream = dir.newDirectoryStream(filter);
+ * </pre>
+ * @param filter
+ * the directory stream filter
+ *
+ * @return a new and open {@code DirectoryStream} object
+ *
+ * @throws NotDirectoryException
+ * if the file could not otherwise be opened because it is not
+ * a directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the directory.
+ */
+ public abstract DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter)
+ throws IOException;
+
+ /**
+ * Creates a new and empty file, failing if the file already exists.
+ *
+ * <p> This {@code Path} locates the file to create. The check for the
+ * existence of the file and the creation of the new file if it does not
+ * exist are a single operation that is atomic with respect to all other
+ * filesystem activities that might affect the directory.
+ *
+ * <p> The {@code attrs} parameter is an optional array of {@link FileAttribute
+ * file-attributes} to set atomically when creating the file. Each attribute
+ * is identified by its {@link FileAttribute#name name}. If more than one
+ * attribute of the same name is included in the array then all but the last
+ * occurrence is ignored.
+ *
+ * @param attrs
+ * an optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return this path
+ *
+ * @throws UnsupportedOperationException
+ * if the array contains an attribute that cannot be set atomically
+ * when creating the file
+ * @throws FileAlreadyExistsException
+ * if a file of that name already exists
+ * <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to the new file.
+ */
+ public abstract Path createFile(FileAttribute<?>... attrs) throws IOException;
+
+ /**
+ * Creates a new directory.
+ *
+ * <p> This {@code Path} locates the directory to create. The check for the
+ * existence of the file and the creation of the directory if it does not
+ * exist are a single operation that is atomic with respect to all other
+ * filesystem activities that might affect the directory.
+ *
+ * <p> The {@code attrs} parameter is an optional array of {@link FileAttribute
+ * file-attributes} to set atomically when creating the directory. Each
+ * file attribute is identified by its {@link FileAttribute#name name}. If
+ * more than one attribute of the same name is included in the array then all
+ * but the last occurrence is ignored.
+ *
+ * @param attrs
+ * an optional list of file attributes to set atomically when
+ * creating the directory
+ *
+ * @return this path
+ *
+ * @throws UnsupportedOperationException
+ * if the array contains an attribute that cannot be set atomically
+ * when creating the directory
+ * @throws FileAlreadyExistsException
+ * if a directory could not otherwise be created because a file of
+ * that name already exists <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to the new directory.
+ */
+ public abstract Path createDirectory(FileAttribute<?>... attrs)
+ throws IOException;
+
+ /**
+ * Opens or creates a file, returning a seekable byte channel to access the
+ * file.
+ *
+ * <p> The {@code options} parameter determines how the file is opened.
+ * The {@link StandardOpenOption#READ READ} and {@link StandardOpenOption#WRITE WRITE}
+ * options determine if the file should be opened for reading and/or writing.
+ * If neither option (or the {@link StandardOpenOption#APPEND APPEND}
+ * option) is contained in the array then the file is opened for reading.
+ * By default reading or writing commences at the beginning of the file.
+ *
+ * <p> In the addition to {@code READ} and {@code WRITE}, the following
+ * options may be present:
+ *
+ * <table border=1 cellpadding=5 summary="">
+ * <tr> <th>Option</th> <th>Description</th> </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#APPEND APPEND} </td>
+ * <td> If this option is present then the file is opened for writing and
+ * each invocation of the channel's {@code write} method first advances
+ * the position to the end of the file and then writes the requested
+ * data. Whether the advancement of the position and the writing of the
+ * data are done in a single atomic operation is system-dependent and
+ * therefore unspecified. This option may not be used in conjunction
+ * with the {@code READ} or {@code TRUNCATE_EXISTING} options. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} </td>
+ * <td> If this option is present then the existing file is truncated to
+ * a size of 0 bytes. This option is ignored when the file is opened only
+ * for reading. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#CREATE_NEW CREATE_NEW} </td>
+ * <td> If this option is present then a new file is created, failing if
+ * the file already exists or is a symbolic link. When creating a file the
+ * check for the existence of the file and the creation of the file if it
+ * does not exist is atomic with respect to other file system operations.
+ * This option is ignored when the file is opened only for reading. </td>
+ * </tr>
+ * <tr>
+ * <td > {@link StandardOpenOption#CREATE CREATE} </td>
+ * <td> If this option is present then an existing file is opened if it
+ * exists, otherwise a new file is created. This option is ignored if the
+ * {@code CREATE_NEW} option is also present or the file is opened only
+ * for reading. </td>
+ * </tr>
+ * <tr>
+ * <td > {@link StandardOpenOption#DELETE_ON_CLOSE DELETE_ON_CLOSE} </td>
+ * <td> When this option is present then the implementation makes a
+ * <em>best effort</em> attempt to delete the file when closed by the
+ * {@link SeekableByteChannel#close close} method. If the {@code close}
+ * method is not invoked then a <em>best effort</em> attempt is made to
+ * delete the file when the Java virtual machine terminates. </td>
+ * </tr>
+ * <tr>
+ * <td>{@link StandardOpenOption#SPARSE SPARSE} </td>
+ * <td> When creating a new file this option is a <em>hint</em> that the
+ * new file will be sparse. This option is ignored when not creating
+ * a new file. </td>
+ * </tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#SYNC SYNC} </td>
+ * <td> Requires that every update to the file's content or metadata be
+ * written synchronously to the underlying storage device. (see <a
+ * href="package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * <tr>
+ * <tr>
+ * <td> {@link StandardOpenOption#DSYNC DSYNC} </td>
+ * <td> Requires that every update to the file's content be written
+ * synchronously to the underlying storage device. (see <a
+ * href="package-summary.html#integrity"> Synchronized I/O file
+ * integrity</a>). </td>
+ * </tr>
+ * </table>
+ *
+ * <p> An implementation may also support additional implementation specific
+ * options.
+ *
+ * <p> The {@code attrs} parameter is an optional array of file {@link
+ * FileAttribute file-attributes} to set atomically when a new file is created.
+ *
+ * <p> In the case of the default provider, the returned seekable byte channel
+ * is a {@link FileChannel}.
+ *
+ * <p> <b>Usage Examples:</b>
+ * <pre>
+ * Path file = ...
+ *
+ * // open file for reading
+ * ReadableByteChannel rbc = file.newByteChannel(EnumSet.of(READ)));
+ *
+ * // open file for writing to the end of an existing file, creating
+ * // the file if it doesn't already exist
+ * WritableByteChannel wbc = file.newByteChannel(EnumSet.of(CREATE,APPEND));
+ *
+ * // create file with initial permissions, opening it for both reading and writing
+ * FileAttribute&lt;Set&lt;PosixFilePermission&gt;&gt; perms = ...
+ * SeekableByteChannel sbc = file.newByteChannel(EnumSet.of(CREATE_NEW,READ,WRITE), perms);
+ * </pre>
+ *
+ * @param options
+ * Options specifying how the file is opened
+ * @param attrs
+ * An optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return a new seekable byte channel
+ *
+ * @throws IllegalArgumentException
+ * if the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * if an unsupported open option is specified or the array contains
+ * attributes that cannot be set atomically when creating the file
+ * @throws FileAlreadyExistsException
+ * if a file of that name already exists and the {@link
+ * StandardOpenOption#CREATE_NEW CREATE_NEW} option is specified
+ * <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the path if the file is
+ * opened for reading. The {@link SecurityManager#checkWrite(String)
+ * checkWrite} method is invoked to check write access to the path
+ * if the file is opened for writing.
+ */
+ public abstract SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException;
+
+ /**
+ * Opens or creates a file, returning a seekable byte channel to access the
+ * file.
+ *
+ * <p> This method extends the options defined by the {@code FileRef}
+ * interface and to the options specified by the {@link
+ * #newByteChannel(Set,FileAttribute[]) newByteChannel} method
+ * except that the options are specified by an array. In the case of the
+ * default provider, the returned seekable byte channel is a {@link
+ * FileChannel}.
+ *
+ * @param options
+ * options specifying how the file is opened
+ *
+ * @return a new seekable byte channel
+ *
+ * @throws IllegalArgumentException
+ * if the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * if an unsupported open option is specified
+ * @throws FileAlreadyExistsException
+ * if a file of that name already exists and the {@link
+ * StandardOpenOption#CREATE_NEW CREATE_NEW} option is specified
+ * <i>(optional specific exception)</i>
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public abstract SeekableByteChannel newByteChannel(OpenOption... options)
+ throws IOException;
+
+ /**
+ * Opens the file located by this path for reading, returning an input
+ * stream to read bytes from the file. The stream will not be buffered, and
+ * is not required to support the {@link InputStream#mark mark} or {@link
+ * InputStream#reset reset} methods. The stream will be safe for access by
+ * multiple concurrent threads. Reading commences at the beginning of the file.
+ *
+ * @return an input stream to read bytes from the file
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file.
+ */
+ public abstract InputStream newInputStream() throws IOException;
+
+ /**
+ * Opens or creates the file located by this path for writing, returning an
+ * output stream to write bytes to the file.
+ *
+ * <p> This method opens or creates a file in exactly the manner specified
+ * by the {@link Path#newByteChannel(Set,FileAttribute[]) newByteChannel}
+ * method except that the {@link StandardOpenOption#READ READ} option may not
+ * be present in the array of open options. If no open options are present
+ * then this method creates a new file for writing or truncates an existing
+ * file.
+ *
+ * <p> The resulting stream will not be buffered. The stream will be safe
+ * for access by multiple concurrent threads.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we wish to open a log file for writing so that we append to the
+ * file if it already exists, or create it when it doesn't exist.
+ * <pre>
+ * Path logfile = ...
+ * OutputStream out = new BufferedOutputStream(logfile.newOutputStream(CREATE, APPEND));
+ * </pre>
+ *
+ * @param options
+ * options specifying how the file is opened
+ *
+ * @return a new seekable byte channel
+ *
+ * @throws IllegalArgumentException
+ * if {@code options} contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * if an unsupported open option is specified
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to the file.
+ */
+ public abstract OutputStream newOutputStream(OpenOption... options)
+ throws IOException;
+
+ /**
+ * Opens or creates the file located by this path for writing, returning an
+ * output stream to write bytes to the file.
+ *
+ * <p> This method opens or creates a file in exactly the manner specified
+ * by the {@link Path#newByteChannel(Set,FileAttribute[]) newByteChannel}
+ * method except that {@code options} parameter may not contain the {@link
+ * StandardOpenOption#READ READ} option. If no open options are present
+ * then this method creates a new file for writing or truncates an existing
+ * file.
+ *
+ * <p> The resulting stream will not be buffered. The stream will be safe
+ * for access by multiple concurrent threads.
+ *
+ * @param options
+ * options specifying how the file is opened
+ * @param attrs
+ * an optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return a new output stream
+ *
+ * @throws IllegalArgumentException
+ * if the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * if an unsupported open option is specified or the array contains
+ * attributes that cannot be set atomically when creating the file
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to the file.
+ */
+ public abstract OutputStream newOutputStream(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException;
+
+ /**
+ * Tells whether or not the file located by this object is considered
+ * <em>hidden</em>. The exact definition of hidden is platform or provider
+ * dependent. On UNIX for example a file is considered to be hidden if its
+ * name begins with a period character ('.'). On Windows a file is
+ * considered hidden if it isn't a directory and the DOS {@link
+ * DosFileAttributes#isHidden hidden} attribute is set.
+ *
+ * <p> Depending on the implementation this method may require to access
+ * the file system to determine if the file is considered hidden.
+ *
+ * @return {@code true} if the file is considered hidden
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file.
+ */
+ public abstract boolean isHidden() throws IOException;
+
+ /**
+ * Tests whether the file located by this path exists.
+ *
+ * <p> This convenience method is intended for cases where it is required to
+ * take action when it can be confirmed that a file exists. This method simply
+ * invokes the {@link #checkAccess checkAccess} method to check if the file
+ * exists. If the {@code checkAccess} method succeeds then this method returns
+ * {@code true}, otherwise if an {@code IOException} is thrown (because the
+ * file doesn't exist or cannot be accessed by this Java virtual machine)
+ * then {@code false} is returned.
+ *
+ * <p> Note that the result of this method is immediately outdated. If this
+ * method indicates the file exists then there is no guarantee that a
+ * subsequence access will succeed. Care should be taken when using this
+ * method in security sensitive applications.
+ *
+ * @return {@code true} if the file exists; {@code false} if the file does
+ * not exist or its existence cannot be determined.
+ *
+ * @throws SecurityException
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} is invoked to check
+ * read access to the file.
+ *
+ * @see #notExists
+ */
+ public abstract boolean exists();
+
+ /**
+ * Tests whether the file located by this path does not exist.
+ *
+ * <p> This convenience method is intended for cases where it is required to
+ * take action when it can be confirmed that a file does not exist. This
+ * method invokes the {@link #checkAccess checkAccess} method to check if the
+ * file exists. If the file does not exist then {@code true} is returned,
+ * otherwise the file exists or cannot be accessed by this Java virtual
+ * machine and {@code false} is returned.
+ *
+ * <p> Note that this method is not the complement of the {@link #exists
+ * exists} method. Where it is not possible to determine if a file exists
+ * or not then both methods return {@code false}. As with the {@code exists}
+ * method, the result of this method is immediately outdated. If this
+ * method indicates the file does exist then there is no guarantee that a
+ * subsequence attempt to create the file will succeed. Care should be taken
+ * when using this method in security sensitive applications.
+ *
+ * @return {@code true} if the file does not exist; {@code false} if the
+ * file exists or its existence cannot be determined.
+ *
+ * @throws SecurityException
+ * In the case of the default provider, the {@link
+ * SecurityManager#checkRead(String)} is invoked to check
+ * read access to the file.
+ */
+ public abstract boolean notExists();
+
+ // -- watchable --
+
+ /**
+ * Registers the file located by this path with a watch service.
+ *
+ * <p> In this release, this path locates a directory that exists. The
+ * directory is registered with the watch service so that entries in the
+ * directory can be watched. The {@code events} parameter is an array of
+ * events to register and may contain the following events:
+ * <ul>
+ * <li>{@link StandardWatchEventKind#ENTRY_CREATE ENTRY_CREATE} -
+ * entry created or moved into the directory</li>
+ * <li>{@link StandardWatchEventKind#ENTRY_DELETE ENTRY_DELETE} -
+ * entry deleted or moved out of the directory</li>
+ * <li>{@link StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} -
+ * entry in directory was modified</li>
+ * </ul>
+ *
+ * <p> The {@link WatchEvent#context context} for these events is the
+ * relative path between the directory located by this path, and the path
+ * that locates the directory entry that is created, deleted, or modified.
+ *
+ * <p> The set of events may include additional implementation specific
+ * event that are not defined by the enum {@link StandardWatchEventKind}
+ *
+ * <p> The {@code modifiers} parameter is an array of <em>modifiers</em>
+ * that qualify how the directory is registered. This release does not
+ * define any <em>standard</em> modifiers. The array may contain
+ * implementation specific modifiers.
+ *
+ * <p> Where a file is registered with a watch service by means of a symbolic
+ * link then it is implementation specific if the watch continues to depend
+ * on the existence of the link after it is registered.
+ *
+ * @param watcher
+ * the watch service to which this object is to be registered
+ * @param events
+ * the events for which this object should be registered
+ * @param modifiers
+ * the modifiers, if any, that modify how the object is registered
+ *
+ * @return a key representing the registration of this object with the
+ * given watch service
+ *
+ * @throws UnsupportedOperationException
+ * if unsupported events or modifiers are specified
+ * @throws IllegalArgumentException
+ * if an invalid combination of events or modifiers is specified
+ * @throws ClosedWatchServiceException
+ * if the watch service is closed
+ * @throws NotDirectoryException
+ * if the file is registered to watch the entries in a directory
+ * and the file is not a directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file.
+ */
+ @Override
+ public abstract WatchKey register(WatchService watcher,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException;
+
+ /**
+ * Registers the file located by this path with a watch service.
+ *
+ * <p> An invocation of this method behaves in exactly the same way as the
+ * invocation
+ * <pre>
+ * watchable.{@link #register(WatchService,WatchEvent.Kind[],WatchEvent.Modifier[]) register}(watcher, events, new WatchEvent.Modifier[0]);
+ * </pre>
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we wish to register a directory for entry create, delete, and modify
+ * events:
+ * <pre>
+ * Path dir = ...
+ * WatchService watcher = ...
+ *
+ * WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+ * </pre>
+ * @param watcher
+ * The watch service to which this object is to be registered
+ * @param events
+ * The events for which this object should be registered
+ *
+ * @return A key representing the registration of this object with the
+ * given watch service
+ *
+ * @throws UnsupportedOperationException
+ * If unsupported events are specified
+ * @throws IllegalArgumentException
+ * If an invalid combination of events is specified
+ * @throws ClosedWatchServiceException
+ * If the watch service is closed
+ * @throws NotDirectoryException
+ * If the file is registered to watch the entries in a directory
+ * and the file is not a directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file.
+ */
+ @Override
+ public abstract WatchKey register(WatchService watcher,
+ WatchEvent.Kind<?>... events)
+ throws IOException;
+
+ // -- Iterable --
+
+ /**
+ * Returns an iterator over the name elements of this path.
+ *
+ * <p> The first element returned by the iterator represents the name
+ * element that is closest to the root in the directory hierarchy, the
+ * second element is the next closest, and so on. The last element returned
+ * is the name of the file or directory denoted by this path. The {@link
+ * #getRoot root} component, if present, is not returned by the iterator.
+ *
+ * @return an iterator over the name elements of this path.
+ */
+ @Override
+ public abstract Iterator<Path> iterator();
+
+ // -- compareTo/equals/hashCode --
+
+ /**
+ * Compares two abstract paths lexicographically. The ordering defined by
+ * this method is provider specific, and in the case of the default
+ * provider, platform specific. This method does not access the file system
+ * and neither file is required to exist.
+ *
+ * @param other the path compared to this path.
+ *
+ * @return zero if the argument is {@link #equals equal} to this path, a
+ * value less than zero if this path is lexicographically less than
+ * the argument, or a value greater than zero if this path is
+ * lexicographically greater than the argument
+ */
+ @Override
+ public abstract int compareTo(Path other);
+
+ /**
+ * Tests this path for equality with the given object.
+ *
+ * <p> If the given object is not a Path, or is a Path associated with a
+ * different provider, then this method immediately returns {@code false}.
+ *
+ * <p> Whether or not two path are equal depends on the file system
+ * implementation. In some cases the paths are compared without regard
+ * to case, and others are case sensitive. This method does not access the
+ * file system and the file is not required to exist.
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * java.lang.Object#equals(Object) Object.equals} method. </p>
+ *
+ * @param other
+ * the object to which this object is to be compared
+ *
+ * @return {@code true} if, and only if, the given object is a {@code Path}
+ * that is identical to this {@code Path}
+ */
+ @Override
+ public abstract boolean equals(Object other);
+
+ /**
+ * Computes a hash code for this path.
+ *
+ * <p> The hash code is based upon the components of the path, and
+ * satisfies the general contract of the {@link Object#hashCode
+ * Object.hashCode} method.
+ *
+ * @return the hash-code value for this path
+ */
+ @Override
+ public abstract int hashCode();
+
+ /**
+ * Returns the string representation of this path.
+ *
+ * <p> If this path was created by converting a path string using the
+ * {@link FileSystem#getPath getPath} method then the path string returned
+ * by this method may differ from the original String used to create the path.
+ *
+ * <p> The returned path string uses the default name {@link
+ * FileSystem#getSeparator separator} to separate names in the path.
+ *
+ * @return the string representation of this path
+ */
+ @Override
+ public abstract String toString();
+}
diff --git a/src/share/classes/java/nio/file/PathMatcher.java b/src/share/classes/java/nio/file/PathMatcher.java
new file mode 100644
index 000000000..5a1cfee88
--- /dev/null
+++ b/src/share/classes/java/nio/file/PathMatcher.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * An interface that is implemented by objects that perform match operations on
+ * paths.
+ *
+ * @since 1.7
+ *
+ * @see FileSystem#getPathMatcher
+ * @see Path#newDirectoryStream(String)
+ */
+
+public interface PathMatcher {
+ /**
+ * Tells if given path matches this matcher's pattern.
+ *
+ * @param path
+ * the path to match
+ *
+ * @return {@code true} if, and only if, the path matches this
+ * matcher's pattern
+ */
+ boolean matches(Path path);
+}
diff --git a/src/share/classes/java/nio/file/Paths.java b/src/share/classes/java/nio/file/Paths.java
new file mode 100644
index 000000000..2cd7a0912
--- /dev/null
+++ b/src/share/classes/java/nio/file/Paths.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.spi.FileSystemProvider;
+import java.net.URI;
+
+/**
+ * This class consists exclusively of static methods that return a {@link Path}
+ * by converting a path string or {@link URI}.
+ *
+ * @since 1.7
+ */
+
+public class Paths {
+ private Paths() { }
+
+ /**
+ * Constructs a {@code Path} by converting the given path string.
+ *
+ * <p> The {@code Path} is obtained by invoking the {@link FileSystem#getPath
+ * getPath} method of the {@link FileSystems#getDefault default} {@link
+ * FileSystem}.
+ *
+ * <p> Note that while this method is very convenient, using it will
+ * imply an assumed reference to the default FileSystem and limit the
+ * utility of the calling code. Hence it should not be used in library code
+ * intended for flexible reuse. A more flexible alternative is to use an
+ * existing {@code Path} instance as an anchor, such as:
+ * <pre>
+ * Path dir = ...
+ * Path path = dir.resolve("file");
+ * </pre>
+ *
+ * @param path
+ * the path string to convert
+ *
+ * @return the resulting {@code Path}
+ *
+ * @throws InvalidPathException
+ * if the path string cannot be converted to a {@code Path}
+ *
+ * @see FileSystem#getPath
+ */
+ public static Path get(String path) {
+ return FileSystems.getDefault().getPath(path);
+ }
+
+ /**
+ * Converts the given URI to a {@link Path} object.
+ *
+ * <p> This method iterates over the {@link FileSystemProvider#installedProviders()
+ * installed} providers to locate the provider that is identified by the
+ * URI {@link URI#getScheme scheme} of the given URI. URI schemes are
+ * compared without regard to case. If the provider is found then its {@link
+ * FileSystemProvider#getPath getPath} method is invoked to convert the
+ * URI.
+ *
+ * <p> In the case of the default provider, identified by the URI scheme
+ * "file", the given URI has a non-empty path component, and undefined query
+ * and fragment components. Whether the authority component may be present
+ * is platform specific. The returned {@code Path} is associated with the
+ * {@link FileSystems#getDefault default} file system.
+ *
+ * <p> The default provider provides a similar <em>round-trip</em> guarantee
+ * to the {@link java.io.File} class. For a given {@code Path} <i>p</i> it
+ * is guaranteed that
+ * <blockquote><tt>
+ * Paths.get(</tt><i>p</i><tt>.{@link Path#toUri() toUri}()).equals(</tt>
+ * <i>p</i><tt>.{@link Path#toAbsolutePath() toAbsolutePath}())</tt>
+ * </blockquote>
+ * so long as the original {@code Path}, the {@code URI}, and the new {@code
+ * Path} are all created in (possibly different invocations of) the same
+ * Java virtual machine. Whether other providers make any guarantees is
+ * provider specific and therefore unspecified.
+ *
+ * @param uri
+ * the URI to convert
+ *
+ * @return the resulting {@code Path}
+ *
+ * @throws IllegalArgumentException
+ * if preconditions on the {@code uri} parameter do not hold. The
+ * format of the URI is provider specific.
+ * @throws FileSystemNotFoundException
+ * if the file system identified by the URI does not exist or the
+ * provider identified by the URI's scheme component is not installed
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission to access the file system
+ */
+ public static Path get(URI uri) {
+ String scheme = uri.getScheme();
+ if (scheme == null)
+ throw new IllegalArgumentException("Missing scheme");
+
+ // check for default provider to avoid loading of installed providers
+ if (scheme.equalsIgnoreCase("file"))
+ return FileSystems.getDefault().provider().getPath(uri);
+
+ // try to find provider
+ for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
+ if (provider.getScheme().equalsIgnoreCase(scheme)) {
+ return provider.getPath(uri);
+ }
+ }
+
+ throw new FileSystemNotFoundException("Provider \"" + scheme + "\" not installed");
+ }
+}
diff --git a/src/share/classes/java/nio/file/ProviderMismatchException.java b/src/share/classes/java/nio/file/ProviderMismatchException.java
new file mode 100644
index 000000000..7c1bbc097
--- /dev/null
+++ b/src/share/classes/java/nio/file/ProviderMismatchException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Unchecked exception thrown when an attempt is made to invoke a method on an
+ * object created by one file system provider with a parameter created by a
+ * different file system provider.
+ */
+public class ProviderMismatchException
+ extends java.lang.IllegalArgumentException
+{
+ static final long serialVersionUID = 4990847485741612530L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public ProviderMismatchException() {
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param msg
+ * the detail message
+ */
+ public ProviderMismatchException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/share/classes/java/nio/file/ProviderNotFoundException.java b/src/share/classes/java/nio/file/ProviderNotFoundException.java
new file mode 100644
index 000000000..dd1fee82d
--- /dev/null
+++ b/src/share/classes/java/nio/file/ProviderNotFoundException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Runtime exception thrown when a provider of the required type cannot be found.
+ */
+
+public class ProviderNotFoundException
+ extends RuntimeException
+{
+ static final long serialVersionUID = -1880012509822920354L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public ProviderNotFoundException() {
+ }
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param msg
+ * the detail message
+ */
+ public ProviderNotFoundException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/share/classes/java/nio/file/ReadOnlyFileSystemException.java b/src/share/classes/java/nio/file/ReadOnlyFileSystemException.java
new file mode 100644
index 000000000..4e92efba0
--- /dev/null
+++ b/src/share/classes/java/nio/file/ReadOnlyFileSystemException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Unchecked exception thrown when an attempt is made to update an object
+ * associated with a {@link FileSystem#isReadOnly() read-only} {@code FileSystem}.
+ */
+
+public class ReadOnlyFileSystemException
+ extends UnsupportedOperationException
+{
+ static final long serialVersionUID = -6822409595617487197L;
+
+ /**
+ * Constructs an instance of this class.
+ */
+ public ReadOnlyFileSystemException() {
+ }
+}
diff --git a/src/share/classes/java/nio/file/SecureDirectoryStream.java b/src/share/classes/java/nio/file/SecureDirectoryStream.java
new file mode 100644
index 000000000..11df095ce
--- /dev/null
+++ b/src/share/classes/java/nio/file/SecureDirectoryStream.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classname" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package java.nio.file;
+
+import java.nio.file.attribute.*;
+import java.nio.channels.SeekableByteChannel;
+import java.util.Set;
+import java.io.IOException;
+
+/**
+ * A {@code DirectoryStream} that defines operations on files that are located
+ * relative to an open directory. A {@code SecureDirectoryStream} is intended
+ * for use by sophisticated or security sensitive applications requiring to
+ * traverse file trees or otherwise operate on directories in a race-free manner.
+ * Race conditions can arise when a sequence of file operations cannot be
+ * carried out in isolation. Each of the file operations defined by this
+ * interface specify a relative {@link Path}. All access to the file is relative
+ * to the open directory irrespective of if the directory is moved or replaced
+ * by an attacker while the directory is open. A {@code SecureDirectoryStream}
+ * may also be used as a virtual <em>working directory</em>.
+ *
+ * <p> A {@code SecureDirectoryStream} requires corresponding support from the
+ * underlying operating system. Where an implementation supports this features
+ * then the {@code DirectoryStream} returned by the {@link Path#newDirectoryStream
+ * newDirectoryStream} method will be a {@code SecureDirectoryStream} and must
+ * be cast to that type in order to invoke the methods defined by this interface.
+ *
+ * <p> As specified by {@code DirectoryStream}, the iterator's {@link
+ * java.util.Iterator#remove() remove} method removes the directory entry for
+ * the last element returned by the iterator. In the case of a {@code
+ * SecureDirectoryStream} the {@code remove} method behaves as if by invoking
+ * the {@link #deleteFile deleteFile} or {@link #deleteDirectory deleteDirectory}
+ * methods defined by this interface. The {@code remove} may require to examine
+ * the file to determine if the file is a directory, and consequently, it may
+ * not be atomic with respect to other file system operations.
+ *
+ * <p> In the case of the default {@link java.nio.file.spi.FileSystemProvider
+ * provider}, and a security manager is set, then the permission checks are
+ * performed using the path obtained by resolving the given relative path
+ * against the <i>original path</i> of the directory (irrespective of if the
+ * directory is moved since it was opened).
+ *
+ * @since 1.7
+ */
+
+public abstract class SecureDirectoryStream
+ implements DirectoryStream<Path>
+{
+ /**
+ * Initialize a new instance of this class.
+ */
+ protected SecureDirectoryStream() { }
+
+ /**
+ * Opens the directory identified by the given path, returning a {@code
+ * SecureDirectoryStream} to iterate over the entries in the directory.
+ *
+ * <p> This method works in exactly the manner specified by the {@link
+ * Path#newDirectoryStream newDirectoryStream} method for the case that
+ * the {@code path} parameter is an {@link Path#isAbsolute absolute} path.
+ * When the parameter is a relative path then the directory to open is
+ * relative to this open directory. The {@code followLinks} parameter
+ * determines if links should be followed. If this parameter is {@code
+ * false} and the file is a symbolic link then this method fails (by
+ * throwing an I/O exception).
+ *
+ * <p> The new directory stream, once created, is not dependent upon the
+ * directory stream used to create it. Closing this directory stream has no
+ * effect upon newly created directory stream.
+ *
+ * @param path
+ * the path to the directory to open
+ * @param followLinks
+ * {@code true} if the links should be followed
+ * @param filter
+ * the directory stream filter or {@code null}.
+ *
+ * @return a new and open {@code SecureDirectoryStream} object
+ *
+ * @throws ClosedDirectoryStreamException
+ * if the directory stream is closed
+ * @throws NotDirectoryException
+ * if the file could not otherwise be opened because it is not
+ * a directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the directory.
+ */
+ public abstract SecureDirectoryStream newDirectoryStream(Path path,
+ boolean followLinks,
+ DirectoryStream.Filter<? super Path> filter)
+ throws IOException;
+
+ /**
+ * Opens or creates a file in this directory, returning a seekable byte
+ * channel to access the file.
+ *
+ * <p> This method works in exactly the manner specified by the {@link
+ * Path#newByteChannel Path.newByteChannel} method for the
+ * case that the {@code path} parameter is an {@link Path#isAbsolute absolute}
+ * path. When the parameter is a relative path then the file to open or
+ * create is relative to this open directory. In addition to the options
+ * defined by the {@code Path.newByteChannel} method, the {@link
+ * LinkOption#NOFOLLOW_LINKS NOFOLLOW_LINKS} option may be used to
+ * ensure that this method fails if the file is a symbolic link.
+ *
+ * <p> The channel, once created, is not dependent upon the directory stream
+ * used to create it. Closing this directory stream has no effect upon the
+ * channel.
+ *
+ * @param path
+ * the path of the file to open open or create
+ * @param options
+ * options specifying how the file is opened
+ * @param attrs
+ * an optional list of attributes to set atomically when creating
+ * the file
+ *
+ * @throws ClosedDirectoryStreamException
+ * if the directory stream is closed
+ * @throws IllegalArgumentException
+ * if the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * if an unsupported open option is specified or the array contains
+ * attributes that cannot be set atomically when creating the file
+ * @throws FileAlreadyExistsException
+ * if a file of that name already exists and the {@link
+ * StandardOpenOption#CREATE_NEW CREATE_NEW} option is specified
+ * <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the path if the file
+ * is opened for reading. The {@link SecurityManager#checkWrite(String)
+ * checkWrite} method is invoked to check write access to the path
+ * if the file is opened for writing.
+ */
+ public abstract SeekableByteChannel newByteChannel(Path path,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException;
+
+ /**
+ * Deletes a file.
+ *
+ * <p> Unlike the {@link FileRef#delete delete()} method, this method
+ * does not first examine the file to determine if the file is a directory.
+ * Whether a directory is deleted by this method is system dependent and
+ * therefore not specified. If the file is a symbolic-link then the link is
+ * deleted (not the final target of the link). When the parameter is a
+ * relative path then the file to delete is relative to this open directory.
+ *
+ * @param path
+ * the path of the file to delete
+ *
+ * @throws ClosedDirectoryStreamException
+ * if the directory stream is closed
+ * @throws NoSuchFileException
+ * if the file does not exist <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkDelete(String) checkDelete}
+ * method is invoked to check delete access to the file
+ */
+ public abstract void deleteFile(Path path) throws IOException;
+
+ /**
+ * Deletes a directory.
+ *
+ * <p> Unlike the {@link FileRef#delete delete()} method, this method
+ * does not first examine the file to determine if the file is a directory.
+ * Whether non-directories are deleted by this method is system dependent and
+ * therefore not specified. When the parameter is a relative path then the
+ * directory to delete is relative to this open directory.
+ *
+ * @param path
+ * the path of the directory to delete
+ *
+ * @throws ClosedDirectoryStreamException
+ * if the directory stream is closed
+ * @throws NoSuchFileException
+ * if the the directory does not exist <i>(optional specific exception)</i>
+ * @throws DirectoryNotEmptyException
+ * if the directory could not otherwise be deleted because it is
+ * not empty <i>(optional specific exception)</i>
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkDelete(String) checkDelete}
+ * method is invoked to check delete access to the directory
+ */
+ public abstract void deleteDirectory(Path path) throws IOException;
+
+ /**
+ * Move a file from this directory to another directory.
+ *
+ * <p> This method works in a similar manner to {@link Path#moveTo moveTo}
+ * method when the {@link StandardCopyOption#ATOMIC_MOVE ATOMIC_MOVE} option
+ * is specified. That is, this method moves a file as an atomic file system
+ * operation. If the {@code srcpath} parameter is an {@link Path#isAbsolute
+ * absolute} path then it locates the source file. If the parameter is a
+ * relative path then it is located relative to this open directory. If
+ * the {@code targetpath} parameter is absolute then it locates the target
+ * file (the {@code targetdir} parameter is ignored). If the parameter is
+ * a relative path it is located relative to the open directory identified
+ * by the {@code targetdir} parameter. In all cases, if the target file
+ * exists then it is implementation specific if it is replaced or this
+ * method fails.
+ *
+ * @param srcpath
+ * the name of the file to move
+ * @param targetdir
+ * the destination directory
+ * @param targetpath
+ * the name to give the file in the destination directory
+ *
+ * @throws ClosedDirectoryStreamException
+ * if this or the target directory stream is closed
+ * @throws FileAlreadyExistsException
+ * if the file already exists in the target directory and cannot
+ * be replaced <i>(optional specific exception)</i>
+ * @throws AtomicMoveNotSupportedException
+ * if the file cannot be moved as an atomic file system operation
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to both the source and
+ * target file.
+ */
+ public abstract void move(Path srcpath, SecureDirectoryStream targetdir, Path targetpath)
+ throws IOException;
+
+ /**
+ * Returns a new file attribute view to access the file attributes of this
+ * directory.
+ *
+ * <p> The resulting file attribute view can be used to read or update the
+ * attributes of this (open) directory. The {@code type} parameter specifies
+ * the type of the attribute view and the method returns an instance of that
+ * type if supported. Invoking this method to obtain a {@link
+ * BasicFileAttributeView} always returns an instance of that class that is
+ * bound to this open directory.
+ *
+ * <p> The state of resulting file attribute view is intimately connected
+ * to this directory stream. Once the directory stream is {@link #close closed},
+ * then all methods to read or update attributes will throw {@link
+ * ClosedDirectoryStreamException ClosedDirectoryStreamException}.
+ *
+ * @param type
+ * the {@code Class} object corresponding to the file attribute view
+ *
+ * @return a new file attribute view of the specified type bound to
+ * this directory stream, or {@code null} if the attribute view
+ * type is not available
+ */
+ public abstract <V extends FileAttributeView> V getFileAttributeView(Class<V> type);
+
+ /**
+ * Returns a new file attribute view to access the file attributes of a file
+ * in this directory.
+ *
+ * <p> The resulting file attribute view can be used to read or update the
+ * attributes of file in this directory. The {@code type} parameter specifies
+ * the type of the attribute view and the method returns an instance of that
+ * type if supported. Invoking this method to obtain a {@link
+ * BasicFileAttributeView} always returns an instance of that class that is
+ * bound to the file in the directory.
+ *
+ * <p> The state of resulting file attribute view is intimately connected
+ * to this directory stream. Once the directory stream {@link #close closed},
+ * then all methods to read or update attributes will throw {@link
+ * ClosedDirectoryStreamException ClosedDirectoryStreamException}. The
+ * file is not required to exist at the time that the file attribute view
+ * is created but methods to read or update attributes of the file will
+ * fail when invoked and the file does not exist.
+ *
+ * @param path
+ * the path of the file
+ * @param type
+ * the {@code Class} object corresponding to the file attribute view
+ * @param options
+ * options indicating how symbolic links are handled
+ *
+ * @return a new file attribute view of the specified type bound to a
+ * this directory stream, or {@code null} if the attribute view
+ * type is not available
+ *
+ */
+ public abstract <V extends FileAttributeView> V getFileAttributeView(Path path,
+ Class<V> type,
+ LinkOption... options);
+}
diff --git a/src/share/classes/java/nio/file/SimpleFileVisitor.java b/src/share/classes/java/nio/file/SimpleFileVisitor.java
new file mode 100644
index 000000000..d9557d024
--- /dev/null
+++ b/src/share/classes/java/nio/file/SimpleFileVisitor.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.nio.file.attribute.BasicFileAttributes;
+import java.io.IOException;
+import java.io.IOError;
+
+/**
+ * A simple visitor of files with default behavior to visit all files and to
+ * re-throw I/O errors.
+ *
+ * <p> Methods in this class may be overridden subject to their general contract.
+ *
+ * @param <T> The type of reference to the files
+ *
+ * @since 1.7
+ */
+
+public class SimpleFileVisitor<T extends FileRef> implements FileVisitor<T> {
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected SimpleFileVisitor() {
+ }
+
+ /**
+ * Invoked for a directory before entries in the directory are visited.
+ *
+ * <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
+ * CONTINUE}.
+ */
+ @Override
+ public FileVisitResult preVisitDirectory(T dir) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ /**
+ * Invoked for a directory that could not be opened.
+ *
+ * <p> Unless overridden, this method throws {@link IOError} with the I/O
+ * exception as cause.
+ *
+ * @throws IOError
+ * with the I/O exception thrown when the attempt to open the
+ * directory failed
+ */
+ @Override
+ public FileVisitResult preVisitDirectoryFailed(T dir, IOException exc) {
+ throw new IOError(exc);
+ }
+
+ /**
+ * Invoked for a file in a directory.
+ *
+ * <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
+ * CONTINUE}.
+ */
+ @Override
+ public FileVisitResult visitFile(T file, BasicFileAttributes attrs) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ /**
+ * Invoked for a file when its basic file attributes could not be read.
+ *
+ * <p> Unless overridden, this method throws {@link IOError} with the I/O
+ * exception as cause.
+ *
+ * @throws IOError
+ * with the I/O exception thrown when the attempt to read the file
+ * attributes failed
+ */
+ @Override
+ public FileVisitResult visitFileFailed(T file, IOException exc) {
+ throw new IOError(exc);
+ }
+
+ /**
+ * Invoked for a directory after entries in the directory, and all of their
+ * descendants, have been visited.
+ *
+ * <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
+ * CONTINUE} if the directory iteration completes without an I/O exception;
+ * otherwise this method throws {@link IOError} with the I/O exception as
+ * cause.
+ *
+ * @throws IOError
+ * if iteration of the directory completed prematurely due to an
+ * I/O error
+ */
+ @Override
+ public FileVisitResult postVisitDirectory(T dir, IOException exc) {
+ if (exc != null)
+ throw new IOError(exc);
+ return FileVisitResult.CONTINUE;
+ }
+}
diff --git a/src/share/classes/java/nio/file/StandardCopyOption.java b/src/share/classes/java/nio/file/StandardCopyOption.java
new file mode 100644
index 000000000..32572c1ef
--- /dev/null
+++ b/src/share/classes/java/nio/file/StandardCopyOption.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines the standard copy options.
+ *
+ * @since 1.7
+ */
+
+public enum StandardCopyOption implements CopyOption {
+ /**
+ * Replace an existing file if it exists.
+ */
+ REPLACE_EXISTING,
+ /**
+ * Copy attributes to the new file.
+ */
+ COPY_ATTRIBUTES,
+ /**
+ * Move the file as an atomic file system operation.
+ */
+ ATOMIC_MOVE;
+}
diff --git a/src/share/classes/java/nio/file/StandardOpenOption.java b/src/share/classes/java/nio/file/StandardOpenOption.java
new file mode 100644
index 000000000..d01763c6a
--- /dev/null
+++ b/src/share/classes/java/nio/file/StandardOpenOption.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines the standard open options.
+ *
+ * @since 1.7
+ */
+
+public enum StandardOpenOption implements OpenOption {
+ /**
+ * Open for read access.
+ */
+ READ,
+
+ /**
+ * Open for write access.
+ */
+ WRITE,
+
+ /**
+ * If the file is opened for {@link #WRITE} access then bytes will be written
+ * to the end of the file rather than the beginning.
+ *
+ * <p> If the file is opened for write access by other programs, then it
+ * is file system specific if writing to the end of the file is atomic.
+ */
+ APPEND,
+
+ /**
+ * If the file already exists and it is opened for {@link #WRITE}
+ * access, then its length is truncated to 0. This option is ignored
+ * if the file is opened only for {@link #READ} access.
+ */
+ TRUNCATE_EXISTING,
+
+ /**
+ * Create a new file if it does not exist.
+ * This option is ignored if the {@link #CREATE_NEW} option is also set.
+ * The check for the existence of the file and the creation of the file
+ * if it does not exist is atomic with respect to other file system
+ * operations.
+ */
+ CREATE,
+
+ /**
+ * Create a new file, failing if the file already exists.
+ * The check for the existence of the file and the creation of the file
+ * if it does not exist is atomic with respect to other file system
+ * operations.
+ */
+ CREATE_NEW,
+
+ /**
+ * Delete on close. When this option is present then the implementation
+ * makes a <em>best effort</em> attempt to delete the file when closed
+ * by the appropriate {@code close} method. If the {@code close} method is
+ * not invoked then a <em>best effort</em> attempt is made to delete the
+ * file when the Java virtual machine terminates (either normally, as
+ * defined by the Java Language Specification, or where possible, abnormally).
+ * This option is primarily intended for use with <em>work files</em> that
+ * are used solely by a single instance of the Java virtual machine. This
+ * option is not recommended for use when opening files that are open
+ * concurrently by other entities. Many of the details as to when and how
+ * the file is deleted are implementation specific and therefore not
+ * specified. In particular, an implementation may be unable to guarantee
+ * that it deletes the expected file when replaced by an attacker while the
+ * file is open. Consequently, security sensitive applications should take
+ * care when using this option.
+ *
+ * <p> For security reasons, this option may imply the {@link
+ * LinkOption#NOFOLLOW_LINKS} option. In other words, if the option is present
+ * when opening an existing file that is a symbolic link then it may fail
+ * (by throwing {@link java.io.IOException}).
+ */
+ DELETE_ON_CLOSE,
+
+ /**
+ * Sparse file. When used with the {@link #CREATE_NEW} option then this
+ * option provides a <em>hint</em> that the new file will be sparse. The
+ * option is ignored when the file system does not support the creation of
+ * sparse files.
+ */
+ SPARSE,
+
+ /**
+ * Requires that every update to the file's content or metadata be written
+ * synchronously to the underlying storage device.
+ *
+ * @see <a href="package-summary.html#integrity">Synchronized I/O file integrity</a>
+ */
+ SYNC,
+
+ /**
+ * Requires that every update to the file's content be written
+ * synchronously to the underlying storage device.
+ *
+ * @see <a href="package-summary.html#integrity">Synchronized I/O file integrity</a>
+ */
+ DSYNC;
+}
diff --git a/src/share/classes/java/nio/file/StandardWatchEventKind.java b/src/share/classes/java/nio/file/StandardWatchEventKind.java
new file mode 100644
index 000000000..6cc937e99
--- /dev/null
+++ b/src/share/classes/java/nio/file/StandardWatchEventKind.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines the <em>standard</em> event kinds.
+ *
+ * @since 1.7
+ */
+
+public class StandardWatchEventKind {
+ private StandardWatchEventKind() { }
+
+ /**
+ * A special event to indicate that events may have been lost or
+ * discarded.
+ *
+ * <p> The {@link WatchEvent#context context} for this event is
+ * implementation specific and may be {@code null}. The event {@link
+ * WatchEvent#count count} may be greater than {@code 1}.
+ *
+ * @see WatchService
+ */
+ public static final WatchEvent.Kind<Void> OVERFLOW =
+ new StdWatchEventKind<Void>("OVERFLOW", Void.class);
+
+ /**
+ * Directory entry created.
+ *
+ * <p> When a directory is registered for this event then the {@link WatchKey}
+ * is queued when it is observed that an entry is created in the directory
+ * or renamed into the directory. The event {@link WatchEvent#count count}
+ * for this event is always {@code 1}.
+ */
+ public static final WatchEvent.Kind<Path> ENTRY_CREATE =
+ new StdWatchEventKind<Path>("ENTRY_CREATE", Path.class);
+
+ /**
+ * Directory entry deleted.
+ *
+ * <p> When a directory is registered for this event then the {@link WatchKey}
+ * is queued when it is observed that an entry is deleted or renamed out of
+ * the directory. The event {@link WatchEvent#count count} for this event
+ * is always {@code 1}.
+ */
+ public static final WatchEvent.Kind<Path> ENTRY_DELETE =
+ new StdWatchEventKind<Path>("ENTRY_DELETE", Path.class);
+
+ /**
+ * Directory entry modified.
+ *
+ * <p> When a directory is registered for this event then the {@link WatchKey}
+ * is queued when it is observed that an entry in the directory has been
+ * modified. The event {@link WatchEvent#count count} for this event is
+ * {@code 1} or greater.
+ */
+ public static final WatchEvent.Kind<Path> ENTRY_MODIFY =
+ new StdWatchEventKind<Path>("ENTRY_MODIFY", Path.class);
+
+ private static class StdWatchEventKind<T> implements WatchEvent.Kind<T> {
+ private final String name;
+ private final Class<T> type;
+ StdWatchEventKind(String name, Class<T> type) {
+ this.name = name;
+ this.type = type;
+ }
+ @Override public String name() { return name; }
+ @Override public Class<T> type() { return type; }
+ @Override public String toString() { return name; }
+ }
+}
diff --git a/src/share/classes/java/nio/file/WatchEvent.java b/src/share/classes/java/nio/file/WatchEvent.java
new file mode 100644
index 000000000..296efd443
--- /dev/null
+++ b/src/share/classes/java/nio/file/WatchEvent.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+/**
+ * An event or a repeated event for an object that is registered with a {@link
+ * WatchService}.
+ *
+ * <p> An event is classified by its {@link #kind() kind} and has a {@link
+ * #count() count} to indicate the number of times that the event has been
+ * observed. This allows for efficient representation of repeated events. The
+ * {@link #context() context} method returns any context associated with
+ * the event. In the case of a repeated event then the context is the same for
+ * all events.
+ *
+ * <p> Watch events are immutable and safe for use by multiple concurrent
+ * threads.
+ *
+ * @param <T> The type of the context object associated with the event
+ *
+ * @since 1.7
+ */
+
+public abstract class WatchEvent<T> {
+
+ /**
+ * An event kind, for the purposes of identification.
+ *
+ * @since 1.7
+ * @see StandardWatchEventKind
+ */
+ public static interface Kind<T> {
+ /**
+ * Returns the name of the event kind.
+ */
+ String name();
+
+ /**
+ * Returns the type of the {@link WatchEvent#context context} value.
+ */
+ Class<T> type();
+ }
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected WatchEvent() { }
+
+ /**
+ * An event modifier that qualifies how a {@link Watchable} is registered
+ * with a {@link WatchService}.
+ *
+ * <p> This release does not define any <em>standard</em> modifiers.
+ *
+ * @since 1.7
+ * @see Watchable#register
+ */
+ public static interface Modifier {
+ /**
+ * Returns the name of the modifier.
+ */
+ String name();
+ }
+
+ /**
+ * Returns the event kind.
+ *
+ * @return the event kind
+ */
+ public abstract Kind<T> kind();
+
+ /**
+ * Returns the event count. If the event count is greater than {@code 1}
+ * then this is a repeated event.
+ *
+ * @return the event count
+ */
+ public abstract int count();
+
+ /**
+ * Returns the context for the event.
+ *
+ * <p> In the case of {@link StandardWatchEventKind#ENTRY_CREATE ENTRY_CREATE},
+ * {@link StandardWatchEventKind#ENTRY_DELETE ENTRY_DELETE}, and {@link
+ * StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} events the context is
+ * a {@code Path} that is the {@link Path#relativize relative} path between
+ * the directory registered with the watch service, and the entry that is
+ * created, deleted, or modified.
+ *
+ * @return the event context; may be {@code null}
+ */
+ public abstract T context();
+}
diff --git a/src/share/classes/java/nio/file/WatchKey.java b/src/share/classes/java/nio/file/WatchKey.java
new file mode 100644
index 000000000..d065585d8
--- /dev/null
+++ b/src/share/classes/java/nio/file/WatchKey.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.util.List;
+
+/**
+ * A token representing the registration of a {@link Watchable watchable} object
+ * with a {@link WatchService}.
+ *
+ * <p> A watch key is created when a watchable object is registered with a watch
+ * service. The key remains {@link #isValid valid} until:
+ * <ol>
+ * <li> It is cancelled, explicitly, by invoking its {@link #cancel cancel}
+ * method, or</li>
+ * <li> Cancelled implicitly, because the object is no longer accessible,
+ * or </li>
+ * <li> By {@link WatchService#close closing} the watch service. </li>
+ * </ol>
+ *
+ * <p> A watch key has a state. When initially created the key is said to be
+ * <em>ready</em>. When an event is detected then the key is <em>signalled</em>
+ * and queued so that it can be retrieved by invoking the watch service's {@link
+ * WatchService#poll() poll} or {@link WatchService#take() take} methods. Once
+ * signalled, a key remains in this state until its {@link #reset reset} method
+ * is invoked to return the key to the ready state. Events detected while the
+ * key is in the signalled state are queued but do not cause the key to be
+ * re-queued for retrieval from the watch service. Events are retrieved by
+ * invoking the key's {@link #pollEvents pollEvents} method. This method
+ * retrieves and removes all events accumulated for the object. When initially
+ * created, a watch key has no pending events. Typically events are retrieved
+ * when the key is in the signalled state leading to the following idiom:
+ *
+ * <pre>
+ * for (;;) {
+ * // retrieve key
+ * WatchKey key = watcher.take();
+ *
+ * // process events
+ * for (WatchEvent&lt;?&gt; event: key.pollEvents()) {
+ * :
+ * }
+ *
+ * // reset the key
+ * boolean valid = key.reset();
+ * if (!valid) {
+ * // object no longer registered
+ * }
+ * }
+ * </pre>
+ *
+ * <p> Watch keys are safe for use by multiple concurrent threads. Where there
+ * are several threads retrieving signalled keys from a watch service then care
+ * should be taken to ensure that the {@code reset} method is only invoked after
+ * the events for the object have been processed. This ensures that one thread
+ * is processing the events for an object at any time.
+ *
+ * @since 1.7
+ */
+
+public abstract class WatchKey {
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected WatchKey() { }
+
+ /**
+ * Tells whether or not this watch key is valid.
+ *
+ * <p> A watch key is valid upon creation and remains until it is cancelled,
+ * or its watch service is closed.
+ *
+ * @return {@code true} if, and only if, this watch key is valid
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Retrieves and removes all pending events for this watch key, returning
+ * a {@code List} of the events that were retrieved.
+ *
+ * <p> Note that this method does not wait if there are no events pending.
+ *
+ * @return the list of the events retrieved
+ */
+ public abstract List<WatchEvent<?>> pollEvents();
+
+ /**
+ * Resets this watch key.
+ *
+ * <p> If this watch key has been cancelled or this watch key is already in
+ * the ready state then invoking this method has no effect. Otherwise
+ * if there are pending events for the object then this watch key is
+ * immediately re-queued to the watch service. If there are no pending
+ * events then the watch key is put into the ready state and will remain in
+ * that state until an event is detected or the watch key is cancelled.
+ *
+ * @return {@code true} if the watch key is valid and has been reset, and
+ * {@code false} if the watch key could not be reset because it is
+ * no longer {@link #isValid valid}
+ */
+ public abstract boolean reset();
+
+ /**
+ * Cancels the registration with the watch service. Upon return the watch key
+ * will be invalid. If the watch key is enqueued, waiting to be retrieved
+ * from the watch service, then it will remain in the queue until it is
+ * removed. Pending events, if any, remain pending and may be retrieved by
+ * invoking the {@link #pollEvents pollEvents} method event after the key is
+ * cancelled.
+ *
+ * <p> If this watch key has already been cancelled then invoking this
+ * method has no effect. Once cancelled, a watch key remains forever invalid.
+ */
+ public abstract void cancel();
+}
diff --git a/src/share/classes/java/nio/file/WatchService.java b/src/share/classes/java/nio/file/WatchService.java
new file mode 100644
index 000000000..52678df79
--- /dev/null
+++ b/src/share/classes/java/nio/file/WatchService.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A watch service that <em>watches</em> registered objects for changes and
+ * events. For example a file manager may use a watch service to monitor a
+ * directory for changes so that it can update its display of the list of files
+ * when files are created or deleted.
+ *
+ * <p> A {@link Watchable} object is registered with a watch service by invoking
+ * its {@link Watchable#register register} method, returning a {@link WatchKey}
+ * to represent the registration. When an event for an object is detected the
+ * key is <em>signalled</em>, and if not currently signalled, it is queued to
+ * the watch service so that it can be retrieved by consumers that invoke the
+ * {@link #poll() poll} or {@link #take() take} methods to retrieve keys
+ * and process events. Once the events have been processed the consumer
+ * invokes the key's {@link WatchKey#reset reset} method to reset the key which
+ * allows the key to be signalled and re-queued with further events.
+ *
+ * <p> Registration with a watch service is cancelled by invoking the key's
+ * {@link WatchKey#cancel cancel} method. A key that is queued at the time that
+ * it is cancelled remains in the queue until it is retrieved. Depending on the
+ * object, a key may be cancelled automatically. For example, suppose a
+ * directory is watched and the watch service detects that it has been deleted
+ * or its file system is no longer accessible. When a key is cancelled in this
+ * manner it is signalled and queued, if not currently signalled. To ensure
+ * that the consumer is notified the return value from the {@code reset}
+ * method indicates if the key is valid.
+ *
+ * <p> A watch service is safe for use by multiple concurrent consumers. To
+ * ensure that only one consumer processes the events for a particular object at
+ * any time then care should be taken to ensure that the key's {@code reset}
+ * method is only invoked after its events have been processed. The {@link
+ * #close close} method may be invoked at any time to close the service causing
+ * any threads waiting to retrieve keys, to throw {@code
+ * ClosedWatchServiceException}.
+ *
+ * <p> File systems may report events faster than they can be retrieved or
+ * processed and an implementation may impose an unspecified limit on the number
+ * of events that it may accumulate. Where an implementation <em>knowingly</em>
+ * discards events then it arranges for the key's {@link WatchKey#pollEvents
+ * pollEvents} method to return an element with an event type of {@link
+ * StandardWatchEventKind#OVERFLOW OVERFLOW}. This event can be used by the
+ * consumer as a trigger to re-examine the state of the object.
+ *
+ * <p> When an event is reported to indicate that a file in a watched directory
+ * has been modified then there is no guarantee that the program (or programs)
+ * that have modified the file have completed. Care should be taken to coordinate
+ * access with other programs that may be updating the file.
+ * The {@link java.nio.channels.FileChannel FileChannel} class defines methods
+ * to lock regions of a file against access by other programs.
+ *
+ * <h4>Platform dependencies</h4>
+ *
+ * <p> The implementation that observes events from the file system is intended
+ * to map directly on to the native file event notification facility where
+ * available, or to use a primitive mechanism, such as polling, when a native
+ * facility is not available. Consequently, many of the details on how events
+ * are detected, their timeliness, and whether their ordering is preserved are
+ * highly implementation specific. For example, when a file in a watched
+ * directory is modified then it may result in a single {@link
+ * StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} event in some
+ * implementations but several events in other implementations. Short-lived
+ * files (meaning files that are deleted very quickly after they are created)
+ * may not be detected by primitive implementations that periodically poll the
+ * file system to detect changes.
+ *
+ * <p> If a watched file is not located on a local storage device then it is
+ * implementation specific if changes to the file can be detected. In particular,
+ * it is not required that changes to files carried out on remote systems be
+ * detected.
+ *
+ * @since 1.7
+ *
+ * @see FileSystem#newWatchService
+ */
+
+public abstract class WatchService
+ implements Closeable
+{
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected WatchService() { }
+
+ /**
+ * Closes this watch service.
+ *
+ * <p> If a thread is currently blocked in the {@link #take take} or {@link
+ * #poll(long,TimeUnit) poll} methods waiting for a key to be queued then
+ * it immediately receives a {@link ClosedWatchServiceException}. Any
+ * valid keys associated with this watch service are {@link WatchKey#isValid
+ * invalidated}.
+ *
+ * <p> After a watch service is closed, any further attempt to invoke
+ * operations upon it will throw {@link ClosedWatchServiceException}.
+ * If this watch service is already closed then invoking this method
+ * has no effect.
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ */
+ @Override
+ public abstract void close() throws IOException;
+
+ /**
+ * Retrieves and removes the next watch key, or {@code null} if none are
+ * present.
+ *
+ * @return the next watch key, or {@code null}
+ *
+ * @throws ClosedWatchServiceException
+ * if this watch service is closed
+ */
+ public abstract WatchKey poll();
+
+ /**
+ * Retrieves and removes the next watch key, waiting if necessary up to the
+ * specified wait time if none are yet present.
+ *
+ * @param timeout
+ * how to wait before giving up, in units of unit
+ * @param unit
+ * a {@code TimeUnit} determining how to interpret the timeout
+ * parameter
+ *
+ * @return the next watch key, or {@code null}
+ *
+ * @throws ClosedWatchServiceException
+ * if this watch service is closed, or it is closed while waiting
+ * for the next key
+ * @throws InterruptedException
+ * if interrupted while waiting
+ */
+ public abstract WatchKey poll(long timeout, TimeUnit unit)
+ throws InterruptedException;
+
+ /**
+ * Retrieves and removes next watch key, waiting if none are yet present.
+ *
+ * @return the next watch key
+ *
+ * @throws ClosedWatchServiceException
+ * if this watch service is closed, or it is closed while waiting
+ * for the next key
+ * @throws InterruptedException
+ * if interrupted while waiting
+ */
+ public abstract WatchKey take() throws InterruptedException;
+}
diff --git a/src/share/classes/java/nio/file/Watchable.java b/src/share/classes/java/nio/file/Watchable.java
new file mode 100644
index 000000000..9bfa627f7
--- /dev/null
+++ b/src/share/classes/java/nio/file/Watchable.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file;
+
+import java.io.IOException;
+
+/**
+ * An object that may be registered with a watch service so that it can be
+ * <em>watched</em> for changes and events.
+ *
+ * <p> This interface defines the {@link #register register} method to register
+ * the object with a {@link WatchService} returning a {@link WatchKey} to
+ * represent the registration. An object may be registered with more than one
+ * watch service. Registration with a watch service is cancelled by invoking the
+ * key's {@link WatchKey#cancel cancel} method.
+ *
+ * @since 1.7
+ *
+ * @see Path#register
+ */
+
+public interface Watchable {
+
+ /**
+ * Registers an object with a watch service.
+ *
+ * <p> If the file system object identified by this object is currently
+ * registered with the watch service then the watch key, representing that
+ * registration, is returned after changing the event set or modifiers to
+ * those specified by the {@code events} and {@code modifiers} parameters.
+ * Changing the event set does not cause pending events for the object to be
+ * discarded. Objects are automatically registered for the {@link
+ * StandardWatchEventKind#OVERFLOW OVERFLOW} event. This event is not
+ * required to be present in the array of events.
+ *
+ * <p> Otherwise the file system object has not yet been registered with the
+ * given watch service, so it is registered and the resulting new key is
+ * returned.
+ *
+ * <p> Implementations of this interface should specify the events they
+ * support.
+ *
+ * @param watcher
+ * the watch service to which this object is to be registered
+ * @param events
+ * the events for which this object should be registered
+ * @param modifiers
+ * the modifiers, if any, that modify how the object is registered
+ *
+ * @return a key representing the registration of this object with the
+ * given watch service
+ *
+ * @throws UnsupportedOperationException
+ * if unsupported events or modifiers are specified
+ * @throws IllegalArgumentException
+ * if an invalid of combination of events are modifiers are specified
+ * @throws ClosedWatchServiceException
+ * if the watch service is closed
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission required to monitor this object. Implementations of
+ * this interface should specify the permission checks.
+ */
+ WatchKey register(WatchService watcher,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException;
+
+
+ /**
+ * Registers an object with a watch service.
+ *
+ * <p> An invocation of this method behaves in exactly the same way as the
+ * invocation
+ * <pre>
+ * watchable.{@link #register(WatchService,WatchEvent.Kind[],WatchEvent.Modifier[]) register}(watcher, events, new WatchEvent.Modifier[0]);
+ * </pre>
+ *
+ * @param watcher
+ * the watch service to which this object is to be registered
+ * @param events
+ * the events for which this object should be registered
+ *
+ * @return a key representing the registration of this object with the
+ * given watch service
+ *
+ * @throws UnsupportedOperationException
+ * if unsupported events are specified
+ * @throws IllegalArgumentException
+ * if an invalid of combination of events are specified
+ * @throws ClosedWatchServiceException
+ * if the watch service is closed
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * if a security manager is installed and it denies an unspecified
+ * permission required to monitor this object. Implementations of
+ * this interface should specify the permission checks.
+ */
+ WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events)
+ throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/AclEntry.java b/src/share/classes/java/nio/file/attribute/AclEntry.java
new file mode 100644
index 000000000..817e1b047
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/AclEntry.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.util.*;
+
+/**
+ * An entry in an access control list (ACL).
+ *
+ * <p> The ACL entry represented by this class is based on the ACL model
+ * specified in <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC&nbsp;3530:
+ * Network File System (NFS) version 4 Protocol</i></a>. Each entry has four
+ * components as follows:
+ *
+ * <ol>
+ * <li><p> The {@link #type() type} component determines if the entry
+ * grants or denies access. </p></li>
+ *
+ * <li><p> The {@link #principal() principal} component, sometimes called the
+ * "who" component, is a {@link UserPrincipal} corresponding to the identity
+ * that the entry grants or denies access
+ * </p></li>
+ *
+ * <li><p> The {@link #permissions permissions} component is a set of
+ * {@link AclEntryPermission permissions}
+ * </p></li>
+ *
+ * <li><p> The {@link #flags flags} component is a set of {@link AclEntryFlag
+ * flags} to indicate how entries are inherited and propagated </p></li>
+ * </ol>
+ *
+ * <p> ACL entries are created using an associated {@link Builder} object by
+ * invoking its {@link Builder#build build} method.
+ *
+ * <p> ACL entries are immutable and are safe for use by multiple concurrent
+ * threads.
+ *
+ * @since 1.7
+ */
+
+public final class AclEntry {
+
+ private final AclEntryType type;
+ private final UserPrincipal who;
+ private final Set<AclEntryPermission> perms;
+ private final Set<AclEntryFlag> flags;
+
+ // cached hash code
+ private volatile int hash;
+
+ // private constructor
+ private AclEntry(AclEntryType type,
+ UserPrincipal who,
+ Set<AclEntryPermission> perms,
+ Set<AclEntryFlag> flags)
+ {
+ this.type = type;
+ this.who = who;
+ this.perms = perms;
+ this.flags = flags;
+ }
+
+ /**
+ * A builder of {@link AclEntry} objects.
+ *
+ * <p> A {@code Builder} object is obtained by invoking one of the {@link
+ * AclEntry#newBuilder newBuilder} methods defined by the {@code AclEntry}
+ * class.
+ *
+ * <p> Builder objects are mutable and are not safe for use by multiple
+ * concurrent threads without appropriate synchronization.
+ *
+ * @since 1.7
+ */
+ public static final class Builder {
+ private AclEntryType type;
+ private UserPrincipal who;
+ private Set<AclEntryPermission> perms;
+ private Set<AclEntryFlag> flags;
+
+ private Builder(AclEntryType type,
+ UserPrincipal who,
+ Set<AclEntryPermission> perms,
+ Set<AclEntryFlag> flags)
+ {
+ assert perms != null && flags != null;
+ this.type = type;
+ this.who = who;
+ this.perms = perms;
+ this.flags = flags;
+ }
+
+ /**
+ * Constructs an {@link AclEntry} from the components of this builder.
+ * The type and who components are required to have been set in order
+ * to construct an {@code AclEntry}.
+ *
+ * @return a new ACL entry
+ *
+ * @throws IllegalStateException
+ * if the type or who component have not been set
+ */
+ public AclEntry build() {
+ if (type == null)
+ throw new IllegalStateException("Missing type component");
+ if (who == null)
+ throw new IllegalStateException("Missing who component");
+ return new AclEntry(type, who, perms, flags);
+ }
+
+ /**
+ * Sets the type component of this builder.
+ *
+ * @return this builder
+ */
+ public Builder setType(AclEntryType type) {
+ if (type == null)
+ throw new NullPointerException();
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Sets the principal component of this builder.
+ *
+ * @return this builder
+ */
+ public Builder setPrincipal(UserPrincipal who) {
+ if (who == null)
+ throw new NullPointerException();
+ this.who = who;
+ return this;
+ }
+
+ // check set only contains elements of the given type
+ private static void checkSet(Set<?> set, Class<?> type) {
+ for (Object e: set) {
+ if (e == null)
+ throw new NullPointerException();
+ type.cast(e);
+ }
+ }
+
+ /**
+ * Sets the permissions component of this builder. On return, the
+ * permissions component of this builder is a copy of the given set.
+ *
+ * @return this builder
+ *
+ * @throws ClassCastException
+ * if the set contains elements that are not of type {@code
+ * AclEntryPermission}
+ */
+ public Builder setPermissions(Set<AclEntryPermission> perms) {
+ // copy and check for erroneous elements
+ perms = new HashSet<AclEntryPermission>(perms);
+ checkSet(perms, AclEntryPermission.class);
+ this.perms = perms;
+ return this;
+ }
+
+ /**
+ * Sets the permissions component of this builder. On return, the
+ * permissions component of this builder is a copy of the permissions in
+ * the given array.
+ *
+ * @return this builder
+ */
+ public Builder setPermissions(AclEntryPermission... perms) {
+ Set<AclEntryPermission> set =
+ new HashSet<AclEntryPermission>(perms.length);
+ // copy and check for null elements
+ for (AclEntryPermission p: perms) {
+ if (p == null)
+ throw new NullPointerException();
+ set.add(p);
+ }
+ this.perms = set;
+ return this;
+ }
+
+ /**
+ * Sets the flags component of this builder. On return, the flags
+ * component of this builder is a copy of the given set.
+ *
+ * @return this builder
+ *
+ * @throws ClassCastException
+ * if the set contains elements that are not of type {@code
+ * AclEntryFlag}
+ */
+ public Builder setFlags(Set<AclEntryFlag> flags) {
+ // copy and check for erroneous elements
+ flags = new HashSet<AclEntryFlag>(flags);
+ checkSet(flags, AclEntryFlag.class);
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * Sets the flags component of this builder. On return, the flags
+ * component of this builder is a copy of the flags in the given
+ * array.
+ *
+ * @return this builder
+ */
+ public Builder setFlags(AclEntryFlag... flags) {
+ Set<AclEntryFlag> set = new HashSet<AclEntryFlag>(flags.length);
+ // copy and check for null elements
+ for (AclEntryFlag f: flags) {
+ if (f == null)
+ throw new NullPointerException();
+ set.add(f);
+ }
+ this.flags = set;
+ return this;
+ }
+ }
+
+ /**
+ * Constructs a new builder. The initial value of the type and who
+ * components is {@code null}. The initial value of the permissions and
+ * flags components is the empty set.
+ *
+ * @return a new builder
+ */
+ public static Builder newBuilder() {
+ Set<AclEntryPermission> perms = Collections.emptySet();
+ Set<AclEntryFlag> flags = Collections.emptySet();
+ return new Builder(null, null, perms, flags);
+ }
+
+ /**
+ * Constructs a new builder with the components of an existing ACL entry.
+ *
+ * @param entry
+ * an ACL entry
+ *
+ * @return a new builder
+ */
+ public static Builder newBuilder(AclEntry entry) {
+ return new Builder(entry.type, entry.who, entry.perms, entry.flags);
+ }
+
+ /**
+ * Returns the ACL entry type.
+ */
+ public AclEntryType type() {
+ return type;
+ }
+
+ /**
+ * Returns the principal component.
+ */
+ public UserPrincipal principal() {
+ return who;
+ }
+
+ /**
+ * Returns a copy of the permissions component.
+ *
+ * <p> The returned set is a modifiable copy of the permissions.
+ */
+ public Set<AclEntryPermission> permissions() {
+ return new HashSet<AclEntryPermission>(perms);
+ }
+
+ /**
+ * Returns a copy of the flags component.
+ *
+ * <p> The returned set is a modifiable copy of the flags.
+ */
+ public Set<AclEntryFlag> flags() {
+ return new HashSet<AclEntryFlag>(flags);
+ }
+
+ /**
+ * Compares the specified object with this ACL entry for equality.
+ *
+ * <p> If the given object is not an {@code AclEntry} then this method
+ * immediately returns {@code false}.
+ *
+ * <p> For two ACL entries to be considered equals requires that they are
+ * both the same type, their who components are equal, their permissions
+ * components are equal, and their flags components are equal.
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * java.lang.Object#equals(Object) Object.equals} method. </p>
+ *
+ * @param ob the object to which this object is to be compared
+ *
+ * @return {@code true} if, and only if, the given object is an AclEntry that
+ * is identical to this AclEntry
+ */
+ @Override
+ public boolean equals(Object ob) {
+ if (ob == this)
+ return true;
+ if (ob == null || !(ob instanceof AclEntry))
+ return false;
+ AclEntry other = (AclEntry)ob;
+ if (this.type != other.type)
+ return false;
+ if (!this.who.equals(other.who))
+ return false;
+ if (!this.perms.equals(other.perms))
+ return false;
+ if (!this.flags.equals(other.flags))
+ return false;
+ return true;
+ }
+
+ private static int hash(int h, Object o) {
+ return h * 127 + o.hashCode();
+ }
+
+ /**
+ * Returns the hash-code value for this ACL entry.
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * Object#hashCode} method.
+ */
+ @Override
+ public int hashCode() {
+ // return cached hash if available
+ if (hash != 0)
+ return hash;
+ int h = type.hashCode();
+ h = hash(h, who);
+ h = hash(h, perms);
+ h = hash(h, flags);
+ hash = h;
+ return hash;
+ }
+
+ /**
+ * Returns the string representation of this ACL entry.
+ *
+ * @return the string representation of this entry
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ // who
+ sb.append(who.getName());
+ sb.append(':');
+
+ // permissions
+ for (AclEntryPermission perm: perms) {
+ sb.append(perm.name());
+ sb.append('/');
+ }
+ sb.setLength(sb.length()-1); // drop final slash
+ sb.append(':');
+
+ // flags
+ if (!flags.isEmpty()) {
+ for (AclEntryFlag flag: flags) {
+ sb.append(flag.name());
+ sb.append('/');
+ }
+ sb.setLength(sb.length()-1); // drop final slash
+ sb.append(':');
+ }
+
+ // type
+ sb.append(type.name());
+ return sb.toString();
+ }
+}
diff --git a/src/share/classes/java/nio/file/attribute/AclEntryFlag.java b/src/share/classes/java/nio/file/attribute/AclEntryFlag.java
new file mode 100644
index 000000000..8cad24d19
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/AclEntryFlag.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * Defines the flags for used by the flags component of an ACL {@link AclEntry
+ * entry}.
+ *
+ * <p> In this release, this class does not define flags related to {@link
+ * AclEntryType#AUDIT} and {@link AclEntryType#ALARM} entry types.
+ *
+ * @since 1.7
+ */
+
+public enum AclEntryFlag {
+
+ /**
+ * Can be placed on a directory and indicates that the ACL entry should be
+ * added to each new non-directory file created.
+ */
+ FILE_INHERIT,
+
+ /**
+ * Can be placed on a directory and indicates that the ACL entry should be
+ * added to each new directory created.
+ */
+ DIRECTORY_INHERIT,
+
+ /**
+ * Can be placed on a directory to indicate that the ACL entry should not
+ * be placed on the newly created directory which is inheritable by
+ * subdirectories of the created directory.
+ */
+ NO_PROPAGATE_INHERIT,
+
+ /**
+ * Can be placed on a directory but does not apply to the directory,
+ * only to newly created files/directories as specified by the
+ * {@link #FILE_INHERIT} and {@link #DIRECTORY_INHERIT} flags.
+ */
+ INHERIT_ONLY;
+}
diff --git a/src/share/classes/java/nio/file/attribute/AclEntryPermission.java b/src/share/classes/java/nio/file/attribute/AclEntryPermission.java
new file mode 100644
index 000000000..b88431c04
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/AclEntryPermission.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * Defines the permissions for use with the permissions component of an ACL
+ * {@link AclEntry entry}.
+ *
+ * @since 1.7
+ */
+
+public enum AclEntryPermission {
+
+ /**
+ * Permission to read the data of the file.
+ */
+ READ_DATA,
+
+ /**
+ * Permission to modify the file's data.
+ */
+ WRITE_DATA,
+
+ /**
+ * Permission to append data to a file.
+ */
+ APPEND_DATA,
+
+ /**
+ * Permission to read the named attributes of a file.
+ *
+ * <p> <a href="http://www.ietf.org/rfc/rfc3530.txt">RFC&nbsp;3530: Network
+ * File System (NFS) version 4 Protocol</a> defines <em>named attributes</em>
+ * as opaque files associated with a file in the file system.
+ */
+ READ_NAMED_ATTRS,
+
+ /**
+ * Permission to write the named attributes of a file.
+ *
+ * <p> <a href="http://www.ietf.org/rfc/rfc3530.txt">RFC&nbsp;3530: Network
+ * File System (NFS) version 4 Protocol</a> defines <em>named attributes</em>
+ * as opaque files associated with a file in the file system.
+ */
+ WRITE_NAMED_ATTRS,
+
+ /**
+ * Permission to execute a file.
+ */
+ EXECUTE,
+
+ /**
+ * Permission to delete a file or directory within a directory.
+ */
+ DELETE_CHILD,
+
+ /**
+ * The ability to read (non-acl) file attributes.
+ */
+ READ_ATTRIBUTES,
+
+ /**
+ * The ability to write (non-acl) file attributes.
+ */
+ WRITE_ATTRIBUTES,
+
+ /**
+ * Permission to delete the file.
+ */
+ DELETE,
+
+ /**
+ * Permission to read the ACL attribute.
+ */
+ READ_ACL,
+
+ /**
+ * Permission to write the ACL attribute.
+ */
+ WRITE_ACL,
+
+ /**
+ * Permission to change the owner.
+ */
+ WRITE_OWNER,
+
+ /**
+ * Permission to access file locally at the server with synchronous reads
+ * and writes.
+ */
+ SYNCHRONIZE;
+
+ /**
+ * Permission to list the entries of a directory (equal to {@link #READ_DATA})
+ */
+ public static final AclEntryPermission LIST_DIRECTORY = READ_DATA;
+
+ /**
+ * Permission to add a new file to a directory (equal to {@link #WRITE_DATA})
+ */
+ public static final AclEntryPermission ADD_FILE = WRITE_DATA;
+
+ /**
+ * Permission to create a subdirectory to a directory (equal to {@link #APPEND_DATA})
+ */
+ public static final AclEntryPermission ADD_SUBDIRECTORY = APPEND_DATA;
+}
diff --git a/src/windows/native/sun/windows/awt_Unicode.h b/src/share/classes/java/nio/file/attribute/AclEntryType.java
index 235140bda..c0051c0e8 100644
--- a/src/windows/native/sun/windows/awt_Unicode.h
+++ b/src/share/classes/java/nio/file/attribute/AclEntryType.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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
@@ -23,28 +23,34 @@
* have any questions.
*/
-/*
- * Unicode to ANSI string conversion macros, based on a slide from a
- * presentation by Asmus Freytag. These must be macros, since the
- * alloca() has to be in the caller's stack space.
- */
-
-#ifndef AWT_UNICODE_H
-#define AWT_UNICODE_H
+package java.nio.file.attribute;
-#include <malloc.h>
+/**
+ * A typesafe enumeration of the access control entry types.
+ *
+ * @since 1.7
+ */
-// Get a Unicode string copy of a Java String object (Java String aren't
-// null-terminated).
-extern LPWSTR J2WHelper(LPWSTR lpw, LPWSTR lpj, int nChars);
-extern LPWSTR J2WHelper1(LPWSTR lpw, LPWSTR lpj, int offset, int nChars);
+public enum AclEntryType {
+ /**
+ * Explicitly grants access to a file or directory.
+ */
+ ALLOW,
-extern LPWSTR JNI_J2WHelper1(JNIEnv *env, LPWSTR lpw, jstring jstr);
+ /**
+ * Explicitly denies access to a file or directory.
+ */
+ DENY,
-#define TO_WSTRING(jstr) \
- ((jstr == NULL) ? NULL : \
- (JNI_J2WHelper1(env, (LPWSTR) alloca((env->GetStringLength(jstr)+1)*2), \
- jstr) \
- ))
+ /**
+ * Log, in a system dependent way, the access specified in the
+ * permissions component of the ACL entry.
+ */
+ AUDIT,
-#endif // AWT_UNICODE_H
+ /**
+ * Generate an alarm, in a system dependent way, the access specified in the
+ * permissions component of the ACL entry.
+ */
+ ALARM
+}
diff --git a/src/share/classes/java/nio/file/attribute/AclFileAttributeView.java b/src/share/classes/java/nio/file/attribute/AclFileAttributeView.java
new file mode 100644
index 000000000..c3d28c914
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/AclFileAttributeView.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.nio.file.*;
+import java.util.List;
+import java.io.IOException;
+
+/**
+ * A file attribute view that supports reading or updating a file's Access
+ * Control Lists (ACL) or file owner attributes.
+ *
+ * <p> ACLs are used to specify access rights to file system objects. An ACL is
+ * an ordered list of {@link AclEntry access-control-entries}, each specifying a
+ * {@link UserPrincipal} and the level of access for that user principal. This
+ * file attribute view defines the {@link #getAcl() getAcl}, and {@link
+ * #setAcl(List) setAcl} methods to read and write ACLs based on the ACL
+ * model specified in <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC&nbsp;3530:
+ * Network File System (NFS) version 4 Protocol</i></a>. This file attribute view
+ * is intended for file system implementations that support the NFSv4 ACL model
+ * or have a <em>well-defined</em> mapping between the NFSv4 ACL model and the ACL
+ * model used by the file system. The details of such mapping are implementation
+ * dependent and are therefore unspecified.
+ *
+ * <p> This class also extends {@code FileOwnerAttributeView} so as to define
+ * methods to get and set the file owner.
+ *
+ * <p> When a file system provides access to a set of {@link FileStore
+ * file-systems} that are not homogeneous then only some of the file systems may
+ * support ACLs. The {@link FileStore#supportsFileAttributeView
+ * supportsFileAttributeView} method can be used to test if a file system
+ * supports ACLs.
+ *
+ * <a name="interop"><h4>Interoperability</h4></a>
+ *
+ * RFC&nbsp;3530 allows for special user identities to be used on platforms that
+ * support the POSIX defined access permissions. The special user identities
+ * are "{@code OWNER@}", "{@code GROUP@}", and "{@code EVERYONE@}". When both
+ * the {@code AclFileAttributeView} and the {@link PosixFileAttributeView}
+ * are supported then these special user identities may be included in ACL {@link
+ * AclEntry entries} that are read or written. The file system's {@link
+ * UserPrincipalLookupService} may be used to obtain a {@link UserPrincipal}
+ * to represent these special identities by invoking the {@link
+ * UserPrincipalLookupService#lookupPrincipalByName lookupPrincipalByName}
+ * method.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we wish to add an entry to an existing ACL to grant "joe" access:
+ * <pre>
+ * // lookup "joe"
+ * UserPrincipal joe = file.getFileSystem().getUserPrincipalLookupService()
+ * .lookupPrincipalByName("joe");
+ *
+ * // get view
+ * AclFileAttributeView view = file.newFileAttributeView(AclFileAttributeView.class);
+ *
+ * // create ACE to give "joe" read access
+ * AclEntry entry = AclEntry.newBuilder()
+ * .setType(AclEntryType.ALLOW)
+ * .setPrincipal(joe)
+ * .setPermissions(AclEntryPermission.READ_DATA, AclEntryPermission.READ_ATTRIBUTES)
+ * .build();
+ *
+ * // read ACL, insert ACE, re-write ACL
+ * List&lt;AclEntry&gt acl = view.getAcl();
+ * acl.add(0, entry); // insert before any DENY entries
+ * view.setAcl(acl);
+ * </pre>
+ *
+ * <h4> Dynamic Access </h4>
+ * <p> Where dynamic access to file attributes is required, the attributes
+ * supported by this attribute view are as follows:
+ * <blockquote>
+ * <table border="1" cellpadding="8">
+ * <tr>
+ * <th> Name </th>
+ * <th> Type </th>
+ * </tr>
+ * <tr>
+ * <td> "acl" </td>
+ * <td> {@link List}&lt;{@link AclEntry}&gt; </td>
+ * </tr>
+ * <tr>
+ * <td> "owner" </td>
+ * <td> {@link UserPrincipal} </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ *
+ * <p> The {@link #getAttribute getAttribute} or {@link #readAttributes
+ * readAttributes} methods may be used to read the ACL or owner attributes as if
+ * by invoking the {@link #getAcl getAcl} or {@link #getOwner getOwner} methods.
+ *
+ * <p> The {@link #setAttribute setAttribute} method may be used to update the
+ * ACL or owner attributes as if by invoking the {@link #setAcl setAcl} or {@link
+ * #setOwner setOwner} methods.
+ *
+ * <h4> Setting the ACL when creating a file </h4>
+ *
+ * <p> Implementations supporting this attribute view may also support setting
+ * the initial ACL when creating a file or directory. The initial ACL
+ * may be provided to methods such as {@link Path#createFile createFile} or {@link
+ * Path#createDirectory createDirectory} as an {@link FileAttribute} with {@link
+ * FileAttribute#name name} {@code "acl:acl"} and a {@link FileAttribute#value
+ * value} that is the list of {@code AclEntry} objects.
+ *
+ * <p> Where an implementation supports an ACL model that differs from the NFSv4
+ * defined ACL model then setting the initial ACL when creating the file must
+ * translate the ACL to the model supported by the file system. Methods that
+ * create a file should reject (by throwing {@link IOException IOException})
+ * any attempt to create a file that would be less secure as a result of the
+ * translation.
+ *
+ * @since 1.7
+ * @see Attributes#getAcl
+ * @see Attributes#setAcl
+ */
+
+public interface AclFileAttributeView
+ extends FileOwnerAttributeView
+{
+ /**
+ * Returns the name of the attribute view. Attribute views of this type
+ * have the name {@code "acl"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * Reads the access control list.
+ *
+ * <p> When the file system uses an ACL model that differs from the NFSv4
+ * defined ACL model, then this method returns an ACL that is the translation
+ * of the ACL to the NFSv4 ACL model.
+ *
+ * <p> The returned list is modifiable so as to facilitate changes to the
+ * existing ACL. The {@link #setAcl setAcl} method is used to update
+ * the file's ACL attribute.
+ *
+ * @return an ordered list of {@link AclEntry entries} representing the
+ * ACL
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ */
+ List<AclEntry> getAcl() throws IOException;
+
+ /**
+ * Updates (replace) the access control list.
+ *
+ * <p> Where the file system supports Access Control Lists, and it uses an
+ * ACL model that differs from the NFSv4 defined ACL model, then this method
+ * must translate the ACL to the model supported by the file system. This
+ * method should reject (by throwing {@link IOException IOException}) any
+ * attempt to write an ACL that would appear to make the file more secure
+ * than would be the case if the ACL were updated. Where an implementation
+ * does not support a mapping of {@link AclEntryType#AUDIT} or {@link
+ * AclEntryType#ALARM} entries, then this method ignores these entries when
+ * writing the ACL.
+ *
+ * <p> If an ACL entry contains a {@link AclEntry#principal user-principal}
+ * that is not associated with the same provider as this attribute view then
+ * {@link ProviderMismatchException} is thrown. Additional validation, if
+ * any, is implementation dependent.
+ *
+ * <p> If the file system supports other security related file attributes
+ * (such as a file {@link PosixFileAttributes#permissions
+ * access-permissions} for example), the updating the access control list
+ * may also cause these security related attributes to be updated.
+ *
+ * @param acl
+ * the new access control list
+ *
+ * @throws IOException
+ * if an I/O error occurs or the ACL is invalid
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ */
+ void setAcl(List<AclEntry> acl) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/AttributeView.java b/src/share/classes/java/nio/file/attribute/AttributeView.java
new file mode 100644
index 000000000..6b0934e7a
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/AttributeView.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * An object that provides a read-only or updatable <em>view</em> of non-opaque
+ * values associated with an object in a filesystem. This interface is extended
+ * or implemented by specific attribute views that define the attributes
+ * supported by the view. A specific attribute view will typically define
+ * type-safe methods to read or update the attributes that it supports. It also
+ * provides <em>dynamic access</em> where the {@link #readAttributes
+ * readAttributes}, {@link #getAttribute getAttribute} and {@link #setAttribute
+ * setAttributs} methods are used to access the attributes by names defined
+ * by the attribute view. Implementations must ensure that the attribute names
+ * do not contain the colon (':') or comma (',') characters.
+ *
+ * @since 1.7
+ */
+
+public interface AttributeView {
+ /**
+ * Returns the name of the attribute view.
+ */
+ String name();
+
+ /**
+ * Reads the value of an attribute.
+ *
+ * @param attribute
+ * the attribute name (case sensitive)
+ *
+ * @return the value of the attribute, or {@code null} if the attribute is
+ * not supported
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * if a security manager is set and it denies access
+ */
+ Object getAttribute(String attribute) throws IOException;
+
+ /**
+ * Sets/updates the value of an attribute.
+ *
+ * @param attribute
+ * the attribute name (case sensitive)
+ * @param value
+ * the attribute value
+ *
+ * @throws UnsupportedOperationException
+ * if the attribute is not supported or this attribute view does
+ * not support updating the value of the attribute
+ * @throws IllegalArgumentException
+ * if the attribute value is of the correct type but has an
+ * inappropriate value
+ * @throws ClassCastException
+ * if the attribute value is not of the expected type or is a
+ * collection containing elements that are not of the expected
+ * type
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * if a security manager is set and it denies access
+ */
+ void setAttribute(String attribute, Object value) throws IOException;
+
+ /**
+ * Reads all, or a subset, of the attributes supported by this file attribute
+ * view.
+ *
+ * <p> The {@code first} and {@code rest} parameters are the names of the
+ * attributes to read. If any of the parameters has the value {@code "*"}
+ * then all attributes are read. Attributes that are not supported are
+ * ignored and will not be present in the returned map. It is implementation
+ * specific if all attributes are read as an atomic operation with respect
+ * to other file system operations.
+ *
+ * @param first
+ * the name of an attribute to read (case sensitive)
+ * @param rest
+ * the names of other attributes to read (case sensitive)
+ *
+ * @return an unmodifiable map of the attributes; may be empty. Its keys are
+ * the attribute names, its values are the attribute values
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * if a security manager is set and it denies access
+ */
+ Map<String,?> readAttributes(String first, String... rest) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/Attributes.java b/src/share/classes/java/nio/file/attribute/Attributes.java
new file mode 100644
index 000000000..b3fe130d5
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/Attributes.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class consists exclusively of static methods that operate on or return
+ * the attributes of files or file stores. These methods provide for convenient
+ * use of the {@link AttributeView attribute-views} defined in this package.
+ *
+ * @since 1.7
+ */
+
+public final class Attributes {
+ private Attributes() {
+ }
+
+ /**
+ * Splits the given attribute name into the name of an attribute view and
+ * the attribute. If the attribute view is not identified then it assumed
+ * to be "basic".
+ */
+ private static String[] split(String attribute) {
+ String[] s = new String[2];
+ int pos = attribute.indexOf(':');
+ if (pos == -1) {
+ s[0] = "basic";
+ s[1] = attribute;
+ } else {
+ s[0] = attribute.substring(0, pos++);
+ s[1] = (pos == attribute.length()) ? "" : attribute.substring(pos);
+ }
+ return s;
+ }
+
+ /**
+ * Sets the value of a file attribute.
+ *
+ * <p> The {@code attribute} parameter identifies the attribute to be set
+ * and takes the form:
+ * <blockquote>
+ * [<i>view-name</i><b>:</b>]<i>attribute-name</i>
+ * </blockquote>
+ * where square brackets [...] delineate an optional component and the
+ * character {@code ':'} stands for itself.
+ *
+ * <p> <i>view-name</i> is the {@link FileAttributeView#name name} of a {@link
+ * FileAttributeView} that identifies a set of file attributes. If not
+ * specified then it defaults to {@code "basic"}, the name of the file
+ * attribute view that identifies the basic set of file attributes common to
+ * many file systems. <i>attribute-name</i> is the name of the attribute
+ * within the set.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we want to set the DOS "hidden" attribute:
+ * <pre>
+ * Attributes.setAttribute(file, "dos:hidden", true);
+ * </pre>
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param attribute
+ * The attribute to set
+ * @param value
+ * The attribute value
+ *
+ * @throws UnsupportedOperationException
+ * If the attribute view is not available or it does not
+ * support updating the attribute
+ * @throws IllegalArgumentException
+ * If the attribute value is of the correct type but has an
+ * inappropriate value
+ * @throws ClassCastException
+ * If the attribute value is not of the expected type or is a
+ * collection containing elements that are not of the expected
+ * type
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file. If this method is invoked
+ * to set security sensitive attributes then the security manager
+ * may be invoked to check for additional permissions.
+ */
+ public static void setAttribute(FileRef file, String attribute, Object value)
+ throws IOException
+ {
+ String[] s = split(attribute);
+ FileAttributeView view = file.getFileAttributeView(s[0]);
+ if (view == null)
+ throw new UnsupportedOperationException("View '" + s[0] + "' not available");
+ view.setAttribute(s[1], value);
+ }
+
+ /**
+ * Reads the value of a file attribute.
+ *
+ * <p> The {@code attribute} parameter identifies the attribute to be read
+ * and takes the form:
+ * <blockquote>
+ * [<i>view-name</i><b>:</b>]<i>attribute-name</i>
+ * </blockquote>
+ * where square brackets [...] delineate an optional component and the
+ * character {@code ':'} stands for itself.
+ *
+ * <p> <i>view-name</i> is the {@link FileAttributeView#name name} of a {@link
+ * FileAttributeView} that identifies a set of file attributes. If not
+ * specified then it defaults to {@code "basic"}, the name of the file
+ * attribute view that identifies the basic set of file attributes common to
+ * many file systems. <i>attribute-name</i> is the name of the attribute.
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled for the case that the file is a symbolic link. By default,
+ * symbolic links are followed and the file attribute of the final target
+ * of the link is read. If the option {@link LinkOption#NOFOLLOW_LINKS
+ * NOFOLLOW_LINKS} is present then symbolic links are not followed and so
+ * the method returns the file attribute of the symbolic link.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we require the user ID of the file owner on a system that
+ * supports a "{@code unix}" view:
+ * <pre>
+ * int uid = (Integer)Attributes.getAttribute(file, "unix:uid");
+ * </pre>
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param attribute
+ * The attribute to read
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return The attribute value, or {@code null} if the attribute view
+ * is not available or it does not support reading the attribute
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, its {@link SecurityManager#checkRead(String) checkRead}
+ * method denies read access to the file. If this method is invoked
+ * to read security sensitive attributes then the security manager
+ * may be invoked to check for additional permissions.
+ */
+ public static Object getAttribute(FileRef file,
+ String attribute,
+ LinkOption... options)
+ throws IOException
+ {
+ String[] s = split(attribute);
+ FileAttributeView view = file.getFileAttributeView(s[0], options);
+ if (view != null)
+ return view.getAttribute(s[1]);
+ // view not available
+ return null;
+ }
+
+ /**
+ * Reads a set of file attributes as a bulk operation.
+ *
+ * <p> The {@code attributes} parameter identifies the attributes to be read
+ * and takes the form:
+ * <blockquote>
+ * [<i>view-name</i><b>:</b>]<i>attribute-list</i>
+ * </blockquote>
+ * where square brackets [...] delineate an optional component and the
+ * character {@code ':'} stands for itself.
+ *
+ * <p> <i>view-name</i> is the {@link FileAttributeView#name name} of a {@link
+ * FileAttributeView} that identifies a set of file attributes. If not
+ * specified then it defaults to {@code "basic"}, the name of the file
+ * attribute view that identifies the basic set of file attributes common to
+ * many file systems.
+ *
+ * <p> The <i>attribute-list</i> component is a comma separated list of
+ * zero or more names of attributes to read. If the list contains the value
+ * {@code "*"} then all attributes are read. Attributes that are not supported
+ * are ignored and will not be present in the returned map. It is
+ * implementation specific if all attributes are read as an atomic operation
+ * with respect to other file system operations.
+ *
+ * <p> The following examples demonstrate possible values for the {@code
+ * attributes} parameter:
+ *
+ * <blockquote>
+ * <table border="0">
+ * <tr>
+ * <td> {@code "*"} </td>
+ * <td> Read all {@link BasicFileAttributes basic-file-attributes}. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code "size,lastModifiedTime,lastAccessTime"} </td>
+ * <td> Reads the file size, last modified time, and last access time
+ * attributes. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code "posix:*"} </td>
+ * <td> Read all {@link PosixFileAttributes POSIX-file-attributes}.. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code "posix:permissions,owner,size"} </td>
+ * <td> Reads the POSX file permissions, owner, and file size. </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled for the case that the file is a symbolic link. By default,
+ * symbolic links are followed and the file attributes of the final target
+ * of the link are read. If the option {@link LinkOption#NOFOLLOW_LINKS
+ * NOFOLLOW_LINKS} is present then symbolic links are not followed and so
+ * the method returns the file attributes of the symbolic link.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param attributes
+ * The attributes to read
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return A map of the attributes returned; may be empty. The map's keys
+ * are the attribute names, its values are the attribute values
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, its {@link SecurityManager#checkRead(String) checkRead}
+ * method denies read access to the file. If this method is invoked
+ * to read security sensitive attributes then the security manager
+ * may be invoke to check for additional permissions.
+ */
+ public static Map<String,?> readAttributes(FileRef file,
+ String attributes,
+ LinkOption... options)
+ throws IOException
+ {
+ String[] s = split(attributes);
+ FileAttributeView view = file.getFileAttributeView(s[0], options);
+ if (view != null) {
+ // further split attributes into the first and rest.
+ String[] names = s[1].split(",");
+ int rem = names.length-1;
+ String first = names[0];
+ String[] rest = new String[rem];
+ if (rem > 0) System.arraycopy(names, 1, rest, 0, rem);
+
+ return view.readAttributes(first, rest);
+ }
+ // view not available
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Reads the basic file attributes of a file.
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled for the case that the file is a symbolic link. By default,
+ * symbolic links are followed and the file attributes of the final target
+ * of the link are read. If the option {@link LinkOption#NOFOLLOW_LINKS
+ * NOFOLLOW_LINKS} is present then symbolic links are not followed and so
+ * the method returns the file attributes of the symbolic link. This option
+ * should be used where there is a need to determine if a file is a
+ * symbolic link:
+ * <pre>
+ * boolean isSymbolicLink = Attributes.readBasicFileAttributes(file, NOFOLLOW_LINKS).isSymbolicLink();
+ * </pre>
+ *
+ * <p> It is implementation specific if all file attributes are read as an
+ * atomic operation with respect to other file system operations.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return The basic file attributes
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, the security manager's {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to file
+ *
+ * @see BasicFileAttributeView#readAttributes
+ */
+ public static BasicFileAttributes readBasicFileAttributes(FileRef file,
+ LinkOption... options)
+ throws IOException
+ {
+ return file.getFileAttributeView(BasicFileAttributeView.class, options)
+ .readAttributes();
+ }
+
+ /**
+ * Reads the POSIX file attributes of a file.
+ *
+ * <p> The {@code file} parameter locates a file that supports the {@link
+ * PosixFileAttributeView}. This file attribute view provides access to a
+ * subset of the file attributes commonly associated with files on file
+ * systems used by operating systems that implement the Portable Operating
+ * System Interface (POSIX) family of standards. It is implementation
+ * specific if all file attributes are read as an atomic operation with
+ * respect to other file system operations.
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled for the case that the file is a symbolic link. By default,
+ * symbolic links are followed and the file attributes of the final target
+ * of the link are read. If the option {@link LinkOption#NOFOLLOW_LINKS
+ * NOFOLLOW_LINKS} is present then symbolic links are not followed and so
+ * the method returns the file attributes of the symbolic link.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return The POSIX file attributes
+ *
+ * @throws UnsupportedOperationException
+ * If the {@code PosixFileAttributeView} is not available
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ *
+ * @see PosixFileAttributeView#readAttributes
+ */
+ public static PosixFileAttributes readPosixFileAttributes(FileRef file,
+ LinkOption... options)
+ throws IOException
+ {
+ PosixFileAttributeView view =
+ file.getFileAttributeView(PosixFileAttributeView.class, options);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ return view.readAttributes();
+ }
+
+ /**
+ * Reads the DOS file attributes of a file.
+ *
+ * <p> The {@code file} parameter locates a file that supports the {@link
+ * DosFileAttributeView}. This file attribute view provides access to
+ * legacy "DOS" attributes supported by the file systems such as File
+ * Allocation Table (FAT), commonly used in <em>consumer devices</em>. It is
+ * implementation specific if all file attributes are read as an atomic
+ * operation with respect to other file system operations.
+ *
+ * <p> The {@code options} array may be used to indicate how symbolic links
+ * are handled for the case that the file is a symbolic link. By default,
+ * symbolic links are followed and the file attributes of the final target
+ * of the link are read. If the option {@link LinkOption#NOFOLLOW_LINKS
+ * NOFOLLOW_LINKS} is present then symbolic links are not followed and so
+ * the method returns the file attributes of the symbolic link.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param options
+ * Options indicating how symbolic links are handled
+ *
+ * @return The DOS file attributes
+ *
+ * @throws UnsupportedOperationException
+ * If the {@code DosFileAttributeView} is not available
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, the security manager's {@link
+ * SecurityManager#checkRead(String) checkRead} method is invoked
+ * to check read access to file
+ *
+ * @see DosFileAttributeView#readAttributes
+ */
+ public static DosFileAttributes readDosFileAttributes(FileRef file,
+ LinkOption... options)
+ throws IOException
+ {
+ DosFileAttributeView view =
+ file.getFileAttributeView(DosFileAttributeView.class, options);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ return view.readAttributes();
+ }
+
+ /**
+ * Returns the owner of a file.
+ *
+ * <p> The {@code file} parameter locates a file that supports the {@link
+ * FileOwnerAttributeView}. This file attribute view provides access to
+ * a file attribute that is the owner of the file.
+ *
+ * @param file
+ * A file reference that locates the file
+ *
+ * @return A user principal representing the owner of the file
+ *
+ * @throws UnsupportedOperationException
+ * If the {@code FileOwnerAttributeView} is not available
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ *
+ * @see FileOwnerAttributeView#getOwner
+ */
+ public static UserPrincipal getOwner(FileRef file) throws IOException {
+ FileOwnerAttributeView view =
+ file.getFileAttributeView(FileOwnerAttributeView.class);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ return view.getOwner();
+ }
+
+ /**
+ * Updates the file owner.
+ *
+ * <p> The {@code file} parameter locates a file that supports the {@link
+ * FileOwnerAttributeView}. This file attribute view provides access to
+ * a file attribute that is the owner of the file.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param owner
+ * The new file owner
+ *
+ * @throws UnsupportedOperationException
+ * If the {@code FileOwnerAttributeView} is not available
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ *
+ * @see FileOwnerAttributeView#setOwner
+ */
+ public static void setOwner(FileRef file, UserPrincipal owner)
+ throws IOException
+ {
+ FileOwnerAttributeView view =
+ file.getFileAttributeView(FileOwnerAttributeView.class);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ view.setOwner(owner);
+ }
+
+ /**
+ * Reads a file's Access Control List (ACL).
+ *
+ * <p> The {@code file} parameter locates a file that supports the {@link
+ * AclFileAttributeView}. This file attribute view provides access to ACLs
+ * based on the ACL model specified in
+ * <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC&nbsp;3530</i></a>.
+ *
+ * @param file
+ * A file reference that locates the file
+ *
+ * @return An ordered list of {@link AclEntry entries} representing the
+ * ACL. The returned list is modifiable.
+ *
+ * @throws UnsupportedOperationException
+ * If the {@code AclAttributeView} is not available
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ *
+ * @see AclFileAttributeView#getAcl
+ */
+ public static List<AclEntry> getAcl(FileRef file) throws IOException {
+ AclFileAttributeView view =
+ file.getFileAttributeView(AclFileAttributeView.class);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ return view.getAcl();
+ }
+
+ /**
+ * Updates a file's Access Control List (ACL).
+ *
+ * <p> The {@code file} parameter locates a file that supports the {@link
+ * AclFileAttributeView}. This file attribute view provides access to ACLs
+ * based on the ACL model specified in
+ * <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC&nbsp;3530</i></a>.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param acl
+ * The new file ACL
+ *
+ * @throws UnsupportedOperationException
+ * If the {@code AclFileAttributeView} is not available
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ *
+ * @see AclFileAttributeView#setAcl
+ */
+ public static void setAcl(FileRef file, List<AclEntry> acl)
+ throws IOException
+ {
+ AclFileAttributeView view =
+ file.getFileAttributeView(AclFileAttributeView.class);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ view.setAcl(acl);
+ }
+
+ /**
+ * Updates the value of a file's last modified time attribute.
+ *
+ * <p> The time value is measured since the epoch
+ * (00:00:00 GMT, January 1, 1970) and is converted to the precision supported
+ * by the file system. Converting from finer to coarser granularities result
+ * in precision loss.
+ *
+ * <p> If the file system does not support a last modified time attribute then
+ * this method has no effect.
+ *
+ * @param file
+ * A file reference that locates the file
+ *
+ * @param lastModifiedTime
+ * The new last modified time, or {@code -1L} to update it to
+ * the current time
+ * @param unit
+ * A {@code TimeUnit} determining how to interpret the
+ * {@code lastModifiedTime} parameter
+ *
+ * @throws IllegalArgumentException
+ * If the {@code lastModifiedime} parameter is a negative value other
+ * than {@code -1L}
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, the security manager's {@link
+ * SecurityManager#checkWrite(String) checkWrite} method is invoked
+ * to check write access to file
+ *
+ * @see BasicFileAttributeView#setTimes
+ */
+ public static void setLastModifiedTime(FileRef file,
+ long lastModifiedTime,
+ TimeUnit unit)
+ throws IOException
+ {
+ file.getFileAttributeView(BasicFileAttributeView.class)
+ .setTimes(lastModifiedTime, null, null, unit);
+ }
+
+ /**
+ * Updates the value of a file's last access time attribute.
+ *
+ * <p> The time value is measured since the epoch
+ * (00:00:00 GMT, January 1, 1970) and is converted to the precision supported
+ * by the file system. Converting from finer to coarser granularities result
+ * in precision loss.
+ *
+ * <p> If the file system does not support a last access time attribute then
+ * this method has no effect.
+ *
+ * @param lastAccessTime
+ * The new last access time, or {@code -1L} to update it to
+ * the current time
+ * @param unit
+ * A {@code TimeUnit} determining how to interpret the
+ * {@code lastModifiedTime} parameter
+ *
+ * @throws IllegalArgumentException
+ * If the {@code lastAccessTime} parameter is a negative value other
+ * than {@code -1L}
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, the security manager's {@link
+ * SecurityManager#checkWrite(String) checkWrite} method is invoked
+ * to check write access to file
+ *
+ * @see BasicFileAttributeView#setTimes
+ */
+ public static void setLastAccessTime(FileRef file,
+ long lastAccessTime,
+ TimeUnit unit)
+ throws IOException
+ {
+ file.getFileAttributeView(BasicFileAttributeView.class)
+ .setTimes(null, lastAccessTime, null, unit);
+ }
+
+ /**
+ * Sets a file's POSIX permissions.
+ *
+ * <p> The {@code file} parameter is a reference to an existing file. It
+ * supports the {@link PosixFileAttributeView} that provides access to file
+ * attributes commonly associated with files on file systems used by
+ * operating systems that implement the Portable Operating System Interface
+ * (POSIX) family of standards.
+ *
+ * @param file
+ * A file reference that locates the file
+ * @param perms
+ * The new set of permissions
+ *
+ * @throws UnsupportedOperationException
+ * If {@code PosixFileAttributeView} is not available
+ * @throws ClassCastException
+ * If the sets contains elements that are not of type {@code
+ * PosixFilePermission}
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ *
+ * @see PosixFileAttributeView#setPermissions
+ */
+ public static void setPosixFilePermissions(FileRef file,
+ Set<PosixFilePermission> perms)
+ throws IOException
+ {
+ PosixFileAttributeView view =
+ file.getFileAttributeView(PosixFileAttributeView.class);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ view.setPermissions(perms);
+ }
+
+ /**
+ * Reads the space attributes of a file store.
+ *
+ * <p> The {@code store} parameter is a file store that supports the
+ * {@link FileStoreSpaceAttributeView} providing access to the space related
+ * attributes of the file store. It is implementation specific if all attributes
+ * are read as an atomic operation with respect to other file system operations.
+ *
+ * @param store
+ * The file store
+ *
+ * @return The file store space attributes
+ *
+ * @throws UnsupportedOperationException
+ * If the file store space attribute view is not supported
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see FileStoreSpaceAttributeView#readAttributes()
+ */
+ public static FileStoreSpaceAttributes readFileStoreSpaceAttributes(FileStore store)
+ throws IOException
+ {
+ FileStoreSpaceAttributeView view =
+ store.getFileStoreAttributeView(FileStoreSpaceAttributeView.class);
+ if (view == null)
+ throw new UnsupportedOperationException();
+ return view.readAttributes();
+ }
+}
diff --git a/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java b/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java
new file mode 100644
index 000000000..70100834d
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+
+/**
+ * A file attribute view that provides a view of a <em>basic set</em> of file
+ * attributes common to many file systems. The basic set of file attributes
+ * consist of <em>mandatory</em> and <em>optional</em> file attributes as
+ * defined by the {@link BasicFileAttributes} interface.
+
+ * <p> The file attributes are retrieved from the file system as a <em>bulk
+ * operation</em> by invoking the {@link #readAttributes() readAttributes} method.
+ * This class also defines the {@link #setTimes setTimes} method to update the
+ * file's time attributes.
+ *
+ * <p> Where dynamic access to file attributes is required, the attributes
+ * supported by this attribute view have the following names and types:
+ * <blockquote>
+ * <table border="1" cellpadding="8">
+ * <tr>
+ * <th> Name </th>
+ * <th> Type </th>
+ * </tr>
+ * <tr>
+ * <td> "lastModifiedTime" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * <tr>
+ * <td> "lastAccessTime" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * <tr>
+ * <td> "creationTime" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * <tr>
+ * <td> "resolution" </td>
+ * <td> {@link java.util.concurrent.TimeUnit} </td>
+ * </tr>
+ * <tr>
+ * <td> "size" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * <tr>
+ * <td> "isRegularFile" </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> "isDirectory" </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> "isSymbolicLink" </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> "isOther" </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> "linkCount" </td>
+ * <td> {@link Integer} </td>
+ * </tr>
+ * <tr>
+ * <td> "fileKey" </td>
+ * <td> {@link Object} </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ *
+ * <p> The {@link #getAttribute getAttribute} or {@link
+ * #readAttributes(String,String[]) readAttributes(String,String[])} methods may
+ * be used to read any of these attributes as if by invoking the {@link
+ * #readAttributes() readAttributes()} method.
+ *
+ * <p> The {@link #setAttribute setAttribute} method may be used to update the
+ * file's last modified time, last access time or create time attributes as if
+ * by invoking the {@link #setTimes setTimes} method. In that case, the time
+ * value is interpreted in {@link TimeUnit#MILLISECONDS milliseconds} and
+ * converted to the precision supported by the file system.
+ *
+ * @since 1.7
+ * @see Attributes
+ */
+
+public interface BasicFileAttributeView
+ extends FileAttributeView
+{
+ /**
+ * Returns the name of the attribute view. Attribute views of this type
+ * have the name {@code "basic"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * Reads the basic file attributes as a bulk operation.
+ *
+ * <p> It is implementation specific if all file attributes are read as an
+ * atomic operation with respect to other file system operations.
+ *
+ * @return the file attributes
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, its {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the file
+ */
+ BasicFileAttributes readAttributes() throws IOException;
+
+ /**
+ * Updates any or all of the file's last modified time, last access time,
+ * and create time attributes.
+ *
+ * <p> This method updates the file's timestamp attributes. The values are
+ * measured since the epoch (00:00:00 GMT, January 1, 1970) and converted to
+ * the precision supported by the file system. Converting from finer to
+ * coarser granularities result in precision loss. If a value is larger
+ * than the maximum supported by the file system then the corresponding
+ * timestamp is set to its maximum value.
+ *
+ * <p> If any of the {@code lastModifiedTime}, {@code lastAccessTime},
+ * or {@code createTime} parameters has the value {@code null} then the
+ * corresponding timestamp is not changed. An implementation may require to
+ * read the existing values of the file attributes when only some, but not
+ * all, of the timestamp attributes are updated. Consequently, this method
+ * may not be an atomic operation with respect to other file system
+ * operations. If all of the {@code lastModifiedTime}, {@code
+ * lastAccessTime} and {@code createTime} parameters are {@code null} then
+ * this method has no effect.
+ *
+ * @param lastModifiedTime
+ * the new last modified time, or {@code -1L} to update it to
+ * the current time, or {@code null} to not change the attribute
+ * @param lastAccessTime
+ * the last access time, or {@code -1L} to update it to
+ * the current time, or {@code null} to not change the attribute.
+ * @param createTime
+ * the file's create time, or {@code -1L} to update it to
+ * the current time, or {@code null} to not change the attribute
+ * @param unit
+ * a {@code TimeUnit} determining how to interpret the time values
+ *
+ * @throws IllegalArgumentException
+ * if any of the parameters is a negative value other than {@code
+ * -1L}
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to the file
+ */
+ void setTimes(Long lastModifiedTime,
+ Long lastAccessTime,
+ Long createTime,
+ TimeUnit unit) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/BasicFileAttributes.java b/src/share/classes/java/nio/file/attribute/BasicFileAttributes.java
new file mode 100644
index 000000000..64c163bc5
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/BasicFileAttributes.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Basic attributes associated with a file in a file system.
+ *
+ * <p> Basic file attributes are attributes that are common to many file systems
+ * and consist of mandatory and optional file attributes as defined by this
+ * interface.
+ *
+ * <p> <b>Usage Example:</b>
+ * <pre>
+ * FileRef file = ...
+ * BasicFileAttributes attrs = Attributes.readBasicFileAttributes(file);
+ * </pre>
+ *
+ * @since 1.7
+ *
+ * @see BasicFileAttributeView
+ */
+
+public interface BasicFileAttributes {
+
+ /**
+ * Returns the time of last modification.
+ *
+ * <p> The {@link #resolution() resolution} method returns the {@link TimeUnit}
+ * to interpret the return value of this method.
+ *
+ * @return a <code>long</code> value representing the time the file was
+ * last modified since the epoch (00:00:00 GMT, January 1, 1970),
+ * or {@code -1L} if the attribute is not supported.
+ */
+ long lastModifiedTime();
+
+ /**
+ * Returns the time of last access if supported.
+ *
+ * <p> The {@link #resolution() resolution} method returns the {@link TimeUnit}
+ * to interpret the return value of this method.
+ *
+ * @return a <code>long</code> value representing the time of last access
+ * since the epoch (00:00:00 GMT, January 1, 1970), or {@code -1L}
+ * if the attribute is not supported.
+ */
+ long lastAccessTime();
+
+ /**
+ * Returns the creation time if supported. The creation time is the time
+ * that the file was created.
+ *
+ * <p> The {@link #resolution() resolution} method returns the {@link TimeUnit}
+ * to interpret the return value of this method.
+ *
+ * @return a <code>long</code> value representing the time the file was
+ * created since the epoch (00:00:00 GMT, January 1, 1970), or
+ * {@code -1L} if the attribute is not supported.
+ */
+ long creationTime();
+
+ /**
+ * Returns the {@link TimeUnit} required to interpret the time of last
+ * modification, time of last access, and creation time.
+ *
+ * @return the {@code TimeUnit} required to interpret the file time stamps
+ */
+ TimeUnit resolution();
+
+ /**
+ * Tells whether the file is a regular file with opaque content.
+ */
+ boolean isRegularFile();
+
+ /**
+ * Tells whether the file is a directory.
+ */
+ boolean isDirectory();
+
+ /**
+ * Tells whether the file is a symbolic-link.
+ */
+ boolean isSymbolicLink();
+
+ /**
+ * Tells whether the file is something other than a regular file, directory,
+ * or link.
+ */
+ boolean isOther();
+
+ /**
+ * Returns the size of the file (in bytes). The size may differ from the
+ * actual size on the file system due to compression, support for sparse
+ * files, or other reasons. The size of files that are not {@link
+ * #isRegularFile regular} files is implementation specific and
+ * therefore unspecified.
+ *
+ * @return the file size, in bytes
+ */
+ long size();
+
+ /**
+ * Returns the number of <em>links</em> to this file.
+ *
+ * <p> On file systems where the same file may be in several directories then
+ * the link count is the number of directory entries for the file. The return
+ * value is {@code 1} on file systems that only allow a file to have a
+ * single name in a single directory.
+ *
+ * @see java.nio.file.Path#createLink
+ */
+ int linkCount();
+
+ /**
+ * Returns an object that uniquely identifies the given file, or {@code
+ * null} if a file key is not available. On some platforms or file systems
+ * it is possible to use an identifier, or a combination of identifiers to
+ * uniquely identify a file. Such identifiers are important for operations
+ * such as file tree traversal in file systems that support <a
+ * href="../package-summary.html#links">symbolic links</a> or file systems
+ * that allow a file to be an entry in more than one directory. On UNIX file
+ * systems, for example, the <em>device ID</em> and <em>inode</em> are
+ * commonly used for such purposes.
+ *
+ * <p> The file key returned by this method can only be guaranteed to be
+ * unique if the file system and files remain static. Whether a file system
+ * re-uses identifiers after a file is deleted is implementation dependent and
+ * therefore unspecified.
+ *
+ * <p> File keys returned by this method can be compared for equality and are
+ * suitable for use in collections. If the file system and files remain static,
+ * and two files are the {@link java.nio.file.FileRef#isSameFile same} with
+ * non-{@code null} file keys, then their file keys are equal.
+ *
+ * @see java.nio.file.Files#walkFileTree
+ */
+ Object fileKey();
+}
diff --git a/src/share/classes/java/nio/file/attribute/DosFileAttributeView.java b/src/share/classes/java/nio/file/attribute/DosFileAttributeView.java
new file mode 100644
index 000000000..c57683999
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/DosFileAttributeView.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.io.IOException;
+
+/**
+ * A file attribute view that provides a view of the legacy "DOS" file attributes.
+ * These attributes are supported by file systems such as the File Allocation
+ * Table (FAT) format commonly used in <em>consumer devices</em>.
+ *
+ * <p> A {@code DosFileAttributeView} is a {@link BasicFileAttributeView} that
+ * additionally supports access to the set of DOS attribute flags that are used
+ * to indicate if the file is read-only, hidden, a system file, or archived.
+ *
+ * <p> Where dynamic access to file attributes is required, the attributes
+ * supported by this attribute view are as defined by {@code
+ * BasicFileAttributeView}, and in addition, the following attributes are
+ * supported:
+ * <blockquote>
+ * <table border="1" cellpadding="8">
+ * <tr>
+ * <th> Name </th>
+ * <th> Type </th>
+ * </tr>
+ * <tr>
+ * <td> readonly </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> hidden </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> system </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * <tr>
+ * <td> archive </td>
+ * <td> {@link Boolean} </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ *
+ * <p> The {@link #getAttribute getAttribute} or {@link #readAttributes(String,String[])
+ * readAttributes(String,String[])} methods may be used to read any of these
+ * attributes, or any of the attributes defined by {@link BasicFileAttributeView}
+ * as if by invoking the {@link #readAttributes readAttributes()} method.
+ *
+ * <p> The {@link #setAttribute setAttribute} method may be used to update the
+ * file's last modified time, last access time or create time attributes as
+ * defined by {@link BasicFileAttributeView}. It may also be used to update
+ * the DOS attributes as if by invoking the {@link #setReadOnly setReadOnly},
+ * {@link #setHidden setHidden}, {@link #setSystem setSystem}, and {@link
+ * #setArchive setArchive} methods respectively.
+ *
+ * @since 1.7
+ */
+
+public interface DosFileAttributeView
+ extends BasicFileAttributeView
+{
+ /**
+ * Returns the name of the attribute view. Attribute views of this type
+ * have the name {@code "dos"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ DosFileAttributes readAttributes() throws IOException;
+
+ /**
+ * Updates the value of the read-only attribute.
+ *
+ * <p> It is implementation specific if the attribute can be updated as an
+ * atomic operation with respect to other file system operations. An
+ * implementation may, for example, require to read the existing value of
+ * the DOS attribute in order to update this attribute.
+ *
+ * @param value
+ * the new value of the attribute
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default, and a security manager is installed,
+ * its {@link SecurityManager#checkWrite(String) checkWrite} method
+ * is invoked to check write access to the file
+ */
+ void setReadOnly(boolean value) throws IOException;
+
+ /**
+ * Updates the value of the hidden attribute.
+ *
+ * <p> It is implementation specific if the attribute can be updated as an
+ * atomic operation with respect to other file system operations. An
+ * implementation may, for example, require to read the existing value of
+ * the DOS attribute in order to update this attribute.
+ *
+ * @param value
+ * the new value of the attribute
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default, and a security manager is installed,
+ * its {@link SecurityManager#checkWrite(String) checkWrite} method
+ * is invoked to check write access to the file
+ */
+ void setHidden(boolean value) throws IOException;
+
+ /**
+ * Updates the value of the system attribute.
+ *
+ * <p> It is implementation specific if the attribute can be updated as an
+ * atomic operation with respect to other file system operations. An
+ * implementation may, for example, require to read the existing value of
+ * the DOS attribute in order to update this attribute.
+ *
+ * @param value
+ * the new value of the attribute
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default, and a security manager is installed,
+ * its {@link SecurityManager#checkWrite(String) checkWrite} method
+ * is invoked to check write access to the file
+ */
+ void setSystem(boolean value) throws IOException;
+
+ /**
+ * Updates the value of the archive attribute.
+ *
+ * <p> It is implementation specific if the attribute can be updated as an
+ * atomic operation with respect to other file system operations. An
+ * implementation may, for example, require to read the existing value of
+ * the DOS attribute in order to update this attribute.
+ *
+ * @param value
+ * the new value of the attribute
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default, and a security manager is installed,
+ * its {@link SecurityManager#checkWrite(String) checkWrite} method
+ * is invoked to check write access to the file
+ */
+ void setArchive(boolean value) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/DosFileAttributes.java b/src/share/classes/java/nio/file/attribute/DosFileAttributes.java
new file mode 100644
index 000000000..53e63d51a
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/DosFileAttributes.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * File attributes associated with a file in a file system that supports
+ * legacy "DOS" attributes.
+ *
+ * <p> The DOS attributes of a file are retrieved using a {@link
+ * DosFileAttributeView} by invoking its {@link DosFileAttributeView#readAttributes
+ * readAttributes} method.
+ *
+ * @since 1.7
+ *
+ * @see Attributes#readDosFileAttributes
+ */
+
+public interface DosFileAttributes
+ extends BasicFileAttributes
+{
+ /**
+ * Returns the value of the read-only attribute.
+ *
+ * <p> This attribute is often used as a simple access control mechanism
+ * to prevent files from being deleted or updated. Whether the file system
+ * or platform does any enforcement to prevent <em>read-only</em> files
+ * from being updated is implementation specific.
+ *
+ * @return the value of the read-only attribute
+ */
+ boolean isReadOnly();
+
+ /**
+ * Returns the value of the hidden attribute.
+ *
+ * <p> This attribute is often used to indicate if the file is visible to
+ * users.
+ *
+ * @return the value of the hidden attribute
+ */
+ boolean isHidden();
+
+ /**
+ * Returns the value of the archive attribute.
+ *
+ * <p> This attribute is typically used by backup programs.
+ *
+ * @return the value of the archive attribute
+ */
+ boolean isArchive();
+
+ /**
+ * Returns the value of the system attribute.
+ *
+ * <p> This attribute is often used to indicate that the file is a component
+ * of the operating system.
+ *
+ * @return the value of the system attribute
+ */
+ boolean isSystem();
+}
diff --git a/src/share/classes/java/nio/file/attribute/FileAttribute.java b/src/share/classes/java/nio/file/attribute/FileAttribute.java
new file mode 100644
index 000000000..ed0d7311f
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/FileAttribute.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * An object that encapsulates the value of a file attribute that can be set
+ * atomically when creating a new file or directory by invoking the {@link
+ * java.nio.file.Path#createFile createFile} or {@link
+ * java.nio.file.Path#createDirectory createDirectory} methods.
+ *
+ * @param <T> The type of the file attribute value
+ *
+ * @since 1.7
+ * @see PosixFilePermissions#asFileAttribute
+ */
+
+public interface FileAttribute<T> {
+ /**
+ * Returns the attribute name.
+ */
+ String name();
+
+ /**
+ * Returns the attribute value.
+ */
+ T value();
+}
diff --git a/src/share/classes/java/nio/file/attribute/FileAttributeView.java b/src/share/classes/java/nio/file/attribute/FileAttributeView.java
new file mode 100644
index 000000000..78a67b4a4
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/FileAttributeView.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * An attribute view that is a read-only or updatable view of non-opaque
+ * values associated with a file in a filesystem. This interface is extended or
+ * implemented by specific file attribute views that define methods to read
+ * and/or update the attributes of a file.
+ *
+ * @since 1.7
+ *
+ * @see java.nio.file.FileRef#getFileAttributeView(Class,java.nio.file.LinkOption[])
+ * @see java.nio.file.FileRef#getFileAttributeView(String,java.nio.file.LinkOption[])
+ */
+
+public interface FileAttributeView
+ extends AttributeView
+{
+}
diff --git a/src/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java b/src/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java
new file mode 100644
index 000000000..0afc19efb
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.io.IOException;
+
+/**
+ * A file attribute view that supports reading or updating the owner of a file.
+ * This file attribute view is intended for file system implementations that
+ * support a file attribute that represents an identity that is the owner of
+ * the file. Often the owner of a file is the identity of the entity that
+ * created the file.
+ *
+ * <p> The {@link #getOwner getOwner} or {@link #setOwner setOwner} methods may
+ * be used to read or update the owner of the file.
+ *
+ * <p> Where dynamic access to file attributes is required, the owner attribute
+ * is identified by the name {@code "owner"}, and the value of the attribute is
+ * a {@link UserPrincipal}. The {@link #readAttributes readAttributes}, {@link
+ * #getAttribute getAttribute} and {@link #setAttribute setAttributes} methods
+ * may be used to read or update the file owner.
+ *
+ * @since 1.7
+ */
+
+public interface FileOwnerAttributeView
+ extends FileAttributeView
+{
+ /**
+ * Returns the name of the attribute view. Attribute views of this type
+ * have the name {@code "owner"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * Read the file owner.
+ *
+ * <p> It it implementation specific if the file owner can be a {@link
+ * GroupPrincipal group}.
+ *
+ * @return the file owner
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserInformation")</tt> or its
+ * {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ */
+ UserPrincipal getOwner() throws IOException;
+
+ /**
+ * Updates the file owner.
+ *
+ * <p> It it implementation specific if the file owner can be a {@link
+ * GroupPrincipal group}. To ensure consistent and correct behavior
+ * across platforms it is recommended that this method should only be used
+ * to set the file owner to a user principal that is not a group.
+ *
+ * @param owner
+ * the new file owner
+ *
+ * @throws IOException
+ * if an I/O error occurs, or the {@code owner} parameter is a
+ * group and this implementation does not support setting the owner
+ * to a group
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserInformation")</tt> or its
+ * {@link SecurityManager#checkWrite(String) checkWrite} method
+ * denies write access to the file.
+ */
+ void setOwner(UserPrincipal owner) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/FileStoreAttributeView.java b/src/share/classes/java/nio/file/attribute/FileStoreAttributeView.java
new file mode 100644
index 000000000..6dfb3ff02
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/FileStoreAttributeView.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * An attribute view that is a read-only or updatable view of the attributes of
+ * a {@link java.nio.file.FileStore}.
+ *
+ * @since 1.7
+ */
+
+public interface FileStoreAttributeView
+ extends AttributeView
+{
+}
diff --git a/src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributeView.java b/src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributeView.java
new file mode 100644
index 000000000..22d38617c
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributeView.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.io.IOException;
+
+/**
+ * A file store attribute view that supports reading of space attributes.
+ *
+ * <p> Where dynamic access to file attributes is required, the attributes
+ * supported by this attribute view have the following names and types:
+ * <blockquote>
+ * <table border="1" cellpadding="8">
+ * <tr>
+ * <th> Name </th>
+ * <th> Type </th>
+ * </tr>
+ * <tr>
+ * <td> "totalSpace" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * <tr>
+ * <td> "usableSpace" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * <tr>
+ * <td> "unallocatedSpace" </td>
+ * <td> {@link Long} </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ * <p> The {@link #getAttribute getAttribute} or {@link #readAttributes
+ * readAttributes(String,String[])} methods may be used to read any of these
+ * attributes as if by invoking the {@link #readAttributes readAttributes()}
+ * method.
+ *
+ * @since 1.7
+ */
+
+public interface FileStoreSpaceAttributeView
+ extends FileStoreAttributeView
+{
+ /**
+ * Returns the name of the attribute view. Attribute views of this type
+ * have the name {@code "space"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * Reads the disk space attributes as a bulk operation.
+ *
+ * <p> It is file system specific if all attributes are read as an
+ * atomic operation with respect to other file system operations.
+ *
+ * @return The disk space attributes
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ FileStoreSpaceAttributes readAttributes() throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributes.java b/src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributes.java
new file mode 100644
index 000000000..6f12d71e5
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributes.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * Space related attributes of a file store.
+ *
+ * @since 1.7
+ *
+ * @see Attributes#readFileStoreSpaceAttributes
+ */
+
+public interface FileStoreSpaceAttributes {
+ /**
+ * Returns the size, in bytes, of the file store.
+ */
+ long totalSpace();
+
+ /**
+ * Returns the number of bytes available to this Java virtual machine on the
+ * file store.
+ *
+ * <p> The returned number of available bytes is a hint, but not a
+ * guarantee, that it is possible to use most or any of these bytes. The
+ * number of usable bytes is most likely to be accurate immediately
+ * after the space attributes are obtained. It is likely to be made inaccurate
+ * by any external I/O operations including those made on the system outside
+ * of this Java virtual machine.
+ */
+ long usableSpace();
+
+ /**
+ * Returns the number of unallocated bytes in the file store.
+ *
+ * <p> The returned number of unallocated bytes is a hint, but not a
+ * guarantee, that it is possible to use most or any of these bytes. The
+ * number of unallocated bytes is most likely to be accurate immediately
+ * after the space attributes are obtained. It is likely to be
+ * made inaccurate by any external I/O operations including those made on
+ * the system outside of this virtual machine.
+ */
+ long unallocatedSpace();
+}
diff --git a/src/share/classes/java/nio/file/attribute/GroupPrincipal.java b/src/share/classes/java/nio/file/attribute/GroupPrincipal.java
new file mode 100644
index 000000000..db09d48ae
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/GroupPrincipal.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+/**
+ * A {@code UserPrincipal} representing a <em>group identity</em>, used to
+ * determine access rights to objects in a file system. The exact definition of
+ * a group is implementation specific, but typically, it represents an identity
+ * created for administrative purposes so as to determine the access rights for
+ * the members of the group. Whether an entity can be a member of multiple
+ * groups, and whether groups can be nested, are implementation specified and
+ * therefore not specified.
+ *
+ * @since 1.7
+ *
+ * @see UserPrincipalLookupService#lookupPrincipalByGroupName
+ */
+
+public interface GroupPrincipal extends UserPrincipal { }
diff --git a/src/share/classes/java/nio/file/attribute/PosixFileAttributeView.java b/src/share/classes/java/nio/file/attribute/PosixFileAttributeView.java
new file mode 100644
index 000000000..285b8bb7d
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/PosixFileAttributeView.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.nio.file.*;
+import java.util.Set;
+import java.io.IOException;
+
+/**
+ * A file attribute view that provides a view of the file attributes commonly
+ * associated with files on file systems used by operating systems that implement
+ * the Portable Operating System Interface (POSIX) family of standards.
+ *
+ * <p> Operating systems that implement the <a href="http://www.opengroup.org">
+ * POSIX</a> family of standards commonly use file systems that have a
+ * file <em>owner</em>, <em>group-owner</em>, and related <em>access
+ * permissions</em>. This file attribute view provides read and write access
+ * to these attributes.
+ *
+ * <p> The {@link #readAttributes() readAttributes} method is used to read the
+ * file's attributes. The file {@link PosixFileAttributes#owner() owner} is
+ * represented by a {@link UserPrincipal} that is the identity of the file owner
+ * for the purposes of access control. The {@link PosixFileAttributes#group()
+ * group-owner}, represented by a {@link GroupPrincipal}, is the identity of the
+ * group owner, where a group is an identity created for administrative purposes
+ * so as to determine the access rights for the members of the group.
+ *
+ * <p> The {@link PosixFileAttributes#permissions() permissions} attribute is a
+ * set of access permissions. This file attribute view provides access to the nine
+ * permission defined by the {@link PosixFilePermission} class.
+ * These nine permission bits determine the <em>read</em>, <em>write</em>, and
+ * <em>execute</em> access for the file owner, group, and others (others
+ * meaning identities other than the owner and members of the group). Some
+ * operating systems and file systems may provide additional permission bits
+ * but access to these other bits is not defined by this class in this release.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we need to print out the owner and access permissions of a file:
+ * <pre>
+ * FileRef file = ...
+ * PosixFileAttributes attrs = file.newFileAttributeView(PosixFileAttributeView.class)
+ * .readAttributes();
+ * System.out.format("%s %s%n",
+ * attrs.owner().getName(),
+ * PosixFilePermissions.toString(attrs.permissions()));
+ * </pre>
+ *
+ * <h4> Dynamic Access </h4>
+ * <p> Where dynamic access to file attributes is required, the attributes
+ * supported by this attribute view are as defined by {@link
+ * BasicFileAttributeView} and {@link FileOwnerAttributeView}, and in addition,
+ * the following attributes are supported:
+ * <blockquote>
+ * <table border="1" cellpadding="8">
+ * <tr>
+ * <th> Name </th>
+ * <th> Type </th>
+ * </tr>
+ * <tr>
+ * <td> "permissions" </td>
+ * <td> {@link Set}&lt;{@link PosixFilePermission}&gt; </td>
+ * </tr>
+ * <tr>
+ * <td> "group" </td>
+ * <td> {@link GroupPrincipal} </td>
+ * </tr>
+ * </table>
+ * </blockquote>
+ *
+ * <p> The {@link #getAttribute getAttribute} or {@link
+ * #readAttributes(String,String[]) readAttributes(String,String[])} methods may
+ * be used to read any of these attributes, or any of the attributes defined by
+ * {@link BasicFileAttributeView} as if by invoking the {@link #readAttributes
+ * readAttributes()} method.
+ *
+ * <p> The {@link #setAttribute setAttribute} method may be used to update the
+ * file's last modified time, last access time or create time attributes as
+ * defined by {@link BasicFileAttributeView}. It may also be used to update
+ * the permissions, owner, or group-owner as if by invoking the {@link
+ * #setPermissions setPermissions}, {@link #setOwner setOwner}, and {@link
+ * #setGroup setGroup} methods respectively.
+ *
+ * <h4> Setting Initial Permissions </h4>
+ * <p> Implementations supporting this attribute view may also support setting
+ * the initial permissions when creating a file or directory. The
+ * initial permissions are provided to the {@link Path#createFile createFile}
+ * or {@link Path#createDirectory createDirectory} methods as a {@link
+ * FileAttribute} with {@link FileAttribute#name name} {@code "posix:permissions"}
+ * and a {@link FileAttribute#value value} that is the set of permissions. The
+ * following example uses the {@link PosixFilePermissions#asFileAttribute
+ * asFileAttribute} method to construct a {@code FileAttribute} when creating a
+ * file:
+ *
+ * <pre>
+ * Path path = ...
+ * Set&lt;PosixFilePermission&gt; perms =
+ * EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ);
+ * path.createFile(PosixFilePermissions.asFileAttribute(perms));
+ * </pre>
+ *
+ * <p> When the access permissions are set at file creation time then the actual
+ * value of the permissions may differ that the value of the attribute object.
+ * The reasons for this are implementation specific. On UNIX systems, for
+ * example, a process has a <em>umask</em> that impacts the permission bits
+ * of newly created files. Where an implementation supports the setting of
+ * the access permissions, and the underlying file system supports access
+ * permissions, then it is required that the value of the actual access
+ * permissions will be equal or less than the value of the attribute
+ * provided to the {@link java.nio.file.Path#createFile createFile} or
+ * {@link java.nio.file.Path#createDirectory createDirectory} methods. In
+ * other words, the file may be more secure than requested.
+ *
+ * @since 1.7
+ *
+ * @see Attributes#readPosixFileAttributes
+ */
+
+public interface PosixFileAttributeView
+ extends BasicFileAttributeView, FileOwnerAttributeView
+{
+ /**
+ * Returns the name of the attribute view. Attribute views of this type
+ * have the name {@code "posix"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ */
+ @Override
+ PosixFileAttributes readAttributes() throws IOException;
+
+ /**
+ * Updates the file permissions.
+ *
+ * @param perms
+ * the new set of permissions
+ *
+ * @throws ClassCastException
+ * if the sets contains elements that are not of type {@code
+ * PosixFilePermission}
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ */
+ void setPermissions(Set<PosixFilePermission> perms) throws IOException;
+
+ /**
+ * Updates the file group-owner.
+ *
+ * @param group
+ * the new file group-owner
+ *
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it denies {@link RuntimePermission}<tt>("accessUserInformation")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ */
+ void setGroup(GroupPrincipal group) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/PosixFileAttributes.java b/src/share/classes/java/nio/file/attribute/PosixFileAttributes.java
new file mode 100644
index 000000000..c09aa9db3
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/PosixFileAttributes.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.util.Set;
+
+/**
+ * File attributes associated with files on file systems used by operating systems
+ * that implement the Portable Operating System Interface (POSIX) family of
+ * standards.
+ *
+ * <p> The POSIX attributes of a file are retrieved using a {@link
+ * PosixFileAttributeView} by invoking its {@link
+ * PosixFileAttributeView#readAttributes readAttributes} method.
+ *
+ * @since 1.7
+ *
+ * @see Attributes#readPosixFileAttributes
+ */
+
+public interface PosixFileAttributes
+ extends BasicFileAttributes
+{
+ /**
+ * Returns the owner of the file.
+ *
+ * @return the file owner
+ *
+ * @see PosixFileAttributeView#setOwner
+ */
+ UserPrincipal owner();
+
+ /**
+ * Returns the group owner of the file.
+ *
+ * @return the file group owner
+ *
+ * @see PosixFileAttributeView#setGroup
+ */
+ GroupPrincipal group();
+
+ /**
+ * Returns the permissions of the file. The file permissions are returned
+ * as a set of {@link PosixFilePermission} elements. The returned set is a
+ * copy of the file permissions and is modifiable. This allows the result
+ * to be modified and passed to the {@link PosixFileAttributeView#setPermissions
+ * setPermissions} method to update the file's permissions.
+ *
+ * @return the file permissions
+ *
+ * @see PosixFileAttributeView#setPermissions
+ */
+ Set<PosixFilePermission> permissions();
+}
diff --git a/src/share/classes/java/nio/file/attribute/PosixFilePermission.java b/src/share/classes/java/nio/file/attribute/PosixFilePermission.java
new file mode 100644
index 000000000..795f50994
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/PosixFilePermission.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.util.*;
+
+/**
+ * Defines the bits for use with the {@link PosixFileAttributes#permissions()
+ * permissions} attribute.
+ *
+ * <p> The {@link PosixFileAttributes} class defines method methods for
+ * manipulating {@link Set sets} of permissions.
+ *
+ * @since 1.7
+ */
+
+public enum PosixFilePermission {
+
+ /**
+ * Read permission, owner.
+ */
+ OWNER_READ,
+
+ /**
+ * Write permission, owner.
+ */
+ OWNER_WRITE,
+
+ /**
+ * Execute/search permission, owner.
+ */
+ OWNER_EXECUTE,
+
+ /**
+ * Read permission, group.
+ */
+ GROUP_READ,
+
+ /**
+ * Write permission, group.
+ */
+ GROUP_WRITE,
+
+ /**
+ * Execute/search permission, group.
+ */
+ GROUP_EXECUTE,
+
+ /**
+ * Read permission, others.
+ */
+ OTHERS_READ,
+
+ /**
+ * Write permission, others.
+ */
+ OTHERS_WRITE,
+
+ /**
+ * Execute/search permission, others.
+ */
+ OTHERS_EXECUTE;
+}
diff --git a/src/share/classes/java/nio/file/attribute/PosixFilePermissions.java b/src/share/classes/java/nio/file/attribute/PosixFilePermissions.java
new file mode 100644
index 000000000..97d322c6a
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/PosixFilePermissions.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import static java.nio.file.attribute.PosixFilePermission.*;
+import java.util.*;
+
+/**
+ * This class consists exclusively of static methods that operate on sets of
+ * {@link PosixFilePermission} objects.
+ *
+ * @since 1.7
+ */
+
+public class PosixFilePermissions {
+ private PosixFilePermissions() { }
+
+ // Write string representation of permission bits to {@code sb}.
+ private static void writeBits(StringBuilder sb, boolean r, boolean w, boolean x) {
+ if (r) {
+ sb.append('r');
+ } else {
+ sb.append('-');
+ }
+ if (w) {
+ sb.append('w');
+ } else {
+ sb.append('-');
+ }
+ if (x) {
+ sb.append('x');
+ } else {
+ sb.append('-');
+ }
+ }
+
+ /**
+ * Returns the {@code String} representation of a set of permissions.
+ *
+ * <p> If the set contains {@code null} or elements that are not of type
+ * {@code PosixFilePermission} then these elements are ignored.
+ *
+ * @param perms
+ * the set of permissions
+ *
+ * @return the string representation of the permission set
+ *
+ * @see #fromString
+ */
+ public static String toString(Set<PosixFilePermission> perms) {
+ StringBuilder sb = new StringBuilder(9);
+ writeBits(sb, perms.contains(OWNER_READ), perms.contains(OWNER_WRITE),
+ perms.contains(OWNER_EXECUTE));
+ writeBits(sb, perms.contains(GROUP_READ), perms.contains(GROUP_WRITE),
+ perms.contains(GROUP_EXECUTE));
+ writeBits(sb, perms.contains(OTHERS_READ), perms.contains(OTHERS_WRITE),
+ perms.contains(OTHERS_EXECUTE));
+ return sb.toString();
+ }
+
+ private static boolean isSet(char c, char setValue) {
+ if (c == setValue)
+ return true;
+ if (c == '-')
+ return false;
+ throw new IllegalArgumentException("Invalid mode");
+ }
+ private static boolean isR(char c) { return isSet(c, 'r'); }
+ private static boolean isW(char c) { return isSet(c, 'w'); }
+ private static boolean isX(char c) { return isSet(c, 'x'); }
+
+ /**
+ * Returns the set of permissions corresponding to a given {@code String}
+ * representation.
+ *
+ * <p> The {@code perms} parameter is a {@code String} representing the
+ * permissions. It has 9 characters that are interpreted as three sets of
+ * three. The first set refers to the owner's permissions; the next to the
+ * group permissions and the last to others. Within each set, the first
+ * character is {@code 'r'} to indicate permission to read, the second
+ * character is {@code 'w'} to indicate permission to write, and the third
+ * character is {@code 'x'} for execute permission. Where a permission is
+ * not set then the corresponding character is set to {@code '-'}.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we require the set of permissions that indicate the owner has read,
+ * write, and execute permissions, the group has read and execute permissions
+ * and others have none.
+ * <pre>
+ * Set&lt;PosixFilePermission&gt; perms = PosixFilePermissions.fromString("rwxr-x---");
+ * </pre>
+ *
+ * @param perms
+ * string representing a set of permissions
+ *
+ * @return the resulting set of permissions
+ *
+ * @throws IllegalArgumentException
+ * if the string cannot be converted to a set of permissions
+ *
+ * @see #toString(Set)
+ */
+ public static Set<PosixFilePermission> fromString(String perms) {
+ if (perms.length() != 9)
+ throw new IllegalArgumentException("Invalid mode");
+ Set<PosixFilePermission> result = new HashSet<PosixFilePermission>();
+ if (isR(perms.charAt(0))) result.add(OWNER_READ);
+ if (isW(perms.charAt(1))) result.add(OWNER_WRITE);
+ if (isX(perms.charAt(2))) result.add(OWNER_EXECUTE);
+ if (isR(perms.charAt(3))) result.add(GROUP_READ);
+ if (isW(perms.charAt(4))) result.add(GROUP_WRITE);
+ if (isX(perms.charAt(5))) result.add(GROUP_EXECUTE);
+ if (isR(perms.charAt(6))) result.add(OTHERS_READ);
+ if (isW(perms.charAt(7))) result.add(OTHERS_WRITE);
+ if (isX(perms.charAt(8))) result.add(OTHERS_EXECUTE);
+ return result;
+ }
+
+ /**
+ * Creates a {@link FileAttribute}, encapsulating a copy of the given file
+ * permissions, suitable for passing to the {@link java.nio.file.Path#createFile
+ * createFile} or {@link java.nio.file.Path#createDirectory createDirectory}
+ * methods.
+ *
+ * @param perms
+ * the set of permissions
+ *
+ * @return an attribute encapsulating the given file permissions with
+ * {@link FileAttribute#name name} {@code "posix:permissions"}
+ *
+ * @throws ClassCastException
+ * if the set contains elements that are not of type {@code
+ * PosixFilePermission}
+ */
+ public static FileAttribute<Set<PosixFilePermission>>
+ asFileAttribute(Set<PosixFilePermission> perms)
+ {
+ // copy set and check for nulls (CCE will be thrown if an element is not
+ // a PosixFilePermission)
+ perms = new HashSet<PosixFilePermission>(perms);
+ for (PosixFilePermission p: perms) {
+ if (p == null)
+ throw new NullPointerException();
+ }
+ final Set<PosixFilePermission> value = perms;
+ return new FileAttribute<Set<PosixFilePermission>>() {
+ @Override
+ public String name() {
+ return "posix:permissions";
+ }
+ @Override
+ public Set<PosixFilePermission> value() {
+ return Collections.unmodifiableSet(value);
+ }
+ };
+ }
+}
diff --git a/src/share/classes/java/nio/file/attribute/UserDefinedFileAttributeView.java b/src/share/classes/java/nio/file/attribute/UserDefinedFileAttributeView.java
new file mode 100644
index 000000000..c0b689601
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/UserDefinedFileAttributeView.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.io.IOException;
+
+/**
+ * A file attribute view that provides a view of a file's user-defined
+ * attributes, sometimes known as <em>extended attributes</em>. User-defined
+ * file attributes are used to store metadata with a file that is not meaningful
+ * to the file system. It is primarily intended for file system implementations
+ * that support such a capability directly but may be emulated. The details of
+ * such emulation are highly implementation specific and therefore not specified.
+ *
+ * <p> This {@code FileAttributeView} provides a view of a file's user-defined
+ * attributes as a set of name/value pairs, where the attribute name is
+ * represented by a {@code String}. An implementation may require to encode and
+ * decode from the platform or file system representation when accessing the
+ * attribute. The value has opaque content. This attribute view defines the
+ * {@link #read read} and {@link #write write} methods to read the value into
+ * or write from a {@link ByteBuffer}. This {@code FileAttributeView} is not
+ * intended for use where the size of an attribute value is larger than {@link
+ * Integer#MAX_VALUE}.
+ *
+ * <p> User-defined attributes may be used in some implementations to store
+ * security related attributes so consequently, in the case of the default
+ * provider at least, all methods that access user-defined attributes require the
+ * {@code RuntimePermission("accessUserDefinedAttributes")} permission when a
+ * security manager is installed.
+ *
+ * <p> The {@link java.nio.file.FileStore#supportsFileAttributeView
+ * supportsFileAttributeView} method may be used to test if a specific {@link
+ * java.nio.file.FileStore FileStore} supports the storage of user-defined
+ * attributes.
+ *
+ * <p> Where dynamic access to file attributes is required, the {@link
+ * #getAttribute getAttribute} or {@link #readAttributes(String,String[])
+ * readAttributes(String,String[])} methods may be used to read the attribute
+ * value. The attribute value is returned as a byte array (byte[]). The {@link
+ * #setAttribute setAttribute} method may be used to write the value of a
+ * user-defined attribute from a buffer (as if by invoking the {@link #write
+ * write} method), or byte array (byte[]).
+ *
+ * @since 1.7
+ */
+
+public interface UserDefinedFileAttributeView
+ extends FileAttributeView
+{
+ /**
+ * Returns the name of this attribute view. Attribute views of this type
+ * have the name {@code "xattr"}.
+ */
+ @Override
+ String name();
+
+ /**
+ * Returns a list containing the names of the user-defined attributes.
+ *
+ * @return An unmodifiable list continaing the names of the file's
+ * user-defined
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserDefinedAttributes")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ */
+ List<String> list() throws IOException;
+
+ /**
+ * Returns the size of the value of a user-defined attribute.
+ *
+ * @param name
+ * The attribute name
+ *
+ * @return The size of the attribute value, in bytes.
+ *
+ * @throws ArithmeticException
+ * If the size of the attribute is larger than {@link Integer#MAX_VALUE}
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserDefinedAttributes")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ */
+ int size(String name) throws IOException;
+
+ /**
+ * Read the value of a user-defined attribute into a buffer.
+ *
+ * <p> This method reads the value of the attribute into the given buffer
+ * as a sequence of bytes, failing if the number of bytes remaining in
+ * the buffer is insufficient to read the complete attribute value. The
+ * number of bytes transferred into the buffer is {@code n}, where {@code n}
+ * is the size of the attribute value. The first byte in the sequence is at
+ * index {@code p} and the last byte is at index {@code p + n - 1}, where
+ * {@code p} is the buffer's position. Upon return the buffer's position
+ * will be equal to {@code p + n}; its limit will not have changed.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we want to read a file's MIME type that is stored as a user-defined
+ * attribute with the name "{@code user.mimetype}".
+ * <pre>
+ * UserDefinedFileAttributeView view = file
+ * .getFileAttributeView(UserDefinedFileAttributeView.class);
+ * String name = "user.mimetype";
+ * ByteBuffer buf = ByteBuffer.allocate(view.size(name));
+ * view.read(name, buf);
+ * buf.flip();
+ * String value = Charset.defaultCharset().decode(buf).toString();
+ * </pre>
+ *
+ * @param name
+ * The attribute name
+ * @param dst
+ * The destination buffer
+ *
+ * @return The number of bytes read, possibly zero
+ *
+ * @throws IllegalArgumentException
+ * If the destination buffer is read-only
+ * @throws IOException
+ * If an I/O error occurs or there is insufficient space in the
+ * destination buffer for the attribute value
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserDefinedAttributes")</tt>
+ * or its {@link SecurityManager#checkRead(String) checkRead} method
+ * denies read access to the file.
+ *
+ * @see #size
+ */
+ int read(String name, ByteBuffer dst) throws IOException;
+
+ /**
+ * Writes the value of a user-defined attribute from a buffer.
+ *
+ * <p> This method writes the value of the attribute from a given buffer as
+ * a sequence of bytes. The size of the value to transfer is {@code r},
+ * where {@code r} is the number of bytes remaining in the buffer, that is
+ * {@code src.remaining()}. The sequence of bytes is transferred from the
+ * buffer starting at index {@code p}, where {@code p} is the buffer's
+ * position. Upon return, the buffer's position will be equal to {@code
+ * p + n}, where {@code n} is the number of bytes transferred; its limit
+ * will not have changed.
+ *
+ * <p> If an attribute of the given name already exists then its value is
+ * replaced. If the attribute does not exist then it is created. If it
+ * implementation specific if a test to check for the existence of the
+ * attribute and the creation of attribute are atomic with repect to other
+ * file system activities.
+ *
+ * <p> Where there is insufficient space to store the attribute, or the
+ * attribute name or value exceed an implementation specific maximum size
+ * then an {@code IOException} is thrown.
+ *
+ * <p> <b>Usage Example:</b>
+ * Suppose we want to write a file's MIME type as a user-defined attribute:
+ * <pre>
+ * UserDefinedFileAttributeView view = file
+ * .getFileAttributeView(UserDefinedFileAttributeView.class);
+ * view.write("user.mimetype", Charset.defaultCharset().encode("text/html"));
+ * </pre>
+ *
+ * @param name
+ * The attribute name
+ * @param src
+ * The buffer containing the attribute value
+ *
+ * @return The number of bytes written, possibly zero
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserDefinedAttributes")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ */
+ int write(String name, ByteBuffer src) throws IOException;
+
+ /**
+ * Deletes a user-defined attribute.
+ *
+ * @param name
+ * The attribute name
+ *
+ * @throws IOException
+ * If an I/O error occurs or the attribute does not exist
+ * @throws SecurityException
+ * In the case of the default provider, a security manager is
+ * installed, and it denies {@link
+ * RuntimePermission}<tt>("accessUserDefinedAttributes")</tt>
+ * or its {@link SecurityManager#checkWrite(String) checkWrite}
+ * method denies write access to the file.
+ */
+ void delete(String name) throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/UserPrincipal.java b/src/share/classes/java/nio/file/attribute/UserPrincipal.java
new file mode 100644
index 000000000..edb04fc6f
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/UserPrincipal.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.security.Principal;
+
+/**
+ * A {@code Principal} representing an identity used to determine access rights
+ * to objects in a file system.
+ *
+ * <p> On many platforms and file systems an entity requires appropriate access
+ * rights or permissions in order to access objects in a file system. The
+ * access rights are generally performed by checking the identity of the entity.
+ * For example, on implementations that use Access Control Lists (ACLs) to
+ * enforce privilege separation then a file in the file system may have an
+ * associated ACL that determines the access rights of identities specified in
+ * the ACL.
+ *
+ * <p> A {@code UserPrincipal} object is an abstract representation of an
+ * identity. It has a {@link #getName() name} that is typically the username or
+ * account name that it represents. User principal objects may be obtained using
+ * a {@link UserPrincipalLookupService}, or returned by {@link
+ * FileAttributeView} implementations that provide access to identity related
+ * attributes. For example, the {@link AclFileAttributeView} and {@link
+ * PosixFileAttributeView} provide access to a file's {@link
+ * PosixFileAttributes#owner owner}.
+ *
+ * @since 1.7
+ */
+
+public interface UserPrincipal extends Principal { }
diff --git a/src/share/classes/java/nio/file/attribute/UserPrincipalLookupService.java b/src/share/classes/java/nio/file/attribute/UserPrincipalLookupService.java
new file mode 100644
index 000000000..ba74882ce
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/UserPrincipalLookupService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.io.IOException;
+
+/**
+ * An object to lookup user and group principals by name. A {@link UserPrincipal}
+ * represents an identity that may be used to determine access rights to objects
+ * in a file system. A {@link GroupPrincipal} represents a <em>group identity</em>.
+ * A {@code UserPrincipalLookupService} defines methods to lookup identities by
+ * name or group name (which are typically user or account names). Whether names
+ * and group names are case sensitive or not depends on the implementation.
+ * The exact definition of a group is implementation specific but typically a
+ * group represents an identity created for administrative purposes so as to
+ * determine the access rights for the members of the group. In particular it is
+ * implementation specific if the <em>namespace</em> for names and groups is the
+ * same or is distinct. To ensure consistent and correct behavior across
+ * platforms it is recommended that this API be used as if the namespaces are
+ * distinct. In other words, the {@link #lookupPrincipalByName
+ * lookupPrincipalByName} should be used to lookup users, and {@link
+ * #lookupPrincipalByGroupName lookupPrincipalByGroupName} should be used to
+ * lookup groups.
+ *
+ * @since 1.7
+ *
+ * @see java.nio.file.FileSystem#getUserPrincipalLookupService
+ */
+
+public abstract class UserPrincipalLookupService {
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected UserPrincipalLookupService() {
+ }
+
+ /**
+ * Lookup a user principal by name.
+ *
+ * @param name
+ * the string representation of the user principal to lookup
+ *
+ * @return a user principal
+ *
+ * @throws UserPrincipalNotFoundException
+ * the principal does not exist
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it checks {@link RuntimePermission}<tt>("lookupUserInformation")</tt>
+ */
+ public abstract UserPrincipal lookupPrincipalByName(String name)
+ throws IOException;
+
+ /**
+ * Lookup a group principal by group name.
+ *
+ * <p> Where an implementation does not support any notion of group then
+ * this method always throws {@link UserPrincipalNotFoundException}. Where
+ * the namespace for user accounts and groups is the same, then this method
+ * is identical to invoking {@link #lookupPrincipalByName
+ * lookupPrincipalByName}.
+ *
+ * @param group
+ * the string representation of the group to lookup
+ *
+ * @return a user principal
+ *
+ * @throws UserPrincipalNotFoundException
+ * the principal does not exist or is not a group
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, it checks {@link RuntimePermission}<tt>("lookupUserInformation")</tt>
+ */
+ public abstract GroupPrincipal lookupPrincipalByGroupName(String group)
+ throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/attribute/UserPrincipalNotFoundException.java b/src/share/classes/java/nio/file/attribute/UserPrincipalNotFoundException.java
new file mode 100644
index 000000000..94cc88ba2
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/UserPrincipalNotFoundException.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.attribute;
+
+import java.io.IOException;
+
+/**
+ * Checked exception thrown when a lookup of {@link UserPrincipal} fails because
+ * the principal does not exist.
+ *
+ * @since 1.7
+ */
+
+public class UserPrincipalNotFoundException
+ extends IOException
+{
+ static final long serialVersionUID = -5369283889045833024L;
+
+ private final String name;
+
+ /**
+ * Constructs an instance of this class.
+ *
+ * @param name
+ * the principal name; may be {@code null}
+ */
+ public UserPrincipalNotFoundException(String name) {
+ super();
+ this.name = name;
+ }
+
+ /**
+ * Returns the user principal name if this exception was created with the
+ * user principal name that was not found, otherwise <tt>null</tt>.
+ *
+ * @return the user principal name or {@code null}
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/share/classes/java/nio/file/attribute/package-info.java b/src/share/classes/java/nio/file/attribute/package-info.java
new file mode 100644
index 000000000..c5301b1f8
--- /dev/null
+++ b/src/share/classes/java/nio/file/attribute/package-info.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * Interfaces and classes providing access to file and file system attributes.
+ *
+ * <blockquote><table cellspacing=1 cellpadding=0 summary="Attribute views">
+ * <tr><th><p align="left">Attribute views</p></th><th><p align="left">Description</p></th></tr>
+ * <tr><td valign=top><tt><i>{@link java.nio.file.attribute.AttributeView}</i></tt></td>
+ * <td>Can read or update non-opaque values associated with objects in a file system</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.file.attribute.FileAttributeView}</i></tt></td>
+ * <td>Can read or update file attributes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.file.attribute.BasicFileAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read or update a basic set of file attributes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.file.attribute.PosixFileAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read or update POSIX defined file attributes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.file.attribute.DosFileAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read or update FAT file attributes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp<i>{@link java.nio.file.attribute.FileOwnerAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read or update the owner of a file</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.file.attribute.AclFileAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read or update Access Control Lists</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.file.attribute.UserDefinedFileAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read or update user-defined file attributes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.file.attribute.FileStoreAttributeView}</i></tt></td>
+ * <td>Can read or update file system attributes</td></tr>
+ * <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.file.attribute.FileStoreSpaceAttributeView}&nbsp;&nbsp;</i></tt></td>
+ * <td>Can read file system <em>space usage</em> related attributes</td></tr>
+ * </table></blockquote>
+ *
+ * <p> An attribute view provides a read-only or updatable view of the non-opaque
+ * values, or <em>metadata</em>, associated with objects in a file system.
+ * The {@link java.nio.file.attribute.FileAttributeView} interface is
+ * extended by several other interfaces that that views to specific sets of file
+ * attributes. {@code FileAttributeViews} are selected by invoking the {@link
+ * java.nio.file.FileRef#getFileAttributeView} method with a
+ * <em>type-token</em> to identify the required view. Views can also be identified
+ * by name. The {@link java.nio.file.attribute.FileStoreAttributeView} interface
+ * provides access to file store attributes. A {@code FileStoreAttributeView} of
+ * a given type is obtained by invoking the {@link
+ * java.nio.file.FileStore#getFileStoreAttributeView} method.
+ *
+ * <p> The {@link java.nio.file.attribute.BasicFileAttributeView}
+ * class defines methods to read and update a <em>basic</em> set of file
+ * attributes that are common to many file systems.
+ *
+ * <p> The {@link java.nio.file.attribute.PosixFileAttributeView}
+ * interface extends {@code BasicFileAttributeView} by defining methods
+ * to access the file attributes commonly used by file systems and operating systems
+ * that implement the Portable Operating System Interface (POSIX) family of
+ * standards.
+ *
+ * <p> The {@link java.nio.file.attribute.DosFileAttributeView}
+ * class extends {@code BasicFileAttributeView} by defining methods to
+ * access the legacy "DOS" file attributes supported on file systems such as File
+ * Allocation Tabl (FAT), commonly used in consumer devices.
+ *
+ * <p> The {@link java.nio.file.attribute.AclFileAttributeView}
+ * class defines methods to read and write the Access Control List (ACL)
+ * file attribute. The ACL model used by this file attribute view is based
+ * on the model defined by <a href="http://www.ietf.org/rfc/rfc3530.txt">
+ * <i>RFC&nbsp;3530: Network File System (NFS) version 4 Protocol</i></a>.
+ *
+ * <p> The {@link java.nio.file.attribute.FileStoreSpaceAttributeView} class
+ * defines methods to read file system space usage related attributes of a file system.
+ *
+ * <p> The {@link java.nio.file.attribute.Attributes} utility class defines
+ * static methods to access file or file system attribute using the above
+ * attribute views.
+ *
+ * <p> In addition to attribute views, this package also defines classes and
+ * interfaces that are used when accessing attributes:
+ *
+ * <ul>
+ *
+ * <p><li> The {@link java.nio.file.attribute.UserPrincipal} and
+ * {@link java.nio.file.attribute.GroupPrincipal} interfaces represent an
+ * identity or group identity. </li>
+ *
+ * <p><li> The {@link java.nio.file.attribute.UserPrincipalLookupService}
+ * interface defines methods to lookup user or group principals. </li>
+ *
+ * <p><li> The {@link java.nio.file.attribute.Attribute} interface
+ * represents the value of an attribute for cases where the attribute value is
+ * require to be set atomically when creating an object in the file system. </li>
+ *
+ * </ul>
+ *
+ *
+ * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
+ * or method in any class or interface in this package will cause a {@link
+ * java.lang.NullPointerException NullPointerException} to be thrown.
+ *
+ * @since 1.7
+ */
+
+package java.nio.file.attribute;
diff --git a/src/share/classes/java/nio/file/package-info.java b/src/share/classes/java/nio/file/package-info.java
new file mode 100644
index 000000000..3623ebe12
--- /dev/null
+++ b/src/share/classes/java/nio/file/package-info.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * Defines interfaces and classes for the Java virtual machine to access files,
+ * file attributes, and file systems.
+ *
+ * <p> The java.nio.file package defines classes to access files and file
+ * systems. The API to access file and file system attributes is defined in the
+ * {@link java.nio.file.attribute} package. The {@link java.nio.file.spi}
+ * package is used by service provider implementors wishing to extend the
+ * platform default provider, or to construct other provider implementations.
+ *
+ * <a name="links"><h3>Symbolic Links</h3></a>
+ * Many operating systems and file systems support for <em>symbolic links</em>.
+ * A symbolic link is a special file that serves as a reference to another file.
+ * For the most part, symbolic links are transparent to applications and
+ * operations on symbolic links are automatically redirected to the <em>target</em>
+ * of the link. Exceptions to this are when a symbolic link is deleted or
+ * renamed/moved in which case the link is deleted or removed rather than the
+ * target of the link. This package includes support for symbolic links where
+ * implementations provide these semantics. File systems may support other types
+ * that are semantically close but support for these other types of links is
+ * not included in this package.
+ *
+ * <a name="interop"><h3>Interoperability</h3></a>
+ * The {@link java.io.File} class defines the {@link java.io.File#toPath
+ * toPath} method to construct a {@link java.nio.file.Path} by converting
+ * the abstract path represented by the {@code java.io.File} object. The resulting
+ * {@code Path} can be used to operate on the same file as the {@code File}
+ * object. The {@code Path} specification provides further information
+ * on the <a href="Path.html#interop">interoperability</a> between {@code Path}
+ * and {@code java.io.File} objects.
+ *
+ * <h3>Visibility</h3>
+ * The view of the files and file system provided by classes in this package are
+ * guaranteed to be consistent with other views provided by other instances in the
+ * same Java virtual machine. The view may or may not, however, be consistent with
+ * the view of the file system as seen by other concurrently running programs due
+ * to caching performed by the underlying operating system and delays induced by
+ * network-filesystem protocols. This is true regardless of the language in which
+ * these other programs are written, and whether they are running on the same machine
+ * or on some other machine. The exact nature of any such inconsistencies are
+ * system-dependent and are therefore unspecified.
+ *
+ * <a name="integrity"><h3>Synchronized I/O File Integrity</h3></a>
+ * The {@link java.nio.file.StandardOpenOption#SYNC SYNC} and {@link
+ * java.nio.file.StandardOpenOption#DSYNC DSYNC} options are used when opening a file
+ * to require that updates to the file are written synchronously to the underlying
+ * storage device. In the case of the default provider, and the file resides on
+ * a local storage device, and the {@link java.nio.channels.SeekableByteChannel
+ * seekable} channel is connected to a file that was opened with one of these
+ * options, then an invocation of the {@link
+ * java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer) write}
+ * method is only guaranteed to return when all changes made to the file
+ * by that invocation have been written to the device. These options are useful
+ * for ensuring that critical information is not lost in the event of a system
+ * crash. If the file does not reside on a local device then no such guarantee
+ * is made. Whether this guarantee is possible with other {@link
+ * java.nio.file.spi.FileSystemProvider provider} implementations is provider
+ * specific.
+ *
+ * <h3>General Exceptions</h3>
+ * Unless otherwise noted, passing a {@code null} argument to a constructor
+ * or method of any class or interface in this package will cause a {@link
+ * java.lang.NullPointerException NullPointerException} to be thrown. Additionally,
+ * invoking a method with a collection containing a {@code null} element will
+ * cause a {@code NullPointerException}, unless otherwise specified.
+ *
+ * <p> Unless otherwise noted, methods that attempt to access the file system
+ * will throw {@link java.nio.file.ClosedFileSystemException} when invoked on
+ * objects associated with a {@link java.nio.file.FileSystem} that has been
+ * {@link java.nio.file.FileSystem#close closed}. Additionally, any methods
+ * that attempt write access to a file system will throw {@link
+ * java.nio.file.ReadOnlyFileSystemException} when invoked on an object associated
+ * with a {@link java.nio.file.FileSystem} that only provides read-only access.
+ *
+ * <p> Unless otherwise noted, invoking a method of any class or interface in
+ * this package created by one {@link java.nio.file.spi.FileSystemProvider
+ * provider} with a parameter that is an object created by another provider,
+ * will throw {@link java.nio.file.ProviderMismatchException}.
+ *
+ * <h3>Optional Specific Exceptions</h3>
+ * Most of the methods defined by classes in this package that access the
+ * file system specify that {@link java.io.IOException} be thrown when an I/O
+ * error occurs. In some cases, these methods define specific I/O exceptions
+ * for common cases. These exceptions, noted as <i>optional specific exceptions</i>,
+ * are thrown by the implementation where it can detect the specific error.
+ * Where the specific error cannot be detected then the more general {@code
+ * IOException} is thrown.
+ *
+ * @since 1.7
+ */
+package java.nio.file;
diff --git a/src/share/classes/java/nio/file/spi/AbstractPath.java b/src/share/classes/java/nio/file/spi/AbstractPath.java
new file mode 100644
index 000000000..133411e3b
--- /dev/null
+++ b/src/share/classes/java/nio/file/spi/AbstractPath.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.spi;
+
+import java.nio.file.*;
+import static java.nio.file.StandardOpenOption.*;
+import java.nio.file.attribute.*;
+import java.nio.channels.*;
+import java.nio.ByteBuffer;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Base implementation class for a {@code Path}.
+ *
+ * <p> This class is intended to be extended by provider implementors. It
+ * implements, or provides default implementations for several of the methods
+ * defined by the {@code Path} class. It implements the {@link #copyTo copyTo}
+ * and {@link #moveTo moveTo} methods for the case that the source and target
+ * are not associated with the same provider.
+ *
+ * @since 1.7
+ */
+
+public abstract class AbstractPath extends Path {
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ protected AbstractPath() { }
+
+ /**
+ * Deletes the file referenced by this object.
+ *
+ * <p> This method invokes the {@link #delete(boolean) delete(boolean)}
+ * method with a parameter of {@code true}. It may be overridden where
+ * required.
+ *
+ * @throws NoSuchFileException {@inheritDoc}
+ * @throws DirectoryNotEmptyException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public void delete() throws IOException {
+ delete(true);
+ }
+
+ /**
+ * Creates a new and empty file, failing if the file already exists.
+ *
+ * <p> This method invokes the {@link #newByteChannel(Set,FileAttribute[])
+ * newByteChannel(Set,FileAttribute...)} method to create the file. It may be
+ * overridden where required.
+ *
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws FileAlreadyExistsException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public Path createFile(FileAttribute<?>... attrs)
+ throws IOException
+ {
+ EnumSet<StandardOpenOption> options = EnumSet.of(CREATE_NEW, WRITE);
+ SeekableByteChannel sbc = newByteChannel(options, attrs);
+ try {
+ sbc.close();
+ } catch (IOException x) {
+ // ignore
+ }
+ return this;
+ }
+
+ /**
+ * Opens or creates a file, returning a seekable byte channel to access the
+ * file.
+ *
+ * <p> This method invokes the {@link #newByteChannel(Set,FileAttribute[])
+ * newByteChannel(Set,FileAttribute...)} method to open or create the file.
+ * It may be overridden where required.
+ *
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws FileAlreadyExistsException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public SeekableByteChannel newByteChannel(OpenOption... options)
+ throws IOException
+ {
+ Set<OpenOption> set = new HashSet<OpenOption>(options.length);
+ Collections.addAll(set, options);
+ return newByteChannel(set);
+ }
+
+ /**
+ * Opens the file located by this path for reading, returning an input
+ * stream to read bytes from the file.
+ *
+ * <p> This method returns an {@code InputStream} that is constructed by
+ * invoking the {@link java.nio.channels.Channels#newInputStream
+ * Channels.newInputStream} method. It may be overridden where a more
+ * efficient implementation is available.
+ *
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public InputStream newInputStream() throws IOException {
+ return Channels.newInputStream(newByteChannel());
+ }
+
+ // opts must be modifiable
+ private OutputStream implNewOutputStream(Set<OpenOption> opts,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ if (opts.isEmpty()) {
+ opts.add(CREATE);
+ opts.add(TRUNCATE_EXISTING);
+ } else {
+ if (opts.contains(READ))
+ throw new IllegalArgumentException("READ not allowed");
+ }
+ opts.add(WRITE);
+ return Channels.newOutputStream(newByteChannel(opts, attrs));
+ }
+
+ /**
+ * Opens or creates the file located by this path for writing, returning an
+ * output stream to write bytes to the file.
+ *
+ * <p> This method returns an {@code OutputStream} that is constructed by
+ * invoking the {@link java.nio.channels.Channels#newOutputStream
+ * Channels.newOutputStream} method. It may be overridden where a more
+ * efficient implementation is available.
+ *
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public OutputStream newOutputStream(OpenOption... options) throws IOException {
+ int len = options.length;
+ Set<OpenOption> opts = new HashSet<OpenOption>(len + 3);
+ if (len > 0) {
+ for (OpenOption opt: options) {
+ opts.add(opt);
+ }
+ }
+ return implNewOutputStream(opts);
+ }
+
+ /**
+ * Opens or creates the file located by this path for writing, returning an
+ * output stream to write bytes to the file.
+ *
+ * <p> This method returns an {@code OutputStream} that is constructed by
+ * invoking the {@link java.nio.channels.Channels#newOutputStream
+ * Channels.newOutputStream} method. It may be overridden where a more
+ * efficient implementation is available.
+ *
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public OutputStream newOutputStream(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ Set<OpenOption> opts = new HashSet<OpenOption>(options);
+ return implNewOutputStream(opts, attrs);
+ }
+
+ /**
+ * Opens the directory referenced by this object, returning a {@code
+ * DirectoryStream} to iterate over all entries in the directory.
+ *
+ * <p> This method invokes the {@link
+ * #newDirectoryStream(java.nio.file.DirectoryStream.Filter)
+ * newDirectoryStream(Filter)} method with a filter that accept all entries.
+ * It may be overridden where required.
+ *
+ * @throws NotDirectoryException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public DirectoryStream<Path> newDirectoryStream() throws IOException {
+ return newDirectoryStream(acceptAllFilter);
+ }
+ private static final DirectoryStream.Filter<Path> acceptAllFilter =
+ new DirectoryStream.Filter<Path>() {
+ @Override public boolean accept(Path entry) { return true; }
+ };
+
+ /**
+ * Opens the directory referenced by this object, returning a {@code
+ * DirectoryStream} to iterate over the entries in the directory. The
+ * entries are filtered by matching the {@code String} representation of
+ * their file names against a given pattern.
+ *
+ * <p> This method constructs a {@link PathMatcher} by invoking the
+ * file system's {@link java.nio.file.FileSystem#getPathMatcher
+ * getPathMatcher} method. This method may be overridden where a more
+ * efficient implementation is available.
+ *
+ * @throws java.util.regex.PatternSyntaxException {@inheritDoc}
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws NotDirectoryException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(String glob)
+ throws IOException
+ {
+ // avoid creating a matcher if all entries are required.
+ if (glob.equals("*"))
+ return newDirectoryStream();
+
+ // create a matcher and return a filter that uses it.
+ final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob);
+ DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
+ @Override
+ public boolean accept(Path entry) {
+ return matcher.matches(entry.getName());
+ }
+ };
+ return newDirectoryStream(filter);
+ }
+
+ /**
+ * Tests whether the file located by this path exists.
+ *
+ * <p> This method invokes the {@link #checkAccess checkAccess} method to
+ * check if the file exists. It may be overridden where a more efficient
+ * implementation is available.
+ */
+ @Override
+ public boolean exists() {
+ try {
+ checkAccess();
+ return true;
+ } catch (IOException x) {
+ // unable to determine if file exists
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether the file located by this path does not exist.
+ *
+ * <p> This method invokes the {@link #checkAccess checkAccess} method to
+ * check if the file exists. It may be overridden where a more efficient
+ * implementation is available.
+ */
+ @Override
+ public boolean notExists() {
+ try {
+ checkAccess();
+ return false;
+ } catch (NoSuchFileException x) {
+ // file confirmed not to exist
+ return true;
+ } catch (IOException x) {
+ return false;
+ }
+ }
+
+ /**
+ * Registers the file located by this path with a watch service.
+ *
+ * <p> This method invokes the {@link #register(WatchService,WatchEvent.Kind[],WatchEvent.Modifier[])
+ * register(WatchService,WatchEvent.Kind[],WatchEvent.Modifier...)}
+ * method to register the file. It may be overridden where required.
+ */
+ @Override
+ public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events)
+ throws IOException
+ {
+ return register(watcher, events, NO_MODIFIERS);
+ }
+ private static final WatchEvent.Modifier[] NO_MODIFIERS = new WatchEvent.Modifier[0];
+
+ /**
+ * Copy the file located by this path to a target location.
+ *
+ * <p> This method is invoked by the {@link #copyTo copyTo} method for
+ * the case that this {@code Path} and the target {@code Path} are
+ * associated with the same provider.
+ *
+ * @param target
+ * The target location
+ * @param options
+ * Options specifying how the copy should be done
+ *
+ * @throws IllegalArgumentException
+ * If an invalid option is specified
+ * @throws FileAlreadyExistsException
+ * The target file exists and cannot be replaced because the
+ * {@code REPLACE_EXISTING} option is not specified, or the target
+ * file is a non-empty directory <i>(optional specific exception)</i>
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkRead(String) checkRead}
+ * method is invoked to check read access to the source file, the
+ * {@link SecurityManager#checkWrite(String) checkWrite} is invoked
+ * to check write access to the target file. If a symbolic link is
+ * copied the security manager is invoked to check {@link
+ * LinkPermission}{@code ("symbolic")}.
+ */
+ protected abstract void implCopyTo(Path target, CopyOption... options)
+ throws IOException;
+
+ /**
+ * Move the file located by this path to a target location.
+ *
+ * <p> This method is invoked by the {@link #moveTo moveTo} method for
+ * the case that this {@code Path} and the target {@code Path} are
+ * associated with the same provider.
+ *
+ * @param target
+ * The target location
+ * @param options
+ * Options specifying how the move should be done
+ *
+ * @throws IllegalArgumentException
+ * If an invalid option is specified
+ * @throws FileAlreadyExistsException
+ * The target file exists and cannot be replaced because the
+ * {@code REPLACE_EXISTING} option is not specified, or the target
+ * file is a non-empty directory
+ * @throws AtomicMoveNotSupportedException
+ * The options array contains the {@code ATOMIC_MOVE} option but
+ * the file cannot be moved as an atomic file system operation.
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default provider, and a security manager is
+ * installed, the {@link SecurityManager#checkWrite(String) checkWrite}
+ * method is invoked to check write access to both the source and
+ * target file.
+ */
+ protected abstract void implMoveTo(Path target, CopyOption... options)
+ throws IOException;
+
+ /**
+ * Copy the file located by this path to a target location.
+ *
+ * <p> If this path is associated with the same {@link FileSystemProvider
+ * provider} as the {@code target} then the {@link #implCopyTo implCopyTo}
+ * method is invoked to copy the file. Otherwise, this method attempts to
+ * copy the file to the target location in a manner that may be less
+ * efficient than would be the case that target is associated with the same
+ * provider as this path.
+ *
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws FileAlreadyExistsException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public final Path copyTo(Path target, CopyOption... options)
+ throws IOException
+ {
+ if ((getFileSystem().provider() == target.getFileSystem().provider())) {
+ implCopyTo(target, options);
+ } else {
+ xProviderCopyTo(target, options);
+ }
+ return target;
+ }
+
+ /**
+ * Move or rename the file located by this path to a target location.
+ *
+ * <p> If this path is associated with the same {@link FileSystemProvider
+ * provider} as the {@code target} then the {@link #implCopyTo implMoveTo}
+ * method is invoked to move the file. Otherwise, this method attempts to
+ * copy the file to the target location and delete the source file. This
+ * implementation may be less efficient than would be the case that
+ * target is associated with the same provider as this path.
+ *
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @throws FileAlreadyExistsException {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ * @throws SecurityException {@inheritDoc}
+ */
+ @Override
+ public final Path moveTo(Path target, CopyOption... options)
+ throws IOException
+ {
+ if ((getFileSystem().provider() == target.getFileSystem().provider())) {
+ implMoveTo(target, options);
+ } else {
+ // different providers so copy + delete
+ xProviderCopyTo(target, convertMoveToCopyOptions(options));
+ delete(false);
+ }
+ return target;
+ }
+
+ /**
+ * Converts the given array of options for moving a file to options suitable
+ * for copying the file when a move is implemented as copy + delete.
+ */
+ private static CopyOption[] convertMoveToCopyOptions(CopyOption... options)
+ throws AtomicMoveNotSupportedException
+ {
+ int len = options.length;
+ CopyOption[] newOptions = new CopyOption[len+2];
+ for (int i=0; i<len; i++) {
+ CopyOption option = options[i];
+ if (option == StandardCopyOption.ATOMIC_MOVE) {
+ throw new AtomicMoveNotSupportedException(null, null,
+ "Atomic move between providers is not supported");
+ }
+ newOptions[i] = option;
+ }
+ newOptions[len] = LinkOption.NOFOLLOW_LINKS;
+ newOptions[len+1] = StandardCopyOption.COPY_ATTRIBUTES;
+ return newOptions;
+ }
+
+ /**
+ * Parses the arguments for a file copy operation.
+ */
+ private static class CopyOptions {
+ boolean replaceExisting = false;
+ boolean copyAttributes = false;
+ boolean followLinks = true;
+
+ private CopyOptions() { }
+
+ static CopyOptions parse(CopyOption... options) {
+ CopyOptions result = new CopyOptions();
+ for (CopyOption option: options) {
+ if (option == StandardCopyOption.REPLACE_EXISTING) {
+ result.replaceExisting = true;
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ result.followLinks = false;
+ continue;
+ }
+ if (option == StandardCopyOption.COPY_ATTRIBUTES) {
+ result.copyAttributes = true;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new IllegalArgumentException("'" + option +
+ "' is not a valid copy option");
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Simple cross-provider copy where the target is a Path.
+ */
+ private void xProviderCopyTo(Path target, CopyOption... options)
+ throws IOException
+ {
+ CopyOptions opts = CopyOptions.parse(options);
+ LinkOption[] linkOptions = (opts.followLinks) ? new LinkOption[0] :
+ new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+
+ // attributes of source file
+ BasicFileAttributes attrs = Attributes
+ .readBasicFileAttributes(this, linkOptions);
+ if (attrs.isSymbolicLink())
+ throw new IOException("Copying of symbolic links not supported");
+
+ // delete target file
+ if (opts.replaceExisting)
+ target.delete(false);
+
+ // create directory or file
+ if (attrs.isDirectory()) {
+ target.createDirectory();
+ } else {
+ xProviderCopyRegularFileTo(target);
+ }
+
+ // copy basic attributes to target
+ if (opts.copyAttributes) {
+ BasicFileAttributeView view = target
+ .getFileAttributeView(BasicFileAttributeView.class, linkOptions);
+ try {
+ view.setTimes(attrs.lastModifiedTime(),
+ attrs.lastAccessTime(),
+ attrs.creationTime(),
+ attrs.resolution());
+ } catch (IOException x) {
+ // rollback
+ try {
+ target.delete(false);
+ } catch (IOException ignore) { }
+ throw x;
+ }
+ }
+ }
+
+
+ /**
+ * Simple copy of regular file to a target file that exists.
+ */
+ private void xProviderCopyRegularFileTo(FileRef target)
+ throws IOException
+ {
+ ReadableByteChannel rbc = newByteChannel();
+ try {
+ // open target file for writing
+ SeekableByteChannel sbc = target.newByteChannel(CREATE, WRITE);
+
+ // simple copy loop
+ try {
+ ByteBuffer buf = ByteBuffer.wrap(new byte[8192]);
+ int n = 0;
+ for (;;) {
+ n = rbc.read(buf);
+ if (n < 0)
+ break;
+ assert n > 0;
+ buf.flip();
+ while (buf.hasRemaining()) {
+ sbc.write(buf);
+ }
+ buf.rewind();
+ }
+
+ } finally {
+ sbc.close();
+ }
+ } finally {
+ rbc.close();
+ }
+ }
+}
diff --git a/src/share/classes/java/nio/file/spi/FileSystemProvider.java b/src/share/classes/java/nio/file/spi/FileSystemProvider.java
new file mode 100644
index 000000000..6b79381ba
--- /dev/null
+++ b/src/share/classes/java/nio/file/spi/FileSystemProvider.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.spi;
+
+import java.nio.file.*;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.channels.*;
+import java.net.URI;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.io.IOException;
+
+/**
+ * Service-provider class for file systems.
+ *
+ * <p> A file system provider is a concrete implementation of this class that
+ * implements the abstract methods defined by this class. A provider is
+ * identified by a {@code URI} {@link #getScheme() scheme}. The default provider
+ * is identified by the URI scheme "file". It creates the {@link FileSystem} that
+ * provides access to the file systems accessible to the Java virtual machine.
+ * The {@link FileSystems} class defines how file system providers are located
+ * and loaded. The default provider is typically a system-default provider but
+ * may be overridden if the system property {@code
+ * java.nio.file.spi.DefaultFileSystemProvider} is set. In that case, the
+ * provider has a one argument constructor whose formal parameter type is {@code
+ * FileSystemProvider}. All other providers have a zero argument constructor
+ * that initializes the provider.
+ *
+ * <p> A provider is a factory for one or more {@link FileSystem} instances. Each
+ * file system is identified by a {@code URI} where the URI's scheme matches
+ * the provider's {@link #getScheme scheme}. The default file system, for example,
+ * is identified by the URI {@code "file:///"}. A memory-based file system,
+ * for example, may be identified by a URI such as {@code "memory:///?name=logfs"}.
+ * The {@link #newFileSystem newFileSystem} method may be used to create a file
+ * system, and the {@link #getFileSystem getFileSystem} method may be used to
+ * obtain a reference to an existing file system created by the provider. Where
+ * a provider is the factory for a single file system then it is provider dependent
+ * if the file system is created when the provider is initialized, or later when
+ * the {@code newFileSystem} method is invoked. In the case of the default
+ * provider, the {@code FileSystem} is created when the provider is initialized.
+ *
+ * <p> In addition to file systems, a provider is also a factory for {@link
+ * FileChannel} and {@link AsynchronousFileChannel} channels. The {@link
+ * #newFileChannel newFileChannel} and {@link #newAsynchronousFileChannel
+ * AsynchronousFileChannel} methods are defined to open or create files, returning
+ * a channel to access the file. These methods are invoked by static factory
+ * methods defined in the {@link java.nio.channels} package.
+ *
+ * <p> All of the methods in this class are safe for use by multiple concurrent
+ * threads.
+ *
+ * @since 1.7
+ */
+
+public abstract class FileSystemProvider {
+ // lock using when loading providers
+ private static final Object lock = new Object();
+
+ // installed providers
+ private static volatile List<FileSystemProvider> installedProviders;
+
+ // used to avoid recursive loading of instaled providers
+ private static boolean loadingProviders = false;
+
+ private static Void checkPermission() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("fileSystemProvider"));
+ return null;
+ }
+ private FileSystemProvider(Void ignore) { }
+
+ /**
+ * Initializes a new instance of this class.
+ *
+ * <p> During construction a provider may safely access files associated
+ * with the default provider but care needs to be taken to avoid circular
+ * loading of other installed providers. If circular loading of installed
+ * providers is detected then an unspecified error is thrown.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link RuntimePermission}<tt>("fileSystemProvider")</tt>
+ */
+ protected FileSystemProvider() {
+ this(checkPermission());
+ }
+
+ // loads all installed providers
+ private static List<FileSystemProvider> loadInstalledProviders() {
+ List<FileSystemProvider> list = new ArrayList<FileSystemProvider>();
+
+ ServiceLoader<FileSystemProvider> sl = ServiceLoader
+ .load(FileSystemProvider.class, ClassLoader.getSystemClassLoader());
+
+ // ServiceConfigurationError may be throw here
+ for (FileSystemProvider provider: sl) {
+ String scheme = provider.getScheme();
+
+ // add to list if the provider is not "file" and isn't a duplicate
+ if (!scheme.equalsIgnoreCase("file")) {
+ boolean found = false;
+ for (FileSystemProvider p: list) {
+ if (p.getScheme().equalsIgnoreCase(scheme)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ list.add(provider);
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Returns a list of the installed file system providers.
+ *
+ * <p> The first invocation of this method causes the default provider to be
+ * initialized (if not already initialized) and loads any other installed
+ * providers as described by the {@link FileSystems} class.
+ *
+ * @return An unmodifiable list of the installed file system providers. The
+ * list contains at least one element, that is the default file
+ * system provider
+ *
+ * @throws ServiceConfigurationError
+ * When an error occurs while loading a service provider
+ */
+ public static List<FileSystemProvider> installedProviders() {
+ if (installedProviders == null) {
+ // ensure default provider is initialized
+ FileSystemProvider defaultProvider = FileSystems.getDefault().provider();
+
+ synchronized (lock) {
+ if (installedProviders == null) {
+ if (loadingProviders) {
+ throw new Error("Circular loading of installed providers detected");
+ }
+ loadingProviders = true;
+
+ List<FileSystemProvider> list = AccessController
+ .doPrivileged(new PrivilegedAction<List<FileSystemProvider>>() {
+ @Override
+ public List<FileSystemProvider> run() {
+ return loadInstalledProviders();
+ }});
+
+ // insert the default provider at the start of the list
+ list.add(0, defaultProvider);
+
+ installedProviders = Collections.unmodifiableList(list);
+ }
+ }
+ }
+ return installedProviders;
+ }
+
+ /**
+ * Returns the URI scheme that identifies this provider.
+ *
+ * @return The URI scheme
+ */
+ public abstract String getScheme();
+
+ /**
+ * Constructs a new {@code FileSystem} object identified by a URI. This
+ * method is invoked by the {@link FileSystems#newFileSystem(URI,Map)}
+ * method to open a new file system identified by a URI.
+ *
+ * <p> The {@code uri} parameter is an absolute, hierarchical URI, with a
+ * scheme equal (without regard to case) to the scheme supported by this
+ * provider. The exact form of the URI is highly provider dependent. The
+ * {@code env} parameter is a map of provider specific properties to configure
+ * the file system.
+ *
+ * <p> This method throws {@link FileSystemAlreadyExistsException} if the
+ * file system already exists because it was previously created by an
+ * invocation of this method. Once a file system is {@link FileSystem#close
+ * closed} it is provider-dependent if the provider allows a new file system
+ * to be created with the same URI as a file system it previously created.
+ *
+ * @param uri
+ * URI reference
+ * @param env
+ * A map of provider specific properties to configure the file system;
+ * may be empty
+ *
+ * @return A new file system
+ *
+ * @throws IllegalArgumentException
+ * If the pre-conditions for the {@code uri} parameter aren't met,
+ * or the {@code env} parameter does not contain properties required
+ * by the provider, or a property value is invalid
+ * @throws IOException
+ * An I/O error occurs creating the file system
+ * @throws SecurityException
+ * If a security manager is installed and it denies an unspecified
+ * permission required by the file system provider implementation
+ * @throws FileSystemAlreadyExistsException
+ * If the file system has already been created
+ */
+ public abstract FileSystem newFileSystem(URI uri, Map<String,?> env)
+ throws IOException;
+
+ /**
+ * Returns an existing {@code FileSystem} created by this provider.
+ *
+ * <p> This method returns a reference to a {@code FileSystem} that was
+ * created by invoking the {@link #newFileSystem(URI,Map) newFileSystem(URI,Map)}
+ * method. File systems created the {@link #newFileSystem(FileRef,Map)
+ * newFileSystem(FileRef,Map)} method are not returned by this method.
+ * The file system is identified by its {@code URI}. Its exact form
+ * is highly provider dependent. In the case of the default provider the URI's
+ * path component is {@code "/"} and the authority, query and fragment components
+ * are undefined (Undefined components are represented by {@code null}).
+ *
+ * <p> Once a file system created by this provider is {@link FileSystem#close
+ * closed} it is provider-dependent if this method returns a reference to
+ * the closed file system or throws {@link FileSystemNotFoundException}.
+ * If the provider allows a new file system to be created with the same URI
+ * as a file system it previously created then this method throws the
+ * exception if invoked after the file system is closed (and before a new
+ * instance is created by the {@link #newFileSystem newFileSystem} method).
+ *
+ * <p> If a security manager is installed then a provider implementation
+ * may require to check a permission before returning a reference to an
+ * existing file system. In the case of the {@link FileSystems#getDefault
+ * default} file system, no permission check is required.
+ *
+ * @param uri
+ * URI reference
+ *
+ * @return The file system
+ *
+ * @throws IllegalArgumentException
+ * If the pre-conditions for the {@code uri} parameter aren't met
+ * @throws FileSystemNotFoundException
+ * If the file system does not exist
+ * @throws SecurityException
+ * If a security manager is installed and it denies an unspecified
+ * permission.
+ */
+ public abstract FileSystem getFileSystem(URI uri);
+
+ /**
+ * Return a {@code Path} object by converting the given {@link URI}.
+ *
+ * <p> The exact form of the URI is file system provider dependent. In the
+ * case of the default provider, the URI scheme is {@code "file"} and the
+ * given URI has a non-empty path component, and undefined query, and
+ * fragment components. The resulting {@code Path} is associated with the
+ * default {@link FileSystems#getDefault default} {@code FileSystem}.
+ *
+ * <p> If a security manager is installed then a provider implementation
+ * may require to check a permission. In the case of the {@link
+ * FileSystems#getDefault default} file system, no permission check is
+ * required.
+ *
+ * @param uri
+ * The URI to convert
+ *
+ * @throws IllegalArgumentException
+ * If the URI scheme does not identify this provider or other
+ * preconditions on the uri parameter do not hold
+ * @throws FileSystemNotFoundException
+ * The file system, identified by the URI, does not exist
+ * @throws SecurityException
+ * If a security manager is installed and it denies an unspecified
+ * permission.
+ */
+ public abstract Path getPath(URI uri);
+
+ /**
+ * Constructs a new {@code FileSystem} to access the contents of a file as a
+ * file system.
+ *
+ * <p> This method is intended for specialized providers of pseudo file
+ * systems where the contents of one or more files is treated as a file
+ * system. The {@code file} parameter is a reference to an existing file
+ * and the {@code env} parameter is a map of provider specific properties to
+ * configure the file system.
+ *
+ * <p> If this provider does not support the creation of such file systems
+ * or if the provider does not recognize the file type of the given file then
+ * it throws {@code UnsupportedOperationException}. The default implementation
+ * of this method throws {@code UnsupportedOperationException}.
+ *
+ * @param file
+ * The file
+ * @param env
+ * A map of provider specific properties to configure the file system;
+ * may be empty
+ *
+ * @return A new file system
+ *
+ * @throws UnsupportedOperationException
+ * If this provider does not support access to the contents as a
+ * file system or it does not recognize the file type of the
+ * given file
+ * @throws IllegalArgumentException
+ * If the {@code env} parameter does not contain properties required
+ * by the provider, or a property value is invalid
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an unspecified
+ * permission.
+ */
+ public FileSystem newFileSystem(FileRef file, Map<String,?> env)
+ throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Opens or creates a file for reading and/or writing, returning a file
+ * channel to access the file.
+ *
+ * <p> This method is invoked by the {@link FileChannel#open(Path,Set,FileAttribute[])
+ * FileChannel.open} method to open a file channel. A provider that does not
+ * support all the features required to construct a file channel throws
+ * {@code UnsupportedOperationException}. The default provider is required
+ * to support the creation of file channels. When not overridden, the
+ * default implementation throws {@code UnsupportedOperationException}.
+ *
+ * @param path
+ * The path of the file to open or create
+ * @param options
+ * Options specifying how the file is opened
+ * @param attrs
+ * An optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return A new file channel
+ *
+ * @throws IllegalArgumentException
+ * If the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * If this provider that does not support creating file channels,
+ * or an unsupported open option or file attribute is specified
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default file system, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check
+ * read access if the file is opened for reading. The {@link
+ * SecurityManager#checkWrite(String)} method is invoked to check
+ * write access if the file is opened for writing
+ */
+ public FileChannel newFileChannel(Path path,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Opens or creates a file for reading and/or writing, returning an
+ * asynchronous file channel to access the file.
+ *
+ * <p> This method is invoked by the {@link
+ * AsynchronousFileChannel#open(Path,Set,ExecutorService,FileAttribute[])
+ * AsynchronousFileChannel.open} method to open an asynchronous file channel.
+ * A provider that does not support all the features required to construct
+ * an asynchronous file channel throws {@code UnsupportedOperationException}.
+ * The default provider is required to support the creation of asynchronous
+ * file channels. When not overridden, the default implementation of this
+ * method throws {@code UnsupportedOperationException}.
+ *
+ * @param path
+ * The path of the file to open or create
+ * @param options
+ * Options specifying how the file is opened
+ * @param executor
+ * The thread pool or {@code null} to associate the channel with
+ * the default thread pool
+ * @param attrs
+ * An optional list of file attributes to set atomically when
+ * creating the file
+ *
+ * @return A new asynchronous file channel
+ *
+ * @throws IllegalArgumentException
+ * If the set contains an invalid combination of options
+ * @throws UnsupportedOperationException
+ * If this provider that does not support creating asynchronous file
+ * channels, or an unsupported open option or file attribute is
+ * specified
+ * @throws IOException
+ * If an I/O error occurs
+ * @throws SecurityException
+ * In the case of the default file system, the {@link
+ * SecurityManager#checkRead(String)} method is invoked to check
+ * read access if the file is opened for reading. The {@link
+ * SecurityManager#checkWrite(String)} method is invoked to check
+ * write access if the file is opened for writing
+ */
+ public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
+ Set<? extends OpenOption> options,
+ ExecutorService executor,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/share/classes/java/nio/file/spi/FileTypeDetector.java b/src/share/classes/java/nio/file/spi/FileTypeDetector.java
new file mode 100644
index 000000000..65e3c49c0
--- /dev/null
+++ b/src/share/classes/java/nio/file/spi/FileTypeDetector.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio.file.spi;
+
+import java.nio.file.FileRef;
+import java.io.IOException;
+
+/**
+ * A file type detector for probing a file to guess its file type.
+ *
+ * <p> A file type detector is a concrete implementation of this class, has a
+ * zero-argument constructor, and implements the abstract methods specified
+ * below.
+ *
+ * <p> The means by which a file type detector determines the file type is
+ * highly implementation specific. A simple implementation might examine the
+ * <em>file extension</em> (a convention used in some platforms) and map it to
+ * a file type. In other cases, the file type may be stored as a file <a
+ * href="../attribute/package-summary.html"> attribute</a> or the bytes in a
+ * file may be examined to guess its file type.
+ *
+ * @see java.nio.file.Files#probeContentType(FileRef)
+ *
+ * @since 1.7
+ */
+
+public abstract class FileTypeDetector {
+
+ private static Void checkPermission() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("fileTypeDetector"));
+ return null;
+ }
+ private FileTypeDetector(Void ignore) { }
+
+ /**
+ * Initializes a new instance of this class.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link RuntimePermission}<tt>("fileTypeDetector")</tt>
+ */
+ protected FileTypeDetector() {
+ this(checkPermission());
+ }
+
+ /**
+ * Probes the given file to guess its content type.
+ *
+ * <p> The means by which this method determines the file type is highly
+ * implementation specific. It may simply examine the file name, it may use
+ * a file <a href="../attribute/package-summary.html">attribute</a>,
+ * or it may examines bytes in the file.
+ *
+ * <p> The probe result is the string form of the value of a
+ * Multipurpose Internet Mail Extension (MIME) content type as
+ * defined by <a href="http://www.ietf.org/rfc/rfc2045.txt"><i>RFC&nbsp;2045:
+ * Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet
+ * Message Bodies</i></a>. The string must be parsable according to the
+ * grammar in the RFC 2045.
+ *
+ * @param file
+ * The file to probe
+ *
+ * @return The content type or {@code null} if the file type is not
+ * recognized
+ *
+ * @throws IOException
+ * An I/O error occurs
+ * @throws SecurityException
+ * If the implementation requires to access the file, and a
+ * security manager is installed, and it denies an unspecified
+ * permission required by a file system provider implementation.
+ * If the file reference is associated with the default file system
+ * provider then the {@link SecurityManager#checkRead(String)} method
+ * is invoked to check read access to the file.
+ *
+ * @see java.nio.file.Files#probeContentType
+ */
+ public abstract String probeContentType(FileRef file)
+ throws IOException;
+}
diff --git a/src/share/classes/java/nio/file/spi/package-info.java b/src/share/classes/java/nio/file/spi/package-info.java
new file mode 100644
index 000000000..88149b797
--- /dev/null
+++ b/src/share/classes/java/nio/file/spi/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * Service-provider classes for the <tt>{@link java.nio.file}</tt> package.
+ *
+ * <p> Only developers who are defining new file system providers or file type
+ * detectors should need to make direct use of this package. </p>
+ *
+ * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
+ * or method in any class or interface in this package will cause a {@link
+ * java.lang.NullPointerException NullPointerException} to be thrown.
+ *
+ * @since 1.7
+ */
+
+package java.nio.file.spi;
diff --git a/src/share/classes/java/util/Calendar.java b/src/share/classes/java/util/Calendar.java
index e1f65e6a8..7fa829643 100644
--- a/src/share/classes/java/util/Calendar.java
+++ b/src/share/classes/java/util/Calendar.java
@@ -1190,7 +1190,9 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
*/
public void set(int field, int value)
{
- if (isLenient() && areFieldsSet && !areAllFieldsSet) {
+ // If the fields are partially normalized, calculate all the
+ // fields before changing any fields.
+ if (areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
diff --git a/src/share/classes/java/util/Collection.java b/src/share/classes/java/util/Collection.java
index 28c923ff2..e7cb3c91e 100644
--- a/src/share/classes/java/util/Collection.java
+++ b/src/share/classes/java/util/Collection.java
@@ -427,7 +427,7 @@ public interface Collection<E> extends Iterable<E> {
* contract for the <tt>Object.hashCode</tt> method, programmers should
* take note that any class that overrides the <tt>Object.equals</tt>
* method must also override the <tt>Object.hashCode</tt> method in order
- * to satisfy the general contract for the <tt>Object.hashCode</tt>method.
+ * to satisfy the general contract for the <tt>Object.hashCode</tt> method.
* In particular, <tt>c1.equals(c2)</tt> implies that
* <tt>c1.hashCode()==c2.hashCode()</tt>.
*
diff --git a/src/share/classes/java/util/CurrencyData.properties b/src/share/classes/java/util/CurrencyData.properties
index 15c45302e..cbb96d8ab 100644
--- a/src/share/classes/java/util/CurrencyData.properties
+++ b/src/share/classes/java/util/CurrencyData.properties
@@ -441,12 +441,16 @@ RO=ROL;2005-06-30-21-00-00;RON
RU=RUB
# RWANDA
RW=RWF
+# SAINT BARTHELEMY
+BL=EUR
# SAINT HELENA
SH=SHP
# SAINT KITTS AND NEVIS
KN=XCD
# SAINT LUCIA
LC=XCD
+# SAINT MARTIN
+MF=EUR
# SAINT PIERRE AND MIQUELON
PM=EUR
# SAINT VINCENT AND THE GRENADINES
diff --git a/src/share/classes/java/util/Formatter.java b/src/share/classes/java/util/Formatter.java
index ad4dca1d3..eb49ec9c5 100644
--- a/src/share/classes/java/util/Formatter.java
+++ b/src/share/classes/java/util/Formatter.java
@@ -39,6 +39,7 @@ import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
+import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
@@ -58,7 +59,7 @@ import sun.misc.FormattedFloatingDecimal;
* An interpreter for printf-style format strings. This class provides support
* for layout justification and alignment, common formats for numeric, string,
* and date/time data, and locale-specific output. Common Java types such as
- * <tt>byte</tt>, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}
+ * {@code byte}, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}
* are supported. Limited formatting customization for arbitrary user types is
* provided through the {@link Formattable} interface.
*
@@ -67,7 +68,7 @@ import sun.misc.FormattedFloatingDecimal;
* class.
*
* <p> Formatted printing for the Java language is heavily inspired by C's
- * <tt>printf</tt>. Although the format strings are similar to C, some
+ * {@code printf}. Although the format strings are similar to C, some
* customizations have been made to accommodate the Java language and exploit
* some of its features. Also, Java formatting is more strict than C's; for
* example, if a conversion is incompatible with a flag, an exception will be
@@ -114,7 +115,7 @@ import sun.misc.FormattedFloatingDecimal;
* // -&gt; "Unable to open file 'food': No such file or directory"
* </pre></blockquote>
*
- * <p> Like C's <tt>sprintf(3)</tt>, Strings may be formatted using the static
+ * <p> Like C's {@code sprintf(3)}, Strings may be formatted using the static
* method {@link String#format(String,Object...) String.format}:
*
* <blockquote><pre>
@@ -156,16 +157,16 @@ import sun.misc.FormattedFloatingDecimal;
* String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c);
* </pre></blockquote>
*
- * This format string is the first argument to the <tt>format</tt> method. It
- * contains three format specifiers "<tt>%1$tm</tt>", "<tt>%1$te</tt>", and
- * "<tt>%1$tY</tt>" which indicate how the arguments should be processed and
+ * This format string is the first argument to the {@code format} method. It
+ * contains three format specifiers "{@code %1$tm}", "{@code %1$te}", and
+ * "{@code %1$tY}" which indicate how the arguments should be processed and
* where they should be inserted in the text. The remaining portions of the
- * format string are fixed text including <tt>"Dukes Birthday: "</tt> and any
+ * format string are fixed text including {@code "Dukes Birthday: "} and any
* other spaces or punctuation.
*
* The argument list consists of all arguments passed to the method after the
* format string. In the above example, the argument list is of size one and
- * consists of the {@link java.util.Calendar Calendar} object <tt>c</tt>.
+ * consists of the {@link java.util.Calendar Calendar} object {@code c}.
*
* <ul>
*
@@ -178,7 +179,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The optional <i>argument_index</i> is a decimal integer indicating the
* position of the argument in the argument list. The first argument is
- * referenced by "<tt>1$</tt>", the second by "<tt>2$</tt>", etc.
+ * referenced by "{@code 1$}", the second by "{@code 2$}", etc.
*
* <p> The optional <i>flags</i> is a set of characters that modify the output
* format. The set of valid flags depends on the conversion.
@@ -205,10 +206,10 @@ import sun.misc.FormattedFloatingDecimal;
* defined as above.
*
* <p> The required <i>conversion</i> is a two character sequence. The first
- * character is <tt>'t'</tt> or <tt>'T'</tt>. The second character indicates
+ * character is {@code 't'} or {@code 'T'}. The second character indicates
* the format to be used. These characters are similar to but not completely
- * identical to those defined by GNU <tt>date</tt> and POSIX
- * <tt>strftime(3c)</tt>.
+ * identical to those defined by GNU {@code date} and POSIX
+ * {@code strftime(3c)}.
*
* <li> The format specifiers which do not correspond to arguments have the
* following syntax:
@@ -234,31 +235,31 @@ import sun.misc.FormattedFloatingDecimal;
* type
*
* <li> <b>Character</b> - may be applied to basic types which represent
- * Unicode characters: <tt>char</tt>, {@link Character}, <tt>byte</tt>, {@link
- * Byte}, <tt>short</tt>, and {@link Short}. This conversion may also be
- * applied to the types <tt>int</tt> and {@link Integer} when {@link
- * Character#isValidCodePoint} returns <tt>true</tt>
+ * Unicode characters: {@code char}, {@link Character}, {@code byte}, {@link
+ * Byte}, {@code short}, and {@link Short}. This conversion may also be
+ * applied to the types {@code int} and {@link Integer} when {@link
+ * Character#isValidCodePoint} returns {@code true}
*
* <li> <b>Numeric</b>
*
* <ol>
*
- * <li> <b>Integral</b> - may be applied to Java integral types: <tt>byte</tt>,
- * {@link Byte}, <tt>short</tt>, {@link Short}, <tt>int</tt> and {@link
- * Integer}, <tt>long</tt>, {@link Long}, and {@link java.math.BigInteger
+ * <li> <b>Integral</b> - may be applied to Java integral types: {@code byte},
+ * {@link Byte}, {@code short}, {@link Short}, {@code int} and {@link
+ * Integer}, {@code long}, {@link Long}, and {@link java.math.BigInteger
* BigInteger}
*
* <li><b>Floating Point</b> - may be applied to Java floating-point types:
- * <tt>float</tt>, {@link Float}, <tt>double</tt>, {@link Double}, and {@link
+ * {@code float}, {@link Float}, {@code double}, {@link Double}, and {@link
* java.math.BigDecimal BigDecimal}
*
* </ol>
*
* <li> <b>Date/Time</b> - may be applied to Java types which are capable of
- * encoding a date or time: <tt>long</tt>, {@link Long}, {@link Calendar}, and
+ * encoding a date or time: {@code long}, {@link Long}, {@link Calendar}, and
* {@link Date}.
*
- * <li> <b>Percent</b> - produces a literal <tt>'%'</tt>
+ * <li> <b>Percent</b> - produces a literal {@code '%'}
* (<tt>'&#92;u0025'</tt>)
*
* <li> <b>Line Separator</b> - produces the platform-specific line separator
@@ -266,9 +267,9 @@ import sun.misc.FormattedFloatingDecimal;
* </ol>
*
* <p> The following table summarizes the supported conversions. Conversions
- * denoted by an upper-case character (i.e. <tt>'B'</tt>, <tt>'H'</tt>,
- * <tt>'S'</tt>, <tt>'C'</tt>, <tt>'X'</tt>, <tt>'E'</tt>, <tt>'G'</tt>,
- * <tt>'A'</tt>, and <tt>'T'</tt>) are the same as those for the corresponding
+ * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
+ * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
+ * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
* lower-case conversion characters except that the result is converted to
* upper case according to the rules of the prevailing {@link java.util.Locale
* Locale}. The result is equivalent to the following invocation of {@link
@@ -283,72 +284,72 @@ import sun.misc.FormattedFloatingDecimal;
* <th valign="bottom"> Argument Category
* <th valign="bottom"> Description
*
- * <tr><td valign="top"> <tt>'b'</tt>, <tt>'B'</tt>
+ * <tr><td valign="top"> {@code 'b'}, {@code 'B'}
* <td valign="top"> general
- * <td> If the argument <i>arg</i> is <tt>null</tt>, then the result is
- * "<tt>false</tt>". If <i>arg</i> is a <tt>boolean</tt> or {@link
+ * <td> If the argument <i>arg</i> is {@code null}, then the result is
+ * "{@code false}". If <i>arg</i> is a {@code boolean} or {@link
* Boolean}, then the result is the string returned by {@link
* String#valueOf(boolean) String.valueOf(arg)}. Otherwise, the result is
* "true".
*
- * <tr><td valign="top"> <tt>'h'</tt>, <tt>'H'</tt>
+ * <tr><td valign="top"> {@code 'h'}, {@code 'H'}
* <td valign="top"> general
- * <td> If the argument <i>arg</i> is <tt>null</tt>, then the result is
- * "<tt>null</tt>". Otherwise, the result is obtained by invoking
- * <tt>Integer.toHexString(arg.hashCode())</tt>.
+ * <td> If the argument <i>arg</i> is {@code null}, then the result is
+ * "{@code null}". Otherwise, the result is obtained by invoking
+ * {@code Integer.toHexString(arg.hashCode())}.
*
- * <tr><td valign="top"> <tt>'s'</tt>, <tt>'S'</tt>
+ * <tr><td valign="top"> {@code 's'}, {@code 'S'}
* <td valign="top"> general
- * <td> If the argument <i>arg</i> is <tt>null</tt>, then the result is
- * "<tt>null</tt>". If <i>arg</i> implements {@link Formattable}, then
+ * <td> If the argument <i>arg</i> is {@code null}, then the result is
+ * "{@code null}". If <i>arg</i> implements {@link Formattable}, then
* {@link Formattable#formatTo arg.formatTo} is invoked. Otherwise, the
- * result is obtained by invoking <tt>arg.toString()</tt>.
+ * result is obtained by invoking {@code arg.toString()}.
*
- * <tr><td valign="top"><tt>'c'</tt>, <tt>'C'</tt>
+ * <tr><td valign="top">{@code 'c'}, {@code 'C'}
* <td valign="top"> character
* <td> The result is a Unicode character
*
- * <tr><td valign="top"><tt>'d'</tt>
+ * <tr><td valign="top">{@code 'd'}
* <td valign="top"> integral
* <td> The result is formatted as a decimal integer
*
- * <tr><td valign="top"><tt>'o'</tt>
+ * <tr><td valign="top">{@code 'o'}
* <td valign="top"> integral
* <td> The result is formatted as an octal integer
*
- * <tr><td valign="top"><tt>'x'</tt>, <tt>'X'</tt>
+ * <tr><td valign="top">{@code 'x'}, {@code 'X'}
* <td valign="top"> integral
* <td> The result is formatted as a hexadecimal integer
*
- * <tr><td valign="top"><tt>'e'</tt>, <tt>'E'</tt>
+ * <tr><td valign="top">{@code 'e'}, {@code 'E'}
* <td valign="top"> floating point
* <td> The result is formatted as a decimal number in computerized
* scientific notation
*
- * <tr><td valign="top"><tt>'f'</tt>
+ * <tr><td valign="top">{@code 'f'}
* <td valign="top"> floating point
* <td> The result is formatted as a decimal number
*
- * <tr><td valign="top"><tt>'g'</tt>, <tt>'G'</tt>
+ * <tr><td valign="top">{@code 'g'}, {@code 'G'}
* <td valign="top"> floating point
* <td> The result is formatted using computerized scientific notation or
* decimal format, depending on the precision and the value after rounding.
*
- * <tr><td valign="top"><tt>'a'</tt>, <tt>'A'</tt>
+ * <tr><td valign="top">{@code 'a'}, {@code 'A'}
* <td valign="top"> floating point
* <td> The result is formatted as a hexadecimal floating-point number with
* a significand and an exponent
*
- * <tr><td valign="top"><tt>'t'</tt>, <tt>'T'</tt>
+ * <tr><td valign="top">{@code 't'}, {@code 'T'}
* <td valign="top"> date/time
* <td> Prefix for date and time conversion characters. See <a
* href="#dt">Date/Time Conversions</a>.
*
- * <tr><td valign="top"><tt>'%'</tt>
+ * <tr><td valign="top">{@code '%'}
* <td valign="top"> percent
- * <td> The result is a literal <tt>'%'</tt> (<tt>'&#92;u0025'</tt>)
+ * <td> The result is a literal {@code '%'} (<tt>'&#92;u0025'</tt>)
*
- * <tr><td valign="top"><tt>'n'</tt>
+ * <tr><td valign="top">{@code 'n'}
* <td valign="top"> line separator
* <td> The result is the platform-specific line separator
*
@@ -360,78 +361,78 @@ import sun.misc.FormattedFloatingDecimal;
* <h4><a name="dt">Date/Time Conversions</a></h4>
*
* <p> The following date and time conversion suffix characters are defined for
- * the <tt>'t'</tt> and <tt>'T'</tt> conversions. The types are similar to but
- * not completely identical to those defined by GNU <tt>date</tt> and POSIX
- * <tt>strftime(3c)</tt>. Additional conversion types are provided to access
- * Java-specific functionality (e.g. <tt>'L'</tt> for milliseconds within the
+ * the {@code 't'} and {@code 'T'} conversions. The types are similar to but
+ * not completely identical to those defined by GNU {@code date} and POSIX
+ * {@code strftime(3c)}. Additional conversion types are provided to access
+ * Java-specific functionality (e.g. {@code 'L'} for milliseconds within the
* second).
*
* <p> The following conversion characters are used for formatting times:
*
* <table cellpadding=5 summary="time">
*
- * <tr><td valign="top"> <tt>'H'</tt>
+ * <tr><td valign="top"> {@code 'H'}
* <td> Hour of the day for the 24-hour clock, formatted as two digits with
- * a leading zero as necessary i.e. <tt>00 - 23</tt>.
+ * a leading zero as necessary i.e. {@code 00 - 23}.
*
- * <tr><td valign="top"><tt>'I'</tt>
+ * <tr><td valign="top">{@code 'I'}
* <td> Hour for the 12-hour clock, formatted as two digits with a leading
- * zero as necessary, i.e. <tt>01 - 12</tt>.
+ * zero as necessary, i.e. {@code 01 - 12}.
*
- * <tr><td valign="top"><tt>'k'</tt>
- * <td> Hour of the day for the 24-hour clock, i.e. <tt>0 - 23</tt>.
+ * <tr><td valign="top">{@code 'k'}
+ * <td> Hour of the day for the 24-hour clock, i.e. {@code 0 - 23}.
*
- * <tr><td valign="top"><tt>'l'</tt>
- * <td> Hour for the 12-hour clock, i.e. <tt>1 - 12</tt>.
+ * <tr><td valign="top">{@code 'l'}
+ * <td> Hour for the 12-hour clock, i.e. {@code 1 - 12}.
*
- * <tr><td valign="top"><tt>'M'</tt>
+ * <tr><td valign="top">{@code 'M'}
* <td> Minute within the hour formatted as two digits with a leading zero
- * as necessary, i.e. <tt>00 - 59</tt>.
+ * as necessary, i.e. {@code 00 - 59}.
*
- * <tr><td valign="top"><tt>'S'</tt>
+ * <tr><td valign="top">{@code 'S'}
* <td> Seconds within the minute, formatted as two digits with a leading
- * zero as necessary, i.e. <tt>00 - 60</tt> ("<tt>60</tt>" is a special
+ * zero as necessary, i.e. {@code 00 - 60} ("{@code 60}" is a special
* value required to support leap seconds).
*
- * <tr><td valign="top"><tt>'L'</tt>
+ * <tr><td valign="top">{@code 'L'}
* <td> Millisecond within the second formatted as three digits with
- * leading zeros as necessary, i.e. <tt>000 - 999</tt>.
+ * leading zeros as necessary, i.e. {@code 000 - 999}.
*
- * <tr><td valign="top"><tt>'N'</tt>
+ * <tr><td valign="top">{@code 'N'}
* <td> Nanosecond within the second, formatted as nine digits with leading
- * zeros as necessary, i.e. <tt>000000000 - 999999999</tt>.
+ * zeros as necessary, i.e. {@code 000000000 - 999999999}.
*
- * <tr><td valign="top"><tt>'p'</tt>
+ * <tr><td valign="top">{@code 'p'}
* <td> Locale-specific {@linkplain
* java.text.DateFormatSymbols#getAmPmStrings morning or afternoon} marker
- * in lower case, e.g."<tt>am</tt>" or "<tt>pm</tt>". Use of the conversion
- * prefix <tt>'T'</tt> forces this output to upper case.
+ * in lower case, e.g."{@code am}" or "{@code pm}". Use of the conversion
+ * prefix {@code 'T'} forces this output to upper case.
*
- * <tr><td valign="top"><tt>'z'</tt>
+ * <tr><td valign="top">{@code 'z'}
* <td> <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC&nbsp;822</a>
- * style numeric time zone offset from GMT, e.g. <tt>-0800</tt>. This
+ * style numeric time zone offset from GMT, e.g. {@code -0800}. This
* value will be adjusted as necessary for Daylight Saving Time. For
- * <tt>long</tt>, {@link Long}, and {@link Date} the time zone used is
- * the {@plainlink TimeZone#getDefault() default time zone} for this
+ * {@code long}, {@link Long}, and {@link Date} the time zone used is
+ * the {@linkplain TimeZone#getDefault() default time zone} for this
* instance of the Java virtual machine.
*
- * <tr><td valign="top"><tt>'Z'</tt>
+ * <tr><td valign="top">{@code 'Z'}
* <td> A string representing the abbreviation for the time zone. This
* value will be adjusted as necessary for Daylight Saving Time. For
- * <tt>long</tt>, {@link Long}, and {@link Date} the time zone used is
- * the {@plainlink TimeZone#getDefault() default time zone} for this
+ * {@code long}, {@link Long}, and {@link Date} the time zone used is
+ * the {@linkplain TimeZone#getDefault() default time zone} for this
* instance of the Java virtual machine. The Formatter's locale will
* supersede the locale of the argument (if any).
*
- * <tr><td valign="top"><tt>'s'</tt>
+ * <tr><td valign="top">{@code 's'}
* <td> Seconds since the beginning of the epoch starting at 1 January 1970
- * <tt>00:00:00</tt> UTC, i.e. <tt>Long.MIN_VALUE/1000</tt> to
- * <tt>Long.MAX_VALUE/1000</tt>.
+ * {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE/1000} to
+ * {@code Long.MAX_VALUE/1000}.
*
- * <tr><td valign="top"><tt>'Q'</tt>
+ * <tr><td valign="top">{@code 'Q'}
* <td> Milliseconds since the beginning of the epoch starting at 1 January
- * 1970 <tt>00:00:00</tt> UTC, i.e. <tt>Long.MIN_VALUE</tt> to
- * <tt>Long.MAX_VALUE</tt>.
+ * 1970 {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE} to
+ * {@code Long.MAX_VALUE}.
*
* </table>
*
@@ -439,55 +440,55 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="date">
*
- * <tr><td valign="top"><tt>'B'</tt>
+ * <tr><td valign="top">{@code 'B'}
* <td> Locale-specific {@linkplain java.text.DateFormatSymbols#getMonths
- * full month name}, e.g. <tt>"January"</tt>, <tt>"February"</tt>.
+ * full month name}, e.g. {@code "January"}, {@code "February"}.
*
- * <tr><td valign="top"><tt>'b'</tt>
+ * <tr><td valign="top">{@code 'b'}
* <td> Locale-specific {@linkplain
* java.text.DateFormatSymbols#getShortMonths abbreviated month name},
- * e.g. <tt>"Jan"</tt>, <tt>"Feb"</tt>.
+ * e.g. {@code "Jan"}, {@code "Feb"}.
*
- * <tr><td valign="top"><tt>'h'</tt>
- * <td> Same as <tt>'b'</tt>.
+ * <tr><td valign="top">{@code 'h'}
+ * <td> Same as {@code 'b'}.
*
- * <tr><td valign="top"><tt>'A'</tt>
+ * <tr><td valign="top">{@code 'A'}
* <td> Locale-specific full name of the {@linkplain
* java.text.DateFormatSymbols#getWeekdays day of the week},
- * e.g. <tt>"Sunday"</tt>, <tt>"Monday"</tt>
+ * e.g. {@code "Sunday"}, {@code "Monday"}
*
- * <tr><td valign="top"><tt>'a'</tt>
+ * <tr><td valign="top">{@code 'a'}
* <td> Locale-specific short name of the {@linkplain
* java.text.DateFormatSymbols#getShortWeekdays day of the week},
- * e.g. <tt>"Sun"</tt>, <tt>"Mon"</tt>
+ * e.g. {@code "Sun"}, {@code "Mon"}
*
- * <tr><td valign="top"><tt>'C'</tt>
- * <td> Four-digit year divided by <tt>100</tt>, formatted as two digits
- * with leading zero as necessary, i.e. <tt>00 - 99</tt>
+ * <tr><td valign="top">{@code 'C'}
+ * <td> Four-digit year divided by {@code 100}, formatted as two digits
+ * with leading zero as necessary, i.e. {@code 00 - 99}
*
- * <tr><td valign="top"><tt>'Y'</tt>
+ * <tr><td valign="top">{@code 'Y'}
* <td> Year, formatted as at least four digits with leading zeros as
- * necessary, e.g. <tt>0092</tt> equals <tt>92</tt> CE for the Gregorian
+ * necessary, e.g. {@code 0092} equals {@code 92} CE for the Gregorian
* calendar.
*
- * <tr><td valign="top"><tt>'y'</tt>
+ * <tr><td valign="top">{@code 'y'}
* <td> Last two digits of the year, formatted with leading zeros as
- * necessary, i.e. <tt>00 - 99</tt>.
+ * necessary, i.e. {@code 00 - 99}.
*
- * <tr><td valign="top"><tt>'j'</tt>
+ * <tr><td valign="top">{@code 'j'}
* <td> Day of year, formatted as three digits with leading zeros as
- * necessary, e.g. <tt>001 - 366</tt> for the Gregorian calendar.
+ * necessary, e.g. {@code 001 - 366} for the Gregorian calendar.
*
- * <tr><td valign="top"><tt>'m'</tt>
+ * <tr><td valign="top">{@code 'm'}
* <td> Month, formatted as two digits with leading zeros as necessary,
- * i.e. <tt>01 - 13</tt>.
+ * i.e. {@code 01 - 13}.
*
- * <tr><td valign="top"><tt>'d'</tt>
+ * <tr><td valign="top">{@code 'd'}
* <td> Day of month, formatted as two digits with leading zeros as
- * necessary, i.e. <tt>01 - 31</tt>
+ * necessary, i.e. {@code 01 - 31}
*
- * <tr><td valign="top"><tt>'e'</tt>
- * <td> Day of month, formatted as two digits, i.e. <tt>1 - 31</tt>.
+ * <tr><td valign="top">{@code 'e'}
+ * <td> Day of month, formatted as two digits, i.e. {@code 1 - 31}.
*
* </table>
*
@@ -496,27 +497,27 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="composites">
*
- * <tr><td valign="top"><tt>'R'</tt>
- * <td> Time formatted for the 24-hour clock as <tt>"%tH:%tM"</tt>
+ * <tr><td valign="top">{@code 'R'}
+ * <td> Time formatted for the 24-hour clock as {@code "%tH:%tM"}
*
- * <tr><td valign="top"><tt>'T'</tt>
- * <td> Time formatted for the 24-hour clock as <tt>"%tH:%tM:%tS"</tt>.
+ * <tr><td valign="top">{@code 'T'}
+ * <td> Time formatted for the 24-hour clock as {@code "%tH:%tM:%tS"}.
*
- * <tr><td valign="top"><tt>'r'</tt>
- * <td> Time formatted for the 12-hour clock as <tt>"%tI:%tM:%tS %Tp"</tt>.
- * The location of the morning or afternoon marker (<tt>'%Tp'</tt>) may be
+ * <tr><td valign="top">{@code 'r'}
+ * <td> Time formatted for the 12-hour clock as {@code "%tI:%tM:%tS %Tp"}.
+ * The location of the morning or afternoon marker ({@code '%Tp'}) may be
* locale-dependent.
*
- * <tr><td valign="top"><tt>'D'</tt>
- * <td> Date formatted as <tt>"%tm/%td/%ty"</tt>.
+ * <tr><td valign="top">{@code 'D'}
+ * <td> Date formatted as {@code "%tm/%td/%ty"}.
*
- * <tr><td valign="top"><tt>'F'</tt>
+ * <tr><td valign="top">{@code 'F'}
* <td> <a href="http://www.w3.org/TR/NOTE-datetime">ISO&nbsp;8601</a>
- * complete date formatted as <tt>"%tY-%tm-%td"</tt>.
+ * complete date formatted as {@code "%tY-%tm-%td"}.
*
- * <tr><td valign="top"><tt>'c'</tt>
- * <td> Date and time formatted as <tt>"%ta %tb %td %tT %tZ %tY"</tt>,
- * e.g. <tt>"Sun Jul 20 16:17:00 EDT 1969"</tt>.
+ * <tr><td valign="top">{@code 'c'}
+ * <td> Date and time formatted as {@code "%ta %tb %td %tT %tZ %tY"},
+ * e.g. {@code "Sun Jul 20 16:17:00 EDT 1969"}.
*
* </table>
*
@@ -590,18 +591,18 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> <sup>1</sup> Depends on the definition of {@link Formattable}.
*
- * <p> <sup>2</sup> For <tt>'d'</tt> conversion only.
+ * <p> <sup>2</sup> For {@code 'd'} conversion only.
*
- * <p> <sup>3</sup> For <tt>'o'</tt>, <tt>'x'</tt>, and <tt>'X'</tt>
+ * <p> <sup>3</sup> For {@code 'o'}, {@code 'x'}, and {@code 'X'}
* conversions only.
*
- * <p> <sup>4</sup> For <tt>'d'</tt>, <tt>'o'</tt>, <tt>'x'</tt>, and
- * <tt>'X'</tt> conversions applied to {@link java.math.BigInteger BigInteger}
- * or <tt>'d'</tt> applied to <tt>byte</tt>, {@link Byte}, <tt>short</tt>, {@link
- * Short}, <tt>int</tt> and {@link Integer}, <tt>long</tt>, and {@link Long}.
+ * <p> <sup>4</sup> For {@code 'd'}, {@code 'o'}, {@code 'x'}, and
+ * {@code 'X'} conversions applied to {@link java.math.BigInteger BigInteger}
+ * or {@code 'd'} applied to {@code byte}, {@link Byte}, {@code short}, {@link
+ * Short}, {@code int} and {@link Integer}, {@code long}, and {@link Long}.
*
- * <p> <sup>5</sup> For <tt>'e'</tt>, <tt>'E'</tt>, <tt>'f'</tt>,
- * <tt>'g'</tt>, and <tt>'G'</tt> conversions only.
+ * <p> <sup>5</sup> For {@code 'e'}, {@code 'E'}, {@code 'f'},
+ * {@code 'g'}, and {@code 'G'} conversions only.
*
* <p> Any characters not explicitly defined as flags are illegal and are
* reserved for future extensions.
@@ -617,11 +618,11 @@ import sun.misc.FormattedFloatingDecimal;
* <p> For general argument types, the precision is the maximum number of
* characters to be written to the output.
*
- * <p> For the floating-point conversions <tt>'e'</tt>, <tt>'E'</tt>, and
- * <tt>'f'</tt> the precision is the number of digits after the decimal
- * separator. If the conversion is <tt>'g'</tt> or <tt>'G'</tt>, then the
+ * <p> For the floating-point conversions {@code 'e'}, {@code 'E'}, and
+ * {@code 'f'} the precision is the number of digits after the decimal
+ * separator. If the conversion is {@code 'g'} or {@code 'G'}, then the
* precision is the total number of digits in the resulting magnitude after
- * rounding. If the conversion is <tt>'a'</tt> or <tt>'A'</tt>, then the
+ * rounding. If the conversion is {@code 'a'} or {@code 'A'}, then the
* precision must not be specified.
*
* <p> For character, integral, and date/time argument types and the percent
@@ -632,10 +633,10 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The argument index is a decimal integer indicating the position of the
* argument in the argument list. The first argument is referenced by
- * "<tt>1$</tt>", the second by "<tt>2$</tt>", etc.
+ * "{@code 1$}", the second by "{@code 2$}", etc.
*
* <p> Another way to reference arguments by position is to use the
- * <tt>'&lt;'</tt> (<tt>'&#92;u003c'</tt>) flag, which causes the argument for
+ * {@code '<'} (<tt>'&#92;u003c'</tt>) flag, which causes the argument for
* the previous format specifier to be re-used. For example, the following two
* statements would produce identical strings:
*
@@ -669,14 +670,14 @@ import sun.misc.FormattedFloatingDecimal;
* applicable to the corresponding argument, then an {@link
* IllegalFormatConversionException} will be thrown.
*
- * <p> All specified exceptions may be thrown by any of the <tt>format</tt>
- * methods of <tt>Formatter</tt> as well as by any <tt>format</tt> convenience
+ * <p> All specified exceptions may be thrown by any of the {@code format}
+ * methods of {@code Formatter} as well as by any {@code format} convenience
* methods such as {@link String#format(String,Object...) String.format} and
* {@link java.io.PrintStream#printf(String,Object...) PrintStream.printf}.
*
- * <p> Conversions denoted by an upper-case character (i.e. <tt>'B'</tt>,
- * <tt>'H'</tt>, <tt>'S'</tt>, <tt>'C'</tt>, <tt>'X'</tt>, <tt>'E'</tt>,
- * <tt>'G'</tt>, <tt>'A'</tt>, and <tt>'T'</tt>) are the same as those for the
+ * <p> Conversions denoted by an upper-case character (i.e. {@code 'B'},
+ * {@code 'H'}, {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'},
+ * {@code 'G'}, {@code 'A'}, and {@code 'T'}) are the same as those for the
* corresponding lower-case conversion characters except that the result is
* converted to upper case according to the rules of the prevailing {@link
* java.util.Locale Locale}. The result is equivalent to the following
@@ -691,56 +692,56 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="dgConv">
*
- * <tr><td valign="top"> <tt>'b'</tt>
+ * <tr><td valign="top"> {@code 'b'}
* <td valign="top"> <tt>'&#92;u0062'</tt>
- * <td> Produces either "<tt>true</tt>" or "<tt>false</tt>" as returned by
+ * <td> Produces either "{@code true}" or "{@code false}" as returned by
* {@link Boolean#toString(boolean)}.
*
- * <p> If the argument is <tt>null</tt>, then the result is
- * "<tt>false</tt>". If the argument is a <tt>boolean</tt> or {@link
+ * <p> If the argument is {@code null}, then the result is
+ * "{@code false}". If the argument is a {@code boolean} or {@link
* Boolean}, then the result is the string returned by {@link
* String#valueOf(boolean) String.valueOf()}. Otherwise, the result is
- * "<tt>true</tt>".
+ * "{@code true}".
*
- * <p> If the <tt>'#'</tt> flag is given, then a {@link
+ * <p> If the {@code '#'} flag is given, then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'B'</tt>
+ * <tr><td valign="top"> {@code 'B'}
* <td valign="top"> <tt>'&#92;u0042'</tt>
- * <td> The upper-case variant of <tt>'b'</tt>.
+ * <td> The upper-case variant of {@code 'b'}.
*
- * <tr><td valign="top"> <tt>'h'</tt>
+ * <tr><td valign="top"> {@code 'h'}
* <td valign="top"> <tt>'&#92;u0068'</tt>
* <td> Produces a string representing the hash code value of the object.
*
- * <p> If the argument, <i>arg</i> is <tt>null</tt>, then the
- * result is "<tt>null</tt>". Otherwise, the result is obtained
- * by invoking <tt>Integer.toHexString(arg.hashCode())</tt>.
+ * <p> If the argument, <i>arg</i> is {@code null}, then the
+ * result is "{@code null}". Otherwise, the result is obtained
+ * by invoking {@code Integer.toHexString(arg.hashCode())}.
*
- * <p> If the <tt>'#'</tt> flag is given, then a {@link
+ * <p> If the {@code '#'} flag is given, then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'H'</tt>
+ * <tr><td valign="top"> {@code 'H'}
* <td valign="top"> <tt>'&#92;u0048'</tt>
- * <td> The upper-case variant of <tt>'h'</tt>.
+ * <td> The upper-case variant of {@code 'h'}.
*
- * <tr><td valign="top"> <tt>'s'</tt>
+ * <tr><td valign="top"> {@code 's'}
* <td valign="top"> <tt>'&#92;u0073'</tt>
* <td> Produces a string.
*
- * <p> If the argument is <tt>null</tt>, then the result is
- * "<tt>null</tt>". If the argument implements {@link Formattable}, then
+ * <p> If the argument is {@code null}, then the result is
+ * "{@code null}". If the argument implements {@link Formattable}, then
* its {@link Formattable#formatTo formatTo} method is invoked.
* Otherwise, the result is obtained by invoking the argument's
- * <tt>toString()</tt> method.
+ * {@code toString()} method.
*
- * <p> If the <tt>'#'</tt> flag is given and the argument is not a {@link
+ * <p> If the {@code '#'} flag is given and the argument is not a {@link
* Formattable} , then a {@link FormatFlagsConversionMismatchException}
* will be thrown.
*
- * <tr><td valign="top"> <tt>'S'</tt>
+ * <tr><td valign="top"> {@code 'S'}
* <td valign="top"> <tt>'&#92;u0053'</tt>
- * <td> The upper-case variant of <tt>'s'</tt>.
+ * <td> The upper-case variant of {@code 's'}.
*
* </table>
*
@@ -748,7 +749,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="dFlags">
*
- * <tr><td valign="top"> <tt>'-'</tt>
+ * <tr><td valign="top"> {@code '-'}
* <td valign="top"> <tt>'&#92;u002d'</tt>
* <td> Left justifies the output. Spaces (<tt>'&#92;u0020'</tt>) will be
* added at the end of the converted value as required to fill the minimum
@@ -756,7 +757,7 @@ import sun.misc.FormattedFloatingDecimal;
* MissingFormatWidthException} will be thrown. If this flag is not given
* then the output will be right-justified.
*
- * <tr><td valign="top"> <tt>'#'</tt>
+ * <tr><td valign="top"> {@code '#'}
* <td valign="top"> <tt>'&#92;u0023'</tt>
* <td> Requires the output use an alternate form. The definition of the
* form is specified by the conversion.
@@ -766,47 +767,47 @@ import sun.misc.FormattedFloatingDecimal;
* <p> The <a name="genWidth">width</a> is the minimum number of characters to
* be written to the
* output. If the length of the converted value is less than the width then
- * the output will be padded by <tt>'&nbsp;&nbsp;'</tt> (<tt>&#92;u0020'</tt>)
+ * the output will be padded by <tt>'&nbsp;&nbsp;'</tt> (<tt>'&#92;u0020'</tt>)
* until the total number of characters equals the width. The padding is on
- * the left by default. If the <tt>'-'</tt> flag is given, then the padding
+ * the left by default. If the {@code '-'} flag is given, then the padding
* will be on the right. If the width is not specified then there is no
* minimum.
*
* <p> The precision is the maximum number of characters to be written to the
* output. The precision is applied before the width, thus the output will be
- * truncated to <tt>precision</tt> characters even if the width is greater than
+ * truncated to {@code precision} characters even if the width is greater than
* the precision. If the precision is not specified then there is no explicit
* limit on the number of characters.
*
* <h4><a name="dchar">Character</a></h4>
*
- * This conversion may be applied to <tt>char</tt> and {@link Character}. It
- * may also be applied to the types <tt>byte</tt>, {@link Byte},
- * <tt>short</tt>, and {@link Short}, <tt>int</tt> and {@link Integer} when
- * {@link Character#isValidCodePoint} returns <tt>true</tt>. If it returns
- * <tt>false</tt> then an {@link IllegalFormatCodePointException} will be
+ * This conversion may be applied to {@code char} and {@link Character}. It
+ * may also be applied to the types {@code byte}, {@link Byte},
+ * {@code short}, and {@link Short}, {@code int} and {@link Integer} when
+ * {@link Character#isValidCodePoint} returns {@code true}. If it returns
+ * {@code false} then an {@link IllegalFormatCodePointException} will be
* thrown.
*
* <table cellpadding=5 summary="charConv">
*
- * <tr><td valign="top"> <tt>'c'</tt>
+ * <tr><td valign="top"> {@code 'c'}
* <td valign="top"> <tt>'&#92;u0063'</tt>
* <td> Formats the argument as a Unicode character as described in <a
* href="../lang/Character.html#unicode">Unicode Character
- * Representation</a>. This may be more than one 16-bit <tt>char</tt> in
+ * Representation</a>. This may be more than one 16-bit {@code char} in
* the case where the argument represents a supplementary character.
*
- * <p> If the <tt>'#'</tt> flag is given, then a {@link
+ * <p> If the {@code '#'} flag is given, then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'C'</tt>
+ * <tr><td valign="top"> {@code 'C'}
* <td valign="top"> <tt>'&#92;u0043'</tt>
- * <td> The upper-case variant of <tt>'c'</tt>.
+ * <td> The upper-case variant of {@code 'c'}.
*
* </table>
*
- * <p> The <tt>'-'</tt> flag defined for <a href="#dFlags">General
- * conversions</a> applies. If the <tt>'#'</tt> flag is given, then a {@link
+ * <p> The {@code '-'} flag defined for <a href="#dFlags">General
+ * conversions</a> applies. If the {@code '#'} flag is given, then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
* <p> The width is defined as for <a href="#genWidth">General conversions</a>.
@@ -843,14 +844,14 @@ import sun.misc.FormattedFloatingDecimal;
* <li> Each digit character <i>d</i> in the string is replaced by a
* locale-specific digit computed relative to the current locale's
* {@linkplain java.text.DecimalFormatSymbols#getZeroDigit() zero digit}
- * <i>z</i>; that is <i>d&nbsp;-&nbsp;</i> <tt>'0'</tt>
+ * <i>z</i>; that is <i>d&nbsp;-&nbsp;</i> {@code '0'}
* <i>&nbsp;+&nbsp;z</i>.
*
* <li> If a decimal separator is present, a locale-specific {@linkplain
* java.text.DecimalFormatSymbols#getDecimalSeparator decimal separator} is
* substituted.
*
- * <li> If the <tt>','</tt> (<tt>'&#92;u002c'</tt>)
+ * <li> If the {@code ','} (<tt>'&#92;u002c'</tt>)
* <a name="l10n group">flag</a> is given, then the locale-specific {@linkplain
* java.text.DecimalFormatSymbols#getGroupingSeparator grouping separator} is
* inserted by scanning the integer part of the string from least significant
@@ -858,111 +859,111 @@ import sun.misc.FormattedFloatingDecimal;
* the locale's {@linkplain java.text.DecimalFormat#getGroupingSize() grouping
* size}.
*
- * <li> If the <tt>'0'</tt> flag is given, then the locale-specific {@linkplain
+ * <li> If the {@code '0'} flag is given, then the locale-specific {@linkplain
* java.text.DecimalFormatSymbols#getZeroDigit() zero digits} are inserted
* after the sign character, if any, and before the first non-zero digit, until
* the length of the string is equal to the requested field width.
*
- * <li> If the value is negative and the <tt>'('</tt> flag is given, then a
- * <tt>'('</tt> (<tt>'&#92;u0028'</tt>) is prepended and a <tt>')'</tt>
+ * <li> If the value is negative and the {@code '('} flag is given, then a
+ * {@code '('} (<tt>'&#92;u0028'</tt>) is prepended and a {@code ')'}
* (<tt>'&#92;u0029'</tt>) is appended.
*
* <li> If the value is negative (or floating-point negative zero) and
- * <tt>'('</tt> flag is not given, then a <tt>'-'</tt> (<tt>'&#92;u002d'</tt>)
+ * {@code '('} flag is not given, then a {@code '-'} (<tt>'&#92;u002d'</tt>)
* is prepended.
*
- * <li> If the <tt>'+'</tt> flag is given and the value is positive or zero (or
- * floating-point positive zero), then a <tt>'+'</tt> (<tt>'&#92;u002b'</tt>)
+ * <li> If the {@code '+'} flag is given and the value is positive or zero (or
+ * floating-point positive zero), then a {@code '+'} (<tt>'&#92;u002b'</tt>)
* will be prepended.
*
* </ol>
*
* <p> If the value is NaN or positive infinity the literal strings "NaN" or
* "Infinity" respectively, will be output. If the value is negative infinity,
- * then the output will be "(Infinity)" if the <tt>'('</tt> flag is given
+ * then the output will be "(Infinity)" if the {@code '('} flag is given
* otherwise the output will be "-Infinity". These values are not localized.
*
* <p><a name="dnint"><b> Byte, Short, Integer, and Long </b></a>
*
- * <p> The following conversions may be applied to <tt>byte</tt>, {@link Byte},
- * <tt>short</tt>, {@link Short}, <tt>int</tt> and {@link Integer},
- * <tt>long</tt>, and {@link Long}.
+ * <p> The following conversions may be applied to {@code byte}, {@link Byte},
+ * {@code short}, {@link Short}, {@code int} and {@link Integer},
+ * {@code long}, and {@link Long}.
*
* <table cellpadding=5 summary="IntConv">
*
- * <tr><td valign="top"> <tt>'d'</tt>
+ * <tr><td valign="top"> {@code 'd'}
* <td valign="top"> <tt>'&#92;u0054'</tt>
* <td> Formats the argument as a decimal integer. The <a
* href="#l10n algorithm">localization algorithm</a> is applied.
*
- * <p> If the <tt>'0'</tt> flag is given and the value is negative, then
+ * <p> If the {@code '0'} flag is given and the value is negative, then
* the zero padding will occur after the sign.
*
- * <p> If the <tt>'#'</tt> flag is given then a {@link
+ * <p> If the {@code '#'} flag is given then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'o'</tt>
+ * <tr><td valign="top"> {@code 'o'}
* <td valign="top"> <tt>'&#92;u006f'</tt>
* <td> Formats the argument as an integer in base eight. No localization
* is applied.
*
* <p> If <i>x</i> is negative then the result will be an unsigned value
- * generated by adding 2<sup>n</sup> to the value where <tt>n</tt> is the
- * number of bits in the type as returned by the static <tt>SIZE</tt> field
+ * generated by adding 2<sup>n</sup> to the value where {@code n} is the
+ * number of bits in the type as returned by the static {@code SIZE} field
* in the {@linkplain Byte#SIZE Byte}, {@linkplain Short#SIZE Short},
* {@linkplain Integer#SIZE Integer}, or {@linkplain Long#SIZE Long}
* classes as appropriate.
*
- * <p> If the <tt>'#'</tt> flag is given then the output will always begin
- * with the radix indicator <tt>'0'</tt>.
+ * <p> If the {@code '#'} flag is given then the output will always begin
+ * with the radix indicator {@code '0'}.
*
- * <p> If the <tt>'0'</tt> flag is given then the output will be padded
+ * <p> If the {@code '0'} flag is given then the output will be padded
* with leading zeros to the field width following any indication of sign.
*
- * <p> If <tt>'('</tt>, <tt>'+'</tt>, '&nbsp&nbsp;', or <tt>','</tt> flags
+ * <p> If {@code '('}, {@code '+'}, '&nbsp&nbsp;', or {@code ','} flags
* are given then a {@link FormatFlagsConversionMismatchException} will be
* thrown.
*
- * <tr><td valign="top"> <tt>'x'</tt>
+ * <tr><td valign="top"> {@code 'x'}
* <td valign="top"> <tt>'&#92;u0078'</tt>
* <td> Formats the argument as an integer in base sixteen. No
* localization is applied.
*
* <p> If <i>x</i> is negative then the result will be an unsigned value
- * generated by adding 2<sup>n</sup> to the value where <tt>n</tt> is the
- * number of bits in the type as returned by the static <tt>SIZE</tt> field
+ * generated by adding 2<sup>n</sup> to the value where {@code n} is the
+ * number of bits in the type as returned by the static {@code SIZE} field
* in the {@linkplain Byte#SIZE Byte}, {@linkplain Short#SIZE Short},
* {@linkplain Integer#SIZE Integer}, or {@linkplain Long#SIZE Long}
* classes as appropriate.
*
- * <p> If the <tt>'#'</tt> flag is given then the output will always begin
- * with the radix indicator <tt>"0x"</tt>.
+ * <p> If the {@code '#'} flag is given then the output will always begin
+ * with the radix indicator {@code "0x"}.
*
- * <p> If the <tt>'0'</tt> flag is given then the output will be padded to
+ * <p> If the {@code '0'} flag is given then the output will be padded to
* the field width with leading zeros after the radix indicator or sign (if
* present).
*
- * <p> If <tt>'('</tt>, <tt>'&nbsp;&nbsp;'</tt>, <tt>'+'</tt>, or
- * <tt>','</tt> flags are given then a {@link
+ * <p> If {@code '('}, <tt>'&nbsp;&nbsp;'</tt>, {@code '+'}, or
+ * {@code ','} flags are given then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'X'</tt>
+ * <tr><td valign="top"> {@code 'X'}
* <td valign="top"> <tt>'&#92;u0058'</tt>
- * <td> The upper-case variant of <tt>'x'</tt>. The entire string
+ * <td> The upper-case variant of {@code 'x'}. The entire string
* representing the number will be converted to {@linkplain
- * String#toUpperCase upper case} including the <tt>'x'</tt> (if any) and
- * all hexadecimal digits <tt>'a'</tt> - <tt>'f'</tt>
+ * String#toUpperCase upper case} including the {@code 'x'} (if any) and
+ * all hexadecimal digits {@code 'a'} - {@code 'f'}
* (<tt>'&#92;u0061'</tt> - <tt>'&#92;u0066'</tt>).
*
* </table>
*
- * <p> If the conversion is <tt>'o'</tt>, <tt>'x'</tt>, or <tt>'X'</tt> and
- * both the <tt>'#'</tt> and the <tt>'0'</tt> flags are given, then result will
- * contain the radix indicator (<tt>'0'</tt> for octal and <tt>"0x"</tt> or
- * <tt>"0X"</tt> for hexadecimal), some number of zeros (based on the width),
+ * <p> If the conversion is {@code 'o'}, {@code 'x'}, or {@code 'X'} and
+ * both the {@code '#'} and the {@code '0'} flags are given, then result will
+ * contain the radix indicator ({@code '0'} for octal and {@code "0x"} or
+ * {@code "0X"} for hexadecimal), some number of zeros (based on the width),
* and the value.
*
- * <p> If the <tt>'-'</tt> flag is not given, then the space padding will occur
+ * <p> If the {@code '-'} flag is not given, then the space padding will occur
* before the sign.
*
* <p> The following <a name="intFlags">flags</a> apply to numeric integral
@@ -970,13 +971,13 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="intFlags">
*
- * <tr><td valign="top"> <tt>'+'</tt>
+ * <tr><td valign="top"> {@code '+'}
* <td valign="top"> <tt>'&#92;u002b'</tt>
* <td> Requires the output to include a positive sign for all positive
* numbers. If this flag is not given then only negative values will
* include a sign.
*
- * <p> If both the <tt>'+'</tt> and <tt>'&nbsp;&nbsp;'</tt> flags are given
+ * <p> If both the {@code '+'} and <tt>'&nbsp;&nbsp;'</tt> flags are given
* then an {@link IllegalFormatFlagsException} will be thrown.
*
* <tr><td valign="top"> <tt>'&nbsp;&nbsp;'</tt>
@@ -984,10 +985,10 @@ import sun.misc.FormattedFloatingDecimal;
* <td> Requires the output to include a single extra space
* (<tt>'&#92;u0020'</tt>) for non-negative values.
*
- * <p> If both the <tt>'+'</tt> and <tt>'&nbsp;&nbsp;'</tt> flags are given
+ * <p> If both the {@code '+'} and <tt>'&nbsp;&nbsp;'</tt> flags are given
* then an {@link IllegalFormatFlagsException} will be thrown.
*
- * <tr><td valign="top"> <tt>'0'</tt>
+ * <tr><td valign="top"> {@code '0'}
* <td valign="top"> <tt>'&#92;u0030'</tt>
* <td> Requires the output to be padded with leading {@linkplain
* java.text.DecimalFormatSymbols#getZeroDigit zeros} to the minimum field
@@ -995,20 +996,20 @@ import sun.misc.FormattedFloatingDecimal;
* or infinity. If the width is not provided, then a {@link
* MissingFormatWidthException} will be thrown.
*
- * <p> If both the <tt>'-'</tt> and <tt>'0'</tt> flags are given then an
+ * <p> If both the {@code '-'} and {@code '0'} flags are given then an
* {@link IllegalFormatFlagsException} will be thrown.
*
- * <tr><td valign="top"> <tt>','</tt>
+ * <tr><td valign="top"> {@code ','}
* <td valign="top"> <tt>'&#92;u002c'</tt>
* <td> Requires the output to include the locale-specific {@linkplain
* java.text.DecimalFormatSymbols#getGroupingSeparator group separators} as
* described in the <a href="#l10n group">"group" section</a> of the
* localization algorithm.
*
- * <tr><td valign="top"> <tt>'('</tt>
+ * <tr><td valign="top"> {@code '('}
* <td valign="top"> <tt>'&#92;u0028'</tt>
- * <td> Requires the output to prepend a <tt>'('</tt>
- * (<tt>'&#92;u0028'</tt>) and append a <tt>')'</tt>
+ * <td> Requires the output to prepend a {@code '('}
+ * (<tt>'&#92;u0028'</tt>) and append a {@code ')'}
* (<tt>'&#92;u0029'</tt>) to negative values.
*
* </table>
@@ -1018,9 +1019,9 @@ import sun.misc.FormattedFloatingDecimal;
*
* <ul>
*
- * <li> The output is right-justified within the <tt>width</tt>
+ * <li> The output is right-justified within the {@code width}
*
- * <li> Negative numbers begin with a <tt>'-'</tt> (<tt>'&#92;u002d'</tt>)
+ * <li> Negative numbers begin with a {@code '-'} (<tt>'&#92;u002d'</tt>)
*
* <li> Positive numbers and zero do not include a sign or extra leading
* space
@@ -1034,7 +1035,7 @@ import sun.misc.FormattedFloatingDecimal;
* separators, radix indicator, and parentheses. If the length of the
* converted value is less than the width then the output will be padded by
* spaces (<tt>'&#92;u0020'</tt>) until the total number of characters equals
- * width. The padding is on the left by default. If <tt>'-'</tt> flag is
+ * width. The padding is on the left by default. If {@code '-'} flag is
* given then the padding will be on the right. If width is not specified then
* there is no minimum.
*
@@ -1048,81 +1049,81 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="BIntConv">
*
- * <tr><td valign="top"> <tt>'d'</tt>
+ * <tr><td valign="top"> {@code 'd'}
* <td valign="top"> <tt>'&#92;u0054'</tt>
* <td> Requires the output to be formatted as a decimal integer. The <a
* href="#l10n algorithm">localization algorithm</a> is applied.
*
- * <p> If the <tt>'#'</tt> flag is given {@link
+ * <p> If the {@code '#'} flag is given {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'o'</tt>
+ * <tr><td valign="top"> {@code 'o'}
* <td valign="top"> <tt>'&#92;u006f'</tt>
* <td> Requires the output to be formatted as an integer in base eight.
* No localization is applied.
*
* <p> If <i>x</i> is negative then the result will be a signed value
- * beginning with <tt>'-'</tt> (<tt>'&#92;u002d'</tt>). Signed output is
+ * beginning with {@code '-'} (<tt>'&#92;u002d'</tt>). Signed output is
* allowed for this type because unlike the primitive types it is not
* possible to create an unsigned equivalent without assuming an explicit
* data-type size.
*
- * <p> If <i>x</i> is positive or zero and the <tt>'+'</tt> flag is given
- * then the result will begin with <tt>'+'</tt> (<tt>'&#92;u002b'</tt>).
+ * <p> If <i>x</i> is positive or zero and the {@code '+'} flag is given
+ * then the result will begin with {@code '+'} (<tt>'&#92;u002b'</tt>).
*
- * <p> If the <tt>'#'</tt> flag is given then the output will always begin
- * with <tt>'0'</tt> prefix.
+ * <p> If the {@code '#'} flag is given then the output will always begin
+ * with {@code '0'} prefix.
*
- * <p> If the <tt>'0'</tt> flag is given then the output will be padded
+ * <p> If the {@code '0'} flag is given then the output will be padded
* with leading zeros to the field width following any indication of sign.
*
- * <p> If the <tt>','</tt> flag is given then a {@link
+ * <p> If the {@code ','} flag is given then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'x'</tt>
+ * <tr><td valign="top"> {@code 'x'}
* <td valign="top"> <tt>'&#92;u0078'</tt>
* <td> Requires the output to be formatted as an integer in base
* sixteen. No localization is applied.
*
* <p> If <i>x</i> is negative then the result will be a signed value
- * beginning with <tt>'-'</tt> (<tt>'&#92;u002d'</tt>). Signed output is
+ * beginning with {@code '-'} (<tt>'&#92;u002d'</tt>). Signed output is
* allowed for this type because unlike the primitive types it is not
* possible to create an unsigned equivalent without assuming an explicit
* data-type size.
*
- * <p> If <i>x</i> is positive or zero and the <tt>'+'</tt> flag is given
- * then the result will begin with <tt>'+'</tt> (<tt>'&#92;u002b'</tt>).
+ * <p> If <i>x</i> is positive or zero and the {@code '+'} flag is given
+ * then the result will begin with {@code '+'} (<tt>'&#92;u002b'</tt>).
*
- * <p> If the <tt>'#'</tt> flag is given then the output will always begin
- * with the radix indicator <tt>"0x"</tt>.
+ * <p> If the {@code '#'} flag is given then the output will always begin
+ * with the radix indicator {@code "0x"}.
*
- * <p> If the <tt>'0'</tt> flag is given then the output will be padded to
+ * <p> If the {@code '0'} flag is given then the output will be padded to
* the field width with leading zeros after the radix indicator or sign (if
* present).
*
- * <p> If the <tt>','</tt> flag is given then a {@link
+ * <p> If the {@code ','} flag is given then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'X'</tt>
+ * <tr><td valign="top"> {@code 'X'}
* <td valign="top"> <tt>'&#92;u0058'</tt>
- * <td> The upper-case variant of <tt>'x'</tt>. The entire string
+ * <td> The upper-case variant of {@code 'x'}. The entire string
* representing the number will be converted to {@linkplain
- * String#toUpperCase upper case} including the <tt>'x'</tt> (if any) and
- * all hexadecimal digits <tt>'a'</tt> - <tt>'f'</tt>
+ * String#toUpperCase upper case} including the {@code 'x'} (if any) and
+ * all hexadecimal digits {@code 'a'} - {@code 'f'}
* (<tt>'&#92;u0061'</tt> - <tt>'&#92;u0066'</tt>).
*
* </table>
*
- * <p> If the conversion is <tt>'o'</tt>, <tt>'x'</tt>, or <tt>'X'</tt> and
- * both the <tt>'#'</tt> and the <tt>'0'</tt> flags are given, then result will
- * contain the base indicator (<tt>'0'</tt> for octal and <tt>"0x"</tt> or
- * <tt>"0X"</tt> for hexadecimal), some number of zeros (based on the width),
+ * <p> If the conversion is {@code 'o'}, {@code 'x'}, or {@code 'X'} and
+ * both the {@code '#'} and the {@code '0'} flags are given, then result will
+ * contain the base indicator ({@code '0'} for octal and {@code "0x"} or
+ * {@code "0X"} for hexadecimal), some number of zeros (based on the width),
* and the value.
*
- * <p> If the <tt>'0'</tt> flag is given and the value is negative, then the
+ * <p> If the {@code '0'} flag is given and the value is negative, then the
* zero padding will occur after the sign.
*
- * <p> If the <tt>'-'</tt> flag is not given, then the space padding will occur
+ * <p> If the {@code '-'} flag is not given, then the space padding will occur
* before the sign.
*
* <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and
@@ -1137,12 +1138,12 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p><a name="dndec"><b> Float and Double</b></a>
*
- * <p> The following conversions may be applied to <tt>float</tt>, {@link
- * Float}, <tt>double</tt> and {@link Double}.
+ * <p> The following conversions may be applied to {@code float}, {@link
+ * Float}, {@code double} and {@link Double}.
*
* <table cellpadding=5 summary="floatConv">
*
- * <tr><td valign="top"> <tt>'e'</tt>
+ * <tr><td valign="top"> {@code 'e'}
* <td valign="top"> <tt>'&#92;u0065'</tt>
* <td> Requires the output to be formatted using <a
* name="scientific">computerized scientific notation</a>. The <a
@@ -1155,7 +1156,7 @@ import sun.misc.FormattedFloatingDecimal;
* localized.
*
* <p> If <i>m</i> is positive-zero or negative-zero, then the exponent
- * will be <tt>"+00"</tt>.
+ * will be {@code "+00"}.
*
* <p> Otherwise, the result is a string that represents the sign and
* magnitude (absolute value) of the argument. The formatting of the sign
@@ -1169,7 +1170,7 @@ import sun.misc.FormattedFloatingDecimal;
* that 1 &lt;= <i>a</i> &lt; 10. The magnitude is then represented as the
* integer part of <i>a</i>, as a single decimal digit, followed by the
* decimal separator followed by decimal digits representing the fractional
- * part of <i>a</i>, followed by the exponent symbol <tt>'e'</tt>
+ * part of <i>a</i>, followed by the exponent symbol {@code 'e'}
* (<tt>'&#92;u0065'</tt>), followed by the sign of the exponent, followed
* by a representation of <i>n</i> as a decimal integer, as produced by the
* method {@link Long#toString(long, int)}, and zero-padded to include at
@@ -1177,7 +1178,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The number of digits in the result for the fractional part of
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
- * specified then the default value is <tt>6</tt>. If the precision is less
+ * specified then the default value is {@code 6}. If the precision is less
* than the number of digits which would appear after the decimal point in
* the string returned by {@link Float#toString(float)} or {@link
* Double#toString(double)} respectively, then the value will be rounded
@@ -1187,15 +1188,15 @@ import sun.misc.FormattedFloatingDecimal;
* Float#toString(float)} or {@link Double#toString(double)} as
* appropriate.
*
- * <p>If the <tt>','</tt> flag is given, then an {@link
+ * <p>If the {@code ','} flag is given, then an {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'E'</tt>
+ * <tr><td valign="top"> {@code 'E'}
* <td valign="top"> <tt>'&#92;u0045'</tt>
- * <td> The upper-case variant of <tt>'e'</tt>. The exponent symbol
- * will be <tt>'E'</tt> (<tt>'&#92;u0045'</tt>).
+ * <td> The upper-case variant of {@code 'e'}. The exponent symbol
+ * will be {@code 'E'} (<tt>'&#92;u0045'</tt>).
*
- * <tr><td valign="top"> <tt>'g'</tt>
+ * <tr><td valign="top"> {@code 'g'}
* <td valign="top"> <tt>'&#92;u0067'</tt>
* <td> Requires the output to be formatted in general scientific notation
* as described below. The <a href="#l10n algorithm">localization
@@ -1214,17 +1215,17 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The total number of significant digits in <i>m</i> is equal to the
* precision. If the precision is not specified, then the default value is
- * <tt>6</tt>. If the precision is <tt>0</tt>, then it is taken to be
- * <tt>1</tt>.
+ * {@code 6}. If the precision is {@code 0}, then it is taken to be
+ * {@code 1}.
*
- * <p> If the <tt>'#'</tt> flag is given then an {@link
+ * <p> If the {@code '#'} flag is given then an {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'G'</tt>
+ * <tr><td valign="top"> {@code 'G'}
* <td valign="top"> <tt>'&#92;u0047'</tt>
- * <td> The upper-case variant of <tt>'g'</tt>.
+ * <td> The upper-case variant of {@code 'g'}.
*
- * <tr><td valign="top"> <tt>'f'</tt>
+ * <tr><td valign="top"> {@code 'f'}
* <td valign="top"> <tt>'&#92;u0066'</tt>
* <td> Requires the output to be formatted using <a name="decimal">decimal
* format</a>. The <a href="#l10n algorithm">localization algorithm</a> is
@@ -1246,17 +1247,17 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The number of digits in the result for the fractional part of
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
- * specified then the default value is <tt>6</tt>. If the precision is less
+ * specified then the default value is {@code 6}. If the precision is less
* than the number of digits which would appear after the decimal point in
* the string returned by {@link Float#toString(float)} or {@link
* Double#toString(double)} respectively, then the value will be rounded
* using the {@linkplain java.math.BigDecimal#ROUND_HALF_UP round half up
* algorithm}. Otherwise, zeros may be appended to reach the precision.
- * For a canonical representation of the value,use {@link
+ * For a canonical representation of the value, use {@link
* Float#toString(float)} or {@link Double#toString(double)} as
* appropriate.
*
- * <tr><td valign="top"> <tt>'a'</tt>
+ * <tr><td valign="top"> {@code 'a'}
* <td valign="top"> <tt>'&#92;u0061'</tt>
* <td> Requires the output to be formatted in hexadecimal exponential
* form. No localization is applied.
@@ -1265,10 +1266,10 @@ import sun.misc.FormattedFloatingDecimal;
* (absolute value) of the argument <i>x</i>.
*
* <p> If <i>x</i> is negative or a negative-zero value then the result
- * will begin with <tt>'-'</tt> (<tt>'&#92;u002d'</tt>).
+ * will begin with {@code '-'} (<tt>'&#92;u002d'</tt>).
*
* <p> If <i>x</i> is positive or a positive-zero value and the
- * <tt>'+'</tt> flag is given then the result will begin with <tt>'+'</tt>
+ * {@code '+'} flag is given then the result will begin with {@code '+'}
* (<tt>'&#92;u002b'</tt>).
*
* <p> The formatting of the magnitude <i>m</i> depends upon its value.
@@ -1279,43 +1280,43 @@ import sun.misc.FormattedFloatingDecimal;
* "Infinity", respectively, will be output.
*
* <li> If <i>m</i> is zero then it is represented by the string
- * <tt>"0x0.0p0"</tt>.
+ * {@code "0x0.0p0"}.
*
- * <li> If <i>m</i> is a <tt>double</tt> value with a normalized
+ * <li> If <i>m</i> is a {@code double} value with a normalized
* representation then substrings are used to represent the significand and
* exponent fields. The significand is represented by the characters
- * <tt>"0x1."</tt> followed by the hexadecimal representation of the rest
+ * {@code "0x1."} followed by the hexadecimal representation of the rest
* of the significand as a fraction. The exponent is represented by
- * <tt>'p'</tt> (<tt>'&#92;u0070'</tt>) followed by a decimal string of the
+ * {@code 'p'} (<tt>'&#92;u0070'</tt>) followed by a decimal string of the
* unbiased exponent as if produced by invoking {@link
* Integer#toString(int) Integer.toString} on the exponent value.
*
- * <li> If <i>m</i> is a <tt>double</tt> value with a subnormal
+ * <li> If <i>m</i> is a {@code double} value with a subnormal
* representation then the significand is represented by the characters
- * <tt>'0x0.'</tt> followed by the hexadecimal representation of the rest
+ * {@code '0x0.'} followed by the hexadecimal representation of the rest
* of the significand as a fraction. The exponent is represented by
- * <tt>'p-1022'</tt>. Note that there must be at least one nonzero digit
+ * {@code 'p-1022'}. Note that there must be at least one nonzero digit
* in a subnormal significand.
*
* </ul>
*
- * <p> If the <tt>'('</tt> or <tt>','</tt> flags are given, then a {@link
+ * <p> If the {@code '('} or {@code ','} flags are given, then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'A'</tt>
+ * <tr><td valign="top"> {@code 'A'}
* <td valign="top"> <tt>'&#92;u0041'</tt>
- * <td> The upper-case variant of <tt>'a'</tt>. The entire string
+ * <td> The upper-case variant of {@code 'a'}. The entire string
* representing the number will be converted to upper case including the
- * <tt>'x'</tt> (<tt>'&#92;u0078'</tt>) and <tt>'p'</tt>
- * (<tt>'&#92;u0070'</tt> and all hexadecimal digits <tt>'a'</tt> -
- * <tt>'f'</tt> (<tt>'&#92;u0061'</tt> - <tt>'&#92;u0066'</tt>).
+ * {@code 'x'} (<tt>'&#92;u0078'</tt>) and {@code 'p'}
+ * (<tt>'&#92;u0070'</tt> and all hexadecimal digits {@code 'a'} -
+ * {@code 'f'} (<tt>'&#92;u0061'</tt> - <tt>'&#92;u0066'</tt>).
*
* </table>
*
* <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and
* Long apply.
*
- * <p> If the <tt>'#'</tt> flag is given, then the decimal separator will
+ * <p> If the {@code '#'} flag is given, then the decimal separator will
* always be present.
*
* <p> If no <a name="floatdFlags">flags</a> are given the default formatting
@@ -1323,9 +1324,9 @@ import sun.misc.FormattedFloatingDecimal;
*
* <ul>
*
- * <li> The output is right-justified within the <tt>width</tt>
+ * <li> The output is right-justified within the {@code width}
*
- * <li> Negative numbers begin with a <tt>'-'</tt>
+ * <li> Negative numbers begin with a {@code '-'}
*
* <li> Positive numbers and positive zero do not include a sign or extra
* leading space
@@ -1343,21 +1344,21 @@ import sun.misc.FormattedFloatingDecimal;
* the length of the converted value is less than the width then the output
* will be padded by spaces (<tt>'&#92;u0020'</tt>) until the total number of
* characters equals width. The padding is on the left by default. If the
- * <tt>'-'</tt> flag is given then the padding will be on the right. If width
+ * {@code '-'} flag is given then the padding will be on the right. If width
* is not specified then there is no minimum.
*
- * <p> If the <a name="floatDPrec">conversion</a> is <tt>'e'</tt>,
- * <tt>'E'</tt> or <tt>'f'</tt>, then the precision is the number of digits
+ * <p> If the <a name="floatDPrec">conversion</a> is {@code 'e'},
+ * {@code 'E'} or {@code 'f'}, then the precision is the number of digits
* after the decimal separator. If the precision is not specified, then it is
- * assumed to be <tt>6</tt>.
+ * assumed to be {@code 6}.
*
- * <p> If the conversion is <tt>'g'</tt> or <tt>'G'</tt>, then the precision is
+ * <p> If the conversion is {@code 'g'} or {@code 'G'}, then the precision is
* the total number of significant digits in the resulting magnitude after
* rounding. If the precision is not specified, then the default value is
- * <tt>6</tt>. If the precision is <tt>0</tt>, then it is taken to be
- * <tt>1</tt>.
+ * {@code 6}. If the precision is {@code 0}, then it is taken to be
+ * {@code 1}.
*
- * <p> If the conversion is <tt>'a'</tt> or <tt>'A'</tt>, then the precision
+ * <p> If the conversion is {@code 'a'} or {@code 'A'}, then the precision
* is the number of hexadecimal digits after the decimal separator. If the
* precision is not provided, then all of the digits as returned by {@link
* Double#toHexString(double)} will be output.
@@ -1369,7 +1370,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="floatConv">
*
- * <tr><td valign="top"> <tt>'e'</tt>
+ * <tr><td valign="top"> {@code 'e'}
* <td valign="top"> <tt>'&#92;u0065'</tt>
* <td> Requires the output to be formatted using <a
* name="scientific">computerized scientific notation</a>. The <a
@@ -1378,7 +1379,7 @@ import sun.misc.FormattedFloatingDecimal;
* <p> The formatting of the magnitude <i>m</i> depends upon its value.
*
* <p> If <i>m</i> is positive-zero or negative-zero, then the exponent
- * will be <tt>"+00"</tt>.
+ * will be {@code "+00"}.
*
* <p> Otherwise, the result is a string that represents the sign and
* magnitude (absolute value) of the argument. The formatting of the sign
@@ -1392,7 +1393,7 @@ import sun.misc.FormattedFloatingDecimal;
* that 1 &lt;= <i>a</i> &lt; 10. The magnitude is then represented as the
* integer part of <i>a</i>, as a single decimal digit, followed by the
* decimal separator followed by decimal digits representing the fractional
- * part of <i>a</i>, followed by the exponent symbol <tt>'e'</tt>
+ * part of <i>a</i>, followed by the exponent symbol {@code 'e'}
* (<tt>'&#92;u0065'</tt>), followed by the sign of the exponent, followed
* by a representation of <i>n</i> as a decimal integer, as produced by the
* method {@link Long#toString(long, int)}, and zero-padded to include at
@@ -1400,7 +1401,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The number of digits in the result for the fractional part of
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
- * specified then the default value is <tt>6</tt>. If the precision is
+ * specified then the default value is {@code 6}. If the precision is
* less than the number of digits which would appear after the decimal
* point in the string returned by {@link Float#toString(float)} or {@link
* Double#toString(double)} respectively, then the value will be rounded
@@ -1409,15 +1410,15 @@ import sun.misc.FormattedFloatingDecimal;
* For a canonical representation of the value, use {@link
* BigDecimal#toString()}.
*
- * <p> If the <tt>','</tt> flag is given, then an {@link
+ * <p> If the {@code ','} flag is given, then an {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'E'</tt>
+ * <tr><td valign="top"> {@code 'E'}
* <td valign="top"> <tt>'&#92;u0045'</tt>
- * <td> The upper-case variant of <tt>'e'</tt>. The exponent symbol
- * will be <tt>'E'</tt> (<tt>'&#92;u0045'</tt>).
+ * <td> The upper-case variant of {@code 'e'}. The exponent symbol
+ * will be {@code 'E'} (<tt>'&#92;u0045'</tt>).
*
- * <tr><td valign="top"> <tt>'g'</tt>
+ * <tr><td valign="top"> {@code 'g'}
* <td valign="top"> <tt>'&#92;u0067'</tt>
* <td> Requires the output to be formatted in general scientific notation
* as described below. The <a href="#l10n algorithm">localization
@@ -1436,17 +1437,17 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The total number of significant digits in <i>m</i> is equal to the
* precision. If the precision is not specified, then the default value is
- * <tt>6</tt>. If the precision is <tt>0</tt>, then it is taken to be
- * <tt>1</tt>.
+ * {@code 6}. If the precision is {@code 0}, then it is taken to be
+ * {@code 1}.
*
- * <p> If the <tt>'#'</tt> flag is given then an {@link
+ * <p> If the {@code '#'} flag is given then an {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
- * <tr><td valign="top"> <tt>'G'</tt>
+ * <tr><td valign="top"> {@code 'G'}
* <td valign="top"> <tt>'&#92;u0047'</tt>
- * <td> The upper-case variant of <tt>'g'</tt>.
+ * <td> The upper-case variant of {@code 'g'}.
*
- * <tr><td valign="top"> <tt>'f'</tt>
+ * <tr><td valign="top"> {@code 'f'}
* <td valign="top"> <tt>'&#92;u0066'</tt>
* <td> Requires the output to be formatted using <a name="decimal">decimal
* format</a>. The <a href="#l10n algorithm">localization algorithm</a> is
@@ -1464,7 +1465,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <p> The number of digits in the result for the fractional part of
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
- * specified then the default value is <tt>6</tt>. If the precision is
+ * specified then the default value is {@code 6}. If the precision is
* less than the number of digits which would appear after the decimal
* point in the string returned by {@link Float#toString(float)} or {@link
* Double#toString(double)} respectively, then the value will be rounded
@@ -1478,7 +1479,7 @@ import sun.misc.FormattedFloatingDecimal;
* <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and
* Long apply.
*
- * <p> If the <tt>'#'</tt> flag is given, then the decimal separator will
+ * <p> If the {@code '#'} flag is given, then the decimal separator will
* always be present.
*
* <p> The <a href="#floatdFlags">default behavior</a> when no flags are
@@ -1490,114 +1491,114 @@ import sun.misc.FormattedFloatingDecimal;
*
* <h4><a name="ddt">Date/Time</a></h4>
*
- * <p> This conversion may be applied to <tt>long</tt>, {@link Long}, {@link
+ * <p> This conversion may be applied to {@code long}, {@link Long}, {@link
* Calendar}, and {@link Date}.
*
* <table cellpadding=5 summary="DTConv">
*
- * <tr><td valign="top"> <tt>'t'</tt>
+ * <tr><td valign="top"> {@code 't'}
* <td valign="top"> <tt>'&#92;u0074'</tt>
* <td> Prefix for date and time conversion characters.
- * <tr><td valign="top"> <tt>'T'</tt>
+ * <tr><td valign="top"> {@code 'T'}
* <td valign="top"> <tt>'&#92;u0054'</tt>
- * <td> The upper-case variant of <tt>'t'</tt>.
+ * <td> The upper-case variant of {@code 't'}.
*
* </table>
*
* <p> The following date and time conversion character suffixes are defined
- * for the <tt>'t'</tt> and <tt>'T'</tt> conversions. The types are similar to
- * but not completely identical to those defined by GNU <tt>date</tt> and
- * POSIX <tt>strftime(3c)</tt>. Additional conversion types are provided to
- * access Java-specific functionality (e.g. <tt>'L'</tt> for milliseconds
+ * for the {@code 't'} and {@code 'T'} conversions. The types are similar to
+ * but not completely identical to those defined by GNU {@code date} and
+ * POSIX {@code strftime(3c)}. Additional conversion types are provided to
+ * access Java-specific functionality (e.g. {@code 'L'} for milliseconds
* within the second).
*
* <p> The following conversion characters are used for formatting times:
*
* <table cellpadding=5 summary="time">
*
- * <tr><td valign="top"> <tt>'H'</tt>
+ * <tr><td valign="top"> {@code 'H'}
* <td valign="top"> <tt>'&#92;u0048'</tt>
* <td> Hour of the day for the 24-hour clock, formatted as two digits with
- * a leading zero as necessary i.e. <tt>00 - 23</tt>. <tt>00</tt>
+ * a leading zero as necessary i.e. {@code 00 - 23}. {@code 00}
* corresponds to midnight.
*
- * <tr><td valign="top"><tt>'I'</tt>
+ * <tr><td valign="top">{@code 'I'}
* <td valign="top"> <tt>'&#92;u0049'</tt>
* <td> Hour for the 12-hour clock, formatted as two digits with a leading
- * zero as necessary, i.e. <tt>01 - 12</tt>. <tt>01</tt> corresponds to
+ * zero as necessary, i.e. {@code 01 - 12}. {@code 01} corresponds to
* one o'clock (either morning or afternoon).
*
- * <tr><td valign="top"><tt>'k'</tt>
+ * <tr><td valign="top">{@code 'k'}
* <td valign="top"> <tt>'&#92;u006b'</tt>
- * <td> Hour of the day for the 24-hour clock, i.e. <tt>0 - 23</tt>.
- * <tt>0</tt> corresponds to midnight.
+ * <td> Hour of the day for the 24-hour clock, i.e. {@code 0 - 23}.
+ * {@code 0} corresponds to midnight.
*
- * <tr><td valign="top"><tt>'l'</tt>
+ * <tr><td valign="top">{@code 'l'}
* <td valign="top"> <tt>'&#92;u006c'</tt>
- * <td> Hour for the 12-hour clock, i.e. <tt>1 - 12</tt>. <tt>1</tt>
+ * <td> Hour for the 12-hour clock, i.e. {@code 1 - 12}. {@code 1}
* corresponds to one o'clock (either morning or afternoon).
*
- * <tr><td valign="top"><tt>'M'</tt>
+ * <tr><td valign="top">{@code 'M'}
* <td valign="top"> <tt>'&#92;u004d'</tt>
* <td> Minute within the hour formatted as two digits with a leading zero
- * as necessary, i.e. <tt>00 - 59</tt>.
+ * as necessary, i.e. {@code 00 - 59}.
*
- * <tr><td valign="top"><tt>'S'</tt>
+ * <tr><td valign="top">{@code 'S'}
* <td valign="top"> <tt>'&#92;u0053'</tt>
* <td> Seconds within the minute, formatted as two digits with a leading
- * zero as necessary, i.e. <tt>00 - 60</tt> ("<tt>60</tt>" is a special
+ * zero as necessary, i.e. {@code 00 - 60} ("{@code 60}" is a special
* value required to support leap seconds).
*
- * <tr><td valign="top"><tt>'L'</tt>
+ * <tr><td valign="top">{@code 'L'}
* <td valign="top"> <tt>'&#92;u004c'</tt>
* <td> Millisecond within the second formatted as three digits with
- * leading zeros as necessary, i.e. <tt>000 - 999</tt>.
+ * leading zeros as necessary, i.e. {@code 000 - 999}.
*
- * <tr><td valign="top"><tt>'N'</tt>
+ * <tr><td valign="top">{@code 'N'}
* <td valign="top"> <tt>'&#92;u004e'</tt>
* <td> Nanosecond within the second, formatted as nine digits with leading
- * zeros as necessary, i.e. <tt>000000000 - 999999999</tt>. The precision
+ * zeros as necessary, i.e. {@code 000000000 - 999999999}. The precision
* of this value is limited by the resolution of the underlying operating
* system or hardware.
*
- * <tr><td valign="top"><tt>'p'</tt>
+ * <tr><td valign="top">{@code 'p'}
* <td valign="top"> <tt>'&#92;u0070'</tt>
* <td> Locale-specific {@linkplain
* java.text.DateFormatSymbols#getAmPmStrings morning or afternoon} marker
- * in lower case, e.g."<tt>am</tt>" or "<tt>pm</tt>". Use of the
- * conversion prefix <tt>'T'</tt> forces this output to upper case. (Note
- * that <tt>'p'</tt> produces lower-case output. This is different from
- * GNU <tt>date</tt> and POSIX <tt>strftime(3c)</tt> which produce
+ * in lower case, e.g."{@code am}" or "{@code pm}". Use of the
+ * conversion prefix {@code 'T'} forces this output to upper case. (Note
+ * that {@code 'p'} produces lower-case output. This is different from
+ * GNU {@code date} and POSIX {@code strftime(3c)} which produce
* upper-case output.)
*
- * <tr><td valign="top"><tt>'z'</tt>
+ * <tr><td valign="top">{@code 'z'}
* <td valign="top"> <tt>'&#92;u007a'</tt>
* <td> <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC&nbsp;822</a>
- * style numeric time zone offset from GMT, e.g. <tt>-0800</tt>. This
+ * style numeric time zone offset from GMT, e.g. {@code -0800}. This
* value will be adjusted as necessary for Daylight Saving Time. For
- * <tt>long</tt>, {@link Long}, and {@link Date} the time zone used is
- * the {@plainlink TimeZone#getDefault() default time zone} for this
+ * {@code long}, {@link Long}, and {@link Date} the time zone used is
+ * the {@linkplain TimeZone#getDefault() default time zone} for this
* instance of the Java virtual machine.
*
- * <tr><td valign="top"><tt>'Z'</tt>
+ * <tr><td valign="top">{@code 'Z'}
* <td> A string representing the abbreviation for the time zone. This
* value will be adjusted as necessary for Daylight Saving Time. For
- * <tt>long</tt>, {@link Long}, and {@link Date} the time zone used is
- * the {@plainlink TimeZone#getDefault() default time zone} for this
+ * {@code long}, {@link Long}, and {@link Date} the time zone used is
+ * the {@linkplain TimeZone#getDefault() default time zone} for this
* instance of the Java virtual machine. The Formatter's locale will
* supersede the locale of the argument (if any).
*
- * <tr><td valign="top"><tt>'s'</tt>
+ * <tr><td valign="top">{@code 's'}
* <td valign="top"> <tt>'&#92;u0073'</tt>
* <td> Seconds since the beginning of the epoch starting at 1 January 1970
- * <tt>00:00:00</tt> UTC, i.e. <tt>Long.MIN_VALUE/1000</tt> to
- * <tt>Long.MAX_VALUE/1000</tt>.
+ * {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE/1000} to
+ * {@code Long.MAX_VALUE/1000}.
*
- * <tr><td valign="top"><tt>'Q'</tt>
+ * <tr><td valign="top">{@code 'Q'}
* <td valign="top"> <tt>'&#92;u004f'</tt>
* <td> Milliseconds since the beginning of the epoch starting at 1 January
- * 1970 <tt>00:00:00</tt> UTC, i.e. <tt>Long.MIN_VALUE</tt> to
- * <tt>Long.MAX_VALUE</tt>. The precision of this value is limited by
+ * 1970 {@code 00:00:00} UTC, i.e. {@code Long.MIN_VALUE} to
+ * {@code Long.MAX_VALUE}. The precision of this value is limited by
* the resolution of the underlying operating system or hardware.
*
* </table>
@@ -1606,71 +1607,71 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="date">
*
- * <tr><td valign="top"><tt>'B'</tt>
+ * <tr><td valign="top">{@code 'B'}
* <td valign="top"> <tt>'&#92;u0042'</tt>
* <td> Locale-specific {@linkplain java.text.DateFormatSymbols#getMonths
- * full month name}, e.g. <tt>"January"</tt>, <tt>"February"</tt>.
+ * full month name}, e.g. {@code "January"}, {@code "February"}.
*
- * <tr><td valign="top"><tt>'b'</tt>
+ * <tr><td valign="top">{@code 'b'}
* <td valign="top"> <tt>'&#92;u0062'</tt>
* <td> Locale-specific {@linkplain
* java.text.DateFormatSymbols#getShortMonths abbreviated month name},
- * e.g. <tt>"Jan"</tt>, <tt>"Feb"</tt>.
+ * e.g. {@code "Jan"}, {@code "Feb"}.
*
- * <tr><td valign="top"><tt>'h'</tt>
+ * <tr><td valign="top">{@code 'h'}
* <td valign="top"> <tt>'&#92;u0068'</tt>
- * <td> Same as <tt>'b'</tt>.
+ * <td> Same as {@code 'b'}.
*
- * <tr><td valign="top"><tt>'A'</tt>
+ * <tr><td valign="top">{@code 'A'}
* <td valign="top"> <tt>'&#92;u0041'</tt>
* <td> Locale-specific full name of the {@linkplain
* java.text.DateFormatSymbols#getWeekdays day of the week},
- * e.g. <tt>"Sunday"</tt>, <tt>"Monday"</tt>
+ * e.g. {@code "Sunday"}, {@code "Monday"}
*
- * <tr><td valign="top"><tt>'a'</tt>
+ * <tr><td valign="top">{@code 'a'}
* <td valign="top"> <tt>'&#92;u0061'</tt>
* <td> Locale-specific short name of the {@linkplain
* java.text.DateFormatSymbols#getShortWeekdays day of the week},
- * e.g. <tt>"Sun"</tt>, <tt>"Mon"</tt>
+ * e.g. {@code "Sun"}, {@code "Mon"}
*
- * <tr><td valign="top"><tt>'C'</tt>
+ * <tr><td valign="top">{@code 'C'}
* <td valign="top"> <tt>'&#92;u0043'</tt>
- * <td> Four-digit year divided by <tt>100</tt>, formatted as two digits
- * with leading zero as necessary, i.e. <tt>00 - 99</tt>
+ * <td> Four-digit year divided by {@code 100}, formatted as two digits
+ * with leading zero as necessary, i.e. {@code 00 - 99}
*
- * <tr><td valign="top"><tt>'Y'</tt>
+ * <tr><td valign="top">{@code 'Y'}
* <td valign="top"> <tt>'&#92;u0059'</tt> <td> Year, formatted to at least
- * four digits with leading zeros as necessary, e.g. <tt>0092</tt> equals
- * <tt>92</tt> CE for the Gregorian calendar.
+ * four digits with leading zeros as necessary, e.g. {@code 0092} equals
+ * {@code 92} CE for the Gregorian calendar.
*
- * <tr><td valign="top"><tt>'y'</tt>
+ * <tr><td valign="top">{@code 'y'}
* <td valign="top"> <tt>'&#92;u0079'</tt>
* <td> Last two digits of the year, formatted with leading zeros as
- * necessary, i.e. <tt>00 - 99</tt>.
+ * necessary, i.e. {@code 00 - 99}.
*
- * <tr><td valign="top"><tt>'j'</tt>
+ * <tr><td valign="top">{@code 'j'}
* <td valign="top"> <tt>'&#92;u006a'</tt>
* <td> Day of year, formatted as three digits with leading zeros as
- * necessary, e.g. <tt>001 - 366</tt> for the Gregorian calendar.
- * <tt>001</tt> corresponds to the first day of the year.
+ * necessary, e.g. {@code 001 - 366} for the Gregorian calendar.
+ * {@code 001} corresponds to the first day of the year.
*
- * <tr><td valign="top"><tt>'m'</tt>
+ * <tr><td valign="top">{@code 'm'}
* <td valign="top"> <tt>'&#92;u006d'</tt>
* <td> Month, formatted as two digits with leading zeros as necessary,
- * i.e. <tt>01 - 13</tt>, where "<tt>01</tt>" is the first month of the
- * year and ("<tt>13</tt>" is a special value required to support lunar
+ * i.e. {@code 01 - 13}, where "{@code 01}" is the first month of the
+ * year and ("{@code 13}" is a special value required to support lunar
* calendars).
*
- * <tr><td valign="top"><tt>'d'</tt>
+ * <tr><td valign="top">{@code 'd'}
* <td valign="top"> <tt>'&#92;u0064'</tt>
* <td> Day of month, formatted as two digits with leading zeros as
- * necessary, i.e. <tt>01 - 31</tt>, where "<tt>01</tt>" is the first day
+ * necessary, i.e. {@code 01 - 31}, where "{@code 01}" is the first day
* of the month.
*
- * <tr><td valign="top"><tt>'e'</tt>
+ * <tr><td valign="top">{@code 'e'}
* <td valign="top"> <tt>'&#92;u0065'</tt>
- * <td> Day of month, formatted as two digits, i.e. <tt>1 - 31</tt> where
- * "<tt>1</tt>" is the first day of the month.
+ * <td> Day of month, formatted as two digits, i.e. {@code 1 - 31} where
+ * "{@code 1}" is the first day of the month.
*
* </table>
*
@@ -1679,45 +1680,45 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="composites">
*
- * <tr><td valign="top"><tt>'R'</tt>
+ * <tr><td valign="top">{@code 'R'}
* <td valign="top"> <tt>'&#92;u0052'</tt>
- * <td> Time formatted for the 24-hour clock as <tt>"%tH:%tM"</tt>
+ * <td> Time formatted for the 24-hour clock as {@code "%tH:%tM"}
*
- * <tr><td valign="top"><tt>'T'</tt>
+ * <tr><td valign="top">{@code 'T'}
* <td valign="top"> <tt>'&#92;u0054'</tt>
- * <td> Time formatted for the 24-hour clock as <tt>"%tH:%tM:%tS"</tt>.
+ * <td> Time formatted for the 24-hour clock as {@code "%tH:%tM:%tS"}.
*
- * <tr><td valign="top"><tt>'r'</tt>
+ * <tr><td valign="top">{@code 'r'}
* <td valign="top"> <tt>'&#92;u0072'</tt>
- * <td> Time formatted for the 12-hour clock as <tt>"%tI:%tM:%tS
- * %Tp"</tt>. The location of the morning or afternoon marker
- * (<tt>'%Tp'</tt>) may be locale-dependent.
+ * <td> Time formatted for the 12-hour clock as {@code "%tI:%tM:%tS
+ * %Tp"}. The location of the morning or afternoon marker
+ * ({@code '%Tp'}) may be locale-dependent.
*
- * <tr><td valign="top"><tt>'D'</tt>
+ * <tr><td valign="top">{@code 'D'}
* <td valign="top"> <tt>'&#92;u0044'</tt>
- * <td> Date formatted as <tt>"%tm/%td/%ty"</tt>.
+ * <td> Date formatted as {@code "%tm/%td/%ty"}.
*
- * <tr><td valign="top"><tt>'F'</tt>
+ * <tr><td valign="top">{@code 'F'}
* <td valign="top"> <tt>'&#92;u0046'</tt>
* <td> <a href="http://www.w3.org/TR/NOTE-datetime">ISO&nbsp;8601</a>
- * complete date formatted as <tt>"%tY-%tm-%td"</tt>.
+ * complete date formatted as {@code "%tY-%tm-%td"}.
*
- * <tr><td valign="top"><tt>'c'</tt>
+ * <tr><td valign="top">{@code 'c'}
* <td valign="top"> <tt>'&#92;u0063'</tt>
- * <td> Date and time formatted as <tt>"%ta %tb %td %tT %tZ %tY"</tt>,
- * e.g. <tt>"Sun Jul 20 16:17:00 EDT 1969"</tt>.
+ * <td> Date and time formatted as {@code "%ta %tb %td %tT %tZ %tY"},
+ * e.g. {@code "Sun Jul 20 16:17:00 EDT 1969"}.
*
* </table>
*
- * <p> The <tt>'-'</tt> flag defined for <a href="#dFlags">General
- * conversions</a> applies. If the <tt>'#'</tt> flag is given, then a {@link
+ * <p> The {@code '-'} flag defined for <a href="#dFlags">General
+ * conversions</a> applies. If the {@code '#'} flag is given, then a {@link
* FormatFlagsConversionMismatchException} will be thrown.
*
* <p> The <a name="dtWidth">width</a> is the minimum number of characters to
* be written to the output. If the length of the converted value is less than
- * the <tt>width</tt> then the output will be padded by spaces
+ * the {@code width} then the output will be padded by spaces
* (<tt>'&#92;u0020'</tt>) until the total number of characters equals width.
- * The padding is on the left by default. If the <tt>'-'</tt> flag is given
+ * The padding is on the left by default. If the {@code '-'} flag is given
* then the padding will be on the right. If width is not specified then there
* is no minimum.
*
@@ -1730,17 +1731,17 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="DTConv">
*
- * <tr><td valign="top"><tt>'%'</tt>
- * <td> The result is a literal <tt>'%'</tt> (<tt>'&#92;u0025'</tt>)
+ * <tr><td valign="top">{@code '%'}
+ * <td> The result is a literal {@code '%'} (<tt>'&#92;u0025'</tt>)
*
* <p> The <a name="dtWidth">width</a> is the minimum number of characters to
- * be written to the output including the <tt>'%'</tt>. If the length of the
- * converted value is less than the <tt>width</tt> then the output will be
+ * be written to the output including the {@code '%'}. If the length of the
+ * converted value is less than the {@code width} then the output will be
* padded by spaces (<tt>'&#92;u0020'</tt>) until the total number of
* characters equals width. The padding is on the left. If width is not
- * specified then just the <tt>'%'</tt> is output.
+ * specified then just the {@code '%'} is output.
*
- * <p> The <tt>'-'</tt> flag defined for <a href="#dFlags">General
+ * <p> The {@code '-'} flag defined for <a href="#dFlags">General
* conversions</a> applies. If any other flags are provided, then a
* {@link FormatFlagsConversionMismatchException} will be thrown.
*
@@ -1755,7 +1756,7 @@ import sun.misc.FormattedFloatingDecimal;
*
* <table cellpadding=5 summary="DTConv">
*
- * <tr><td valign="top"><tt>'n'</tt>
+ * <tr><td valign="top">{@code 'n'}
* <td> the platform-specific line separator as returned by {@link
* System#getProperty System.getProperty("line.separator")}.
*
@@ -1774,7 +1775,7 @@ import sun.misc.FormattedFloatingDecimal;
* <li> <i>Explicit indexing</i> is used when the format specifier contains an
* argument index. The argument index is a decimal integer indicating the
* position of the argument in the argument list. The first argument is
- * referenced by "<tt>1$</tt>", the second by "<tt>2$</tt>", etc. An argument
+ * referenced by "{@code 1$}", the second by "{@code 2$}", etc. An argument
* may be referenced more than once.
*
* <p> For example:
@@ -1786,7 +1787,7 @@ import sun.misc.FormattedFloatingDecimal;
* </pre></blockquote>
*
* <li> <i>Relative indexing</i> is used when the format specifier contains a
- * <tt>'&lt;'</tt> (<tt>'&#92;u003c'</tt>) flag which causes the argument for
+ * {@code '<'} (<tt>'&#92;u003c'</tt>) flag which causes the argument for
* the previous format specifier to be re-used. If there is no previous
* argument, then a {@link MissingFormatArgumentException} is thrown.
*
@@ -1797,7 +1798,7 @@ import sun.misc.FormattedFloatingDecimal;
* </pre></blockquote>
*
* <li> <i>Ordinary indexing</i> is used when the format specifier contains
- * neither an argument index nor a <tt>'&lt;'</tt> flag. Each format specifier
+ * neither an argument index nor a {@code '<'} flag. Each format specifier
* which uses ordinary indexing is assigned a sequential implicit index into
* argument list which is independent of the indices used by explicit or
* relative indexing.
@@ -1827,7 +1828,7 @@ import sun.misc.FormattedFloatingDecimal;
* <p> If there are more arguments than format specifiers, the extra arguments
* are ignored.
*
- * <p> Unless otherwise specified, passing a <tt>null</tt> argument to any
+ * <p> Unless otherwise specified, passing a {@code null} argument to any
* method or constructor in this class will cause a {@link
* NullPointerException} to be thrown.
*
@@ -1875,8 +1876,8 @@ public final class Formatter implements Closeable, Flushable {
* locale} for this instance of the Java virtual machine.
*
* @param a
- * Destination for the formatted output. If <tt>a</tt> is
- * <tt>null</tt> then a {@link StringBuilder} will be created.
+ * Destination for the formatted output. If {@code a} is
+ * {@code null} then a {@link StringBuilder} will be created.
*/
public Formatter(Appendable a) {
if (a == null)
@@ -1894,7 +1895,7 @@ public final class Formatter implements Closeable, Flushable {
*
* @param l
* The {@linkplain java.util.Locale locale} to apply during
- * formatting. If <tt>l</tt> is <tt>null</tt> then no localization
+ * formatting. If {@code l} is {@code null} then no localization
* is applied.
*/
public Formatter(Locale l) {
@@ -1905,12 +1906,12 @@ public final class Formatter implements Closeable, Flushable {
* Constructs a new formatter with the specified destination and locale.
*
* @param a
- * Destination for the formatted output. If <tt>a</tt> is
- * <tt>null</tt> then a {@link StringBuilder} will be created.
+ * Destination for the formatted output. If {@code a} is
+ * {@code null} then a {@link StringBuilder} will be created.
*
* @param l
* The {@linkplain java.util.Locale locale} to apply during
- * formatting. If <tt>l</tt> is <tt>null</tt> then no localization
+ * formatting. If {@code l} is {@code null} then no localization
* is applied.
*/
public Formatter(Appendable a, Locale l) {
@@ -2003,7 +2004,7 @@ public final class Formatter implements Closeable, Flushable {
*
* @param l
* The {@linkplain java.util.Locale locale} to apply during
- * formatting. If <tt>l</tt> is <tt>null</tt> then no localization
+ * formatting. If {@code l} is {@code null} then no localization
* is applied.
*
* @throws FileNotFoundException
@@ -2111,7 +2112,7 @@ public final class Formatter implements Closeable, Flushable {
*
* @param l
* The {@linkplain java.util.Locale locale} to apply during
- * formatting. If <tt>l</tt> is <tt>null</tt> then no localization
+ * formatting. If {@code l} is {@code null} then no localization
* is applied.
*
* @throws FileNotFoundException
@@ -2211,7 +2212,7 @@ public final class Formatter implements Closeable, Flushable {
*
* @param l
* The {@linkplain java.util.Locale locale} to apply during
- * formatting. If <tt>l</tt> is <tt>null</tt> then no localization
+ * formatting. If {@code l} is {@code null} then no localization
* is applied.
*
* @throws UnsupportedEncodingException
@@ -2236,7 +2237,7 @@ public final class Formatter implements Closeable, Flushable {
* <p> The {@link #format(java.util.Locale,String,Object...) format} method
* for this object which has a locale argument does not change this value.
*
- * @return <tt>null</tt> if no localization is applied, otherwise a
+ * @return {@code null} if no localization is applied, otherwise a
* locale
*
* @throws FormatterClosedException
@@ -2263,7 +2264,7 @@ public final class Formatter implements Closeable, Flushable {
}
/**
- * Returns the result of invoking <tt>toString()</tt> on the destination
+ * Returns the result of invoking {@code toString()} on the destination
* for the output. For example, the following code formats text into a
* {@link StringBuilder} then retrieves the resultant string:
*
@@ -2280,13 +2281,13 @@ public final class Formatter implements Closeable, Flushable {
* <pre>
* out().toString() </pre>
*
- * <p> Depending on the specification of <tt>toString</tt> for the {@link
+ * <p> Depending on the specification of {@code toString} for the {@link
* Appendable}, the returned string may or may not contain the characters
* written to the destination. For instance, buffers typically return
- * their contents in <tt>toString()</tt>, but streams cannot since the
+ * their contents in {@code toString()}, but streams cannot since the
* data is discarded.
*
- * @return The result of invoking <tt>toString()</tt> on the destination
+ * @return The result of invoking {@code toString()} on the destination
* for the output
*
* @throws FormatterClosedException
@@ -2300,7 +2301,7 @@ public final class Formatter implements Closeable, Flushable {
/**
* Flushes this formatter. If the destination implements the {@link
- * java.io.Flushable} interface, its <tt>flush</tt> method will be invoked.
+ * java.io.Flushable} interface, its {@code flush} method will be invoked.
*
* <p> Flushing a formatter writes any buffered output in the destination
* to the underlying stream.
@@ -2322,7 +2323,7 @@ public final class Formatter implements Closeable, Flushable {
/**
* Closes this formatter. If the destination implements the {@link
- * java.io.Closeable} interface, its <tt>close</tt> method will be invoked.
+ * java.io.Closeable} interface, its {@code close} method will be invoked.
*
* <p> Closing a formatter allows it to release resources it may be holding
* (such as open files). If the formatter is already closed, then invoking
@@ -2351,13 +2352,13 @@ public final class Formatter implements Closeable, Flushable {
}
/**
- * Returns the <tt>IOException</tt> last thrown by this formatter's {@link
+ * Returns the {@code IOException} last thrown by this formatter's {@link
* Appendable}.
*
- * <p> If the destination's <tt>append()</tt> method never throws
- * <tt>IOException</tt>, then this method will always return <tt>null</tt>.
+ * <p> If the destination's {@code append()} method never throws
+ * {@code IOException}, then this method will always return {@code null}.
*
- * @return The last exception thrown by the Appendable or <tt>null</tt> if
+ * @return The last exception thrown by the Appendable or {@code null} if
* no such exception exists.
*/
public IOException ioException() {
@@ -2405,7 +2406,7 @@ public final class Formatter implements Closeable, Flushable {
*
* @param l
* The {@linkplain java.util.Locale locale} to apply during
- * formatting. If <tt>l</tt> is <tt>null</tt> then no localization
+ * formatting. If {@code l} is {@code null} then no localization
* is applied. This does not change this object's locale that was
* set during construction.
*
@@ -3569,15 +3570,23 @@ public final class Formatter implements Closeable, Flushable {
// Create a new BigDecimal with the desired precision.
int prec = (precision == -1 ? 6 : precision);
int scale = value.scale();
- int compPrec = value.precision();
- if (scale > prec)
- compPrec -= (scale - prec);
- MathContext mc = new MathContext(compPrec);
- BigDecimal v
- = new BigDecimal(value.unscaledValue(), scale, mc);
- BigDecimalLayout bdl
- = new BigDecimalLayout(v.unscaledValue(), v.scale(),
+ if (scale > prec) {
+ // more "scale" digits than the requested "precision
+ int compPrec = value.precision();
+ if (compPrec <= scale) {
+ // case of 0.xxxxxx
+ value = value.setScale(prec, RoundingMode.HALF_UP);
+ } else {
+ compPrec -= (scale - prec);
+ value = new BigDecimal(value.unscaledValue(),
+ scale,
+ new MathContext(compPrec));
+ }
+ }
+ BigDecimalLayout bdl = new BigDecimalLayout(
+ value.unscaledValue(),
+ value.scale(),
BigDecimalLayoutForm.DECIMAL_FLOAT);
char mant[] = bdl.mantissa();
@@ -4187,7 +4196,7 @@ public final class Formatter implements Closeable, Flushable {
}
}
- // Returns a string representation of the current <tt>Flags</tt>.
+ // Returns a string representation of the current {@code Flags}.
public static String toString(Flags f) {
return f.toString();
}
diff --git a/src/share/classes/java/util/LocaleISOData.java b/src/share/classes/java/util/LocaleISOData.java
index 2dc1e37e9..c7eca492b 100644
--- a/src/share/classes/java/util/LocaleISOData.java
+++ b/src/share/classes/java/util/LocaleISOData.java
@@ -250,6 +250,7 @@ class LocaleISOData {
+ "BH" + "BHR" // Bahrain, Kingdom of
+ "BI" + "BDI" // Burundi, Republic of
+ "BJ" + "BEN" // Benin, People's Republic of
+ + "BL" + "BLM" // Saint Barth\u00e9lemy
+ "BM" + "BMU" // Bermuda
+ "BN" + "BRN" // Brunei Darussalam
+ "BO" + "BOL" // Bolivia, Republic of
@@ -273,7 +274,7 @@ class LocaleISOData {
+ "CN" + "CHN" // China, People's Republic of
+ "CO" + "COL" // Colombia, Republic of
+ "CR" + "CRI" // Costa Rica, Republic of
- + "CS" + "SCG" // Serbia and Montenegro
+// + "CS" + "SCG" // Serbia and Montenegro
+ "CU" + "CUB" // Cuba, Republic of
+ "CV" + "CPV" // Cape Verde, Republic of
+ "CX" + "CXR" // Christmas Island
@@ -363,6 +364,7 @@ class LocaleISOData {
+ "MC" + "MCO" // Monaco, Principality of
+ "MD" + "MDA" // Moldova, Republic of
+ "ME" + "MNE" // Montenegro, Republic of
+ + "MF" + "MAF" // Saint Martin
+ "MG" + "MDG" // Madagascar, Republic of
+ "MH" + "MHL" // Marshall Islands
+ "MK" + "MKD" // Macedonia, the former Yugoslav Republic of
diff --git a/src/share/classes/java/util/Random.java b/src/share/classes/java/util/Random.java
index 26cc4e234..cfa482481 100644
--- a/src/share/classes/java/util/Random.java
+++ b/src/share/classes/java/util/Random.java
@@ -32,7 +32,7 @@ import sun.misc.Unsafe;
* An instance of this class is used to generate a stream of
* pseudorandom numbers. The class uses a 48-bit seed, which is
* modified using a linear congruential formula. (See Donald Knuth,
- * <i>The Art of Computer Programming, Volume 3</i>, Section 3.2.1.)
+ * <i>The Art of Computer Programming, Volume 2</i>, Section 3.2.1.)
* <p>
* If two instances of {@code Random} are created with the same
* seed, and the same sequence of method calls is made for each, they
diff --git a/src/share/classes/java/util/Scanner.java b/src/share/classes/java/util/Scanner.java
index 8fd828fff..3e3806d1f 100644
--- a/src/share/classes/java/util/Scanner.java
+++ b/src/share/classes/java/util/Scanner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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,6 +25,7 @@
package java.util;
+import java.nio.file.FileRef;
import java.util.regex.*;
import java.io.*;
import java.math.*;
@@ -673,6 +674,49 @@ public final class Scanner implements Iterator<String> {
}
/**
+ * {@note new}
+ * Constructs a new <code>Scanner</code> that produces values scanned
+ * from the specified file. Bytes from the file are converted into
+ * characters using the underlying platform's
+ * {@linkplain java.nio.charset.Charset#defaultCharset() default charset}.
+ *
+ * @param source
+ * A file to be scanned
+ * @throws IOException
+ * if an I/O error occurs opening source
+ *
+ * @since 1.7
+ */
+ public Scanner(FileRef source)
+ throws IOException
+ {
+ this(source.newByteChannel());
+ }
+
+ /**
+ * {@note new}
+ * Constructs a new <code>Scanner</code> that produces values scanned
+ * from the specified file. Bytes from the file are converted into
+ * characters using the specified charset.
+ *
+ * @param source
+ * A file to be scanned
+ * @param charsetName
+ * The encoding type used to convert bytes from the file
+ * into characters to be scanned
+ * @throws IOException
+ * if an I/O error occurs opening source
+ * @throws IllegalArgumentException
+ * if the specified encoding is not found
+ * @since 1.7
+ */
+ public Scanner(FileRef source, String charsetName)
+ throws IOException
+ {
+ this(source.newByteChannel(), charsetName);
+ }
+
+ /**
* Constructs a new <code>Scanner</code> that produces values scanned
* from the specified string.
*
diff --git a/src/share/classes/java/util/logging/Logger.java b/src/share/classes/java/util/logging/Logger.java
index 155db02d9..28f942a0f 100644
--- a/src/share/classes/java/util/logging/Logger.java
+++ b/src/share/classes/java/util/logging/Logger.java
@@ -27,6 +27,7 @@
package java.util.logging;
import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.security.*;
import java.lang.ref.WeakReference;
@@ -165,10 +166,11 @@ public class Logger {
private static final int offValue = Level.OFF.intValue();
private LogManager manager;
private String name;
- private ArrayList<Handler> handlers;
+ private final CopyOnWriteArrayList<Handler> handlers =
+ new CopyOnWriteArrayList<Handler>();
private String resourceBundleName;
- private boolean useParentHandlers = true;
- private Filter filter;
+ private volatile boolean useParentHandlers = true;
+ private volatile Filter filter;
private boolean anonymous;
private ResourceBundle catalog; // Cached resource bundle
@@ -180,9 +182,9 @@ public class Logger {
private static Object treeLock = new Object();
// We keep weak references from parents to children, but strong
// references from children to parents.
- private Logger parent; // our nearest parent.
+ private volatile Logger parent; // our nearest parent.
private ArrayList<WeakReference<Logger>> kids; // WeakReferences to loggers that have us as parent
- private Level levelObject;
+ private volatile Level levelObject;
private volatile int levelValue; // current effective level value
/**
@@ -438,7 +440,7 @@ public class Logger {
* @exception SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*/
- public synchronized void setFilter(Filter newFilter) throws SecurityException {
+ public void setFilter(Filter newFilter) throws SecurityException {
checkAccess();
filter = newFilter;
}
@@ -448,7 +450,7 @@ public class Logger {
*
* @return a filter object (may be null)
*/
- public synchronized Filter getFilter() {
+ public Filter getFilter() {
return filter;
}
@@ -465,10 +467,9 @@ public class Logger {
if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
return;
}
- synchronized (this) {
- if (filter != null && !filter.isLoggable(record)) {
- return;
- }
+ Filter theFilter = filter;
+ if (theFilter != null && !theFilter.isLoggable(record)) {
+ return;
}
// Post the LogRecord to all our Handlers, and then to
@@ -476,12 +477,8 @@ public class Logger {
Logger logger = this;
while (logger != null) {
- Handler targets[] = logger.getHandlers();
-
- if (targets != null) {
- for (int i = 0; i < targets.length; i++) {
- targets[i].publish(record);
- }
+ for (Handler handler : logger.handlers) {
+ handler.publish(record);
}
if (!logger.getUseParentHandlers()) {
@@ -1182,13 +1179,10 @@ public class Logger {
* @exception SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*/
- public synchronized void addHandler(Handler handler) throws SecurityException {
+ public void addHandler(Handler handler) throws SecurityException {
// Check for null handler
handler.getClass();
checkAccess();
- if (handlers == null) {
- handlers = new ArrayList<Handler>();
- }
handlers.add(handler);
}
@@ -1201,14 +1195,11 @@ public class Logger {
* @exception SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*/
- public synchronized void removeHandler(Handler handler) throws SecurityException {
+ public void removeHandler(Handler handler) throws SecurityException {
checkAccess();
if (handler == null) {
return;
}
- if (handlers == null) {
- return;
- }
handlers.remove(handler);
}
@@ -1217,11 +1208,8 @@ public class Logger {
* <p>
* @return an array of all registered Handlers
*/
- public synchronized Handler[] getHandlers() {
- if (handlers == null) {
- return emptyHandlers;
- }
- return handlers.toArray(new Handler[handlers.size()]);
+ public Handler[] getHandlers() {
+ return handlers.toArray(emptyHandlers);
}
/**
@@ -1235,7 +1223,7 @@ public class Logger {
* @exception SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*/
- public synchronized void setUseParentHandlers(boolean useParentHandlers) {
+ public void setUseParentHandlers(boolean useParentHandlers) {
checkAccess();
this.useParentHandlers = useParentHandlers;
}
@@ -1246,7 +1234,7 @@ public class Logger {
*
* @return true if output is to be sent to the logger's parent
*/
- public synchronized boolean getUseParentHandlers() {
+ public boolean getUseParentHandlers() {
return useParentHandlers;
}
@@ -1354,9 +1342,12 @@ public class Logger {
* @return nearest existing parent Logger
*/
public Logger getParent() {
- synchronized (treeLock) {
- return parent;
- }
+ // Note: this used to be synchronized on treeLock. However, this only
+ // provided memory semantics, as there was no guarantee that the caller
+ // would synchronize on treeLock (in fact, there is no way for external
+ // callers to so synchronize). Therefore, we have made parent volatile
+ // instead.
+ return parent;
}
/**
diff --git a/src/share/classes/java/util/regex/Matcher.java b/src/share/classes/java/util/regex/Matcher.java
index ee7931688..61b75ebc7 100644
--- a/src/share/classes/java/util/regex/Matcher.java
+++ b/src/share/classes/java/util/regex/Matcher.java
@@ -491,6 +491,45 @@ public final class Matcher implements MatchResult {
}
/**
+ * Returns the input subsequence captured by the given
+ * <a href="Pattern.html#groupname">named-capturing group</a> during the previous
+ * match operation.
+ *
+ * <p> If the match was successful but the group specified failed to match
+ * any part of the input sequence, then <tt>null</tt> is returned. Note
+ * that some groups, for example <tt>(a*)</tt>, match the empty string.
+ * This method will return the empty string when such a group successfully
+ * matches the empty string in the input. </p>
+ *
+ * @param name
+ * The name of a named-capturing group in this matcher's pattern
+ *
+ * @return The (possibly empty) subsequence captured by the named group
+ * during the previous match, or <tt>null</tt> if the group
+ * failed to match part of the input
+ *
+ * @throws IllegalStateException
+ * If no match has yet been attempted,
+ * or if the previous match operation failed
+ *
+ * @throws IllegalArgumentException
+ * If there is no capturing group in the pattern
+ * with the given name
+ */
+ public String group(String name) {
+ if (name == null)
+ throw new NullPointerException("Null group name");
+ if (first < 0)
+ throw new IllegalStateException("No match found");
+ if (!parentPattern.namedGroups().containsKey(name))
+ throw new IllegalArgumentException("No group with name <" + name + ">");
+ int group = parentPattern.namedGroups().get(name);
+ if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
+ return null;
+ return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
+ }
+
+ /**
* Returns the number of capturing groups in this matcher's pattern.
*
* <p> Group zero denotes the entire pattern by convention. It is not
@@ -649,9 +688,11 @@ public final class Matcher implements MatchResult {
*
* <p> The replacement string may contain references to subsequences
* captured during the previous match: Each occurrence of
- * <tt>$</tt><i>g</i><tt></tt> will be replaced by the result of
- * evaluating {@link #group(int) group}<tt>(</tt><i>g</i><tt>)</tt>.
- * The first number after the <tt>$</tt> is always treated as part of
+ * <tt>$</tt>&lt;<i>name</i>&gt; or <tt>$</tt><i>g</i>
+ * will be replaced by the result of evaluating the corresponding
+ * {@link #group(String) group(name)} or {@link #group(int) group(g)</tt>}
+ * respectively. For <tt>$</tt><i>g</i><tt></tt>,
+ * the first number after the <tt>$</tt> is always treated as part of
* the group reference. Subsequent numbers are incorporated into g if
* they would form a legal group reference. Only the numerals '0'
* through '9' are considered as potential components of the group
@@ -695,6 +736,10 @@ public final class Matcher implements MatchResult {
* If no match has yet been attempted,
* or if the previous match operation failed
*
+ * @throws IllegalArgumentException
+ * If the replacement string refers to a named-capturing
+ * group that does not exist in the pattern
+ *
* @throws IndexOutOfBoundsException
* If the replacement string refers to a capturing group
* that does not exist in the pattern
@@ -719,29 +764,62 @@ public final class Matcher implements MatchResult {
} else if (nextChar == '$') {
// Skip past $
cursor++;
- // The first number is always a group
- int refNum = (int)replacement.charAt(cursor) - '0';
- if ((refNum < 0)||(refNum > 9))
- throw new IllegalArgumentException(
- "Illegal group reference");
- cursor++;
-
- // Capture the largest legal group string
- boolean done = false;
- while (!done) {
- if (cursor >= replacement.length()) {
- break;
- }
- int nextDigit = replacement.charAt(cursor) - '0';
- if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
- break;
+ // A StringIndexOutOfBoundsException is thrown if
+ // this "$" is the last character in replacement
+ // string in current implementation, a IAE might be
+ // more appropriate.
+ nextChar = replacement.charAt(cursor);
+ int refNum = -1;
+ if (nextChar == '<') {
+ cursor++;
+ StringBuilder gsb = new StringBuilder();
+ while (cursor < replacement.length()) {
+ nextChar = replacement.charAt(cursor);
+ if (ASCII.isLower(nextChar) ||
+ ASCII.isUpper(nextChar) ||
+ ASCII.isDigit(nextChar)) {
+ gsb.append(nextChar);
+ cursor++;
+ } else {
+ break;
+ }
}
- int newRefNum = (refNum * 10) + nextDigit;
- if (groupCount() < newRefNum) {
- done = true;
- } else {
- refNum = newRefNum;
- cursor++;
+ if (gsb.length() == 0)
+ throw new IllegalArgumentException(
+ "named capturing group has 0 length name");
+ if (nextChar != '>')
+ throw new IllegalArgumentException(
+ "named capturing group is missing trailing '>'");
+ String gname = gsb.toString();
+ if (!parentPattern.namedGroups().containsKey(gname))
+ throw new IllegalArgumentException(
+ "No group with name <" + gname + ">");
+ refNum = parentPattern.namedGroups().get(gname);
+ cursor++;
+ } else {
+ // The first number is always a group
+ refNum = (int)nextChar - '0';
+ if ((refNum < 0)||(refNum > 9))
+ throw new IllegalArgumentException(
+ "Illegal group reference");
+ cursor++;
+ // Capture the largest legal group string
+ boolean done = false;
+ while (!done) {
+ if (cursor >= replacement.length()) {
+ break;
+ }
+ int nextDigit = replacement.charAt(cursor) - '0';
+ if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
+ break;
+ }
+ int newRefNum = (refNum * 10) + nextDigit;
+ if (groupCount() < newRefNum) {
+ done = true;
+ } else {
+ refNum = newRefNum;
+ cursor++;
+ }
}
}
// Append group
diff --git a/src/share/classes/java/util/regex/Pattern.java b/src/share/classes/java/util/regex/Pattern.java
index 2458a855e..919c1bf3c 100644
--- a/src/share/classes/java/util/regex/Pattern.java
+++ b/src/share/classes/java/util/regex/Pattern.java
@@ -29,6 +29,7 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.CharacterIterator;
import java.text.Normalizer;
+import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Arrays;
@@ -298,6 +299,10 @@ import java.util.Arrays;
* <td valign="bottom" headers="matches">Whatever the <i>n</i><sup>th</sup>
* <a href="#cg">capturing group</a> matched</td></tr>
*
+ * <tr><td valign="bottom" headers="construct backref"><tt>\</tt><i>k</i>&lt;<i>name</i>&gt;</td>
+ * <td valign="bottom" headers="matches">Whatever the
+ * <a href="#groupname">named-capturing group</a> "name" matched</td></tr>
+ *
* <tr><th>&nbsp;</th></tr>
* <tr align="left"><th colspan="2" id="quot">Quotation</th></tr>
*
@@ -310,8 +315,10 @@ import java.util.Arrays;
* <!-- Metachars: !$()*+.<>?[\]^{|} -->
*
* <tr><th>&nbsp;</th></tr>
- * <tr align="left"><th colspan="2" id="special">Special constructs (non-capturing)</th></tr>
+ * <tr align="left"><th colspan="2" id="special">Special constructs (named-capturing and non-capturing)</th></tr>
*
+ * <tr><td valign="top" headers="construct special"><tt>(?&lt;<a href="#groupname">name</a>&gt;</tt><i>X</i><tt>)</tt></td>
+ * <td headers="matches"><i>X</i>, as a named-capturing group</td></tr>
* <tr><td valign="top" headers="construct special"><tt>(?:</tt><i>X</i><tt>)</tt></td>
* <td headers="matches"><i>X</i>, as a non-capturing group</td></tr>
* <tr><td valign="top" headers="construct special"><tt>(?idmsux-idmsux)&nbsp;</tt></td>
@@ -449,6 +456,8 @@ import java.util.Arrays;
* <a name="cg">
* <h4> Groups and capturing </h4>
*
+ * <a name="gnumber">
+ * <h5> Group number </h5>
* <p> Capturing groups are numbered by counting their opening parentheses from
* left to right. In the expression <tt>((A)(B(C)))</tt>, for example, there
* are four such groups: </p>
@@ -471,6 +480,24 @@ import java.util.Arrays;
* subsequence may be used later in the expression, via a back reference, and
* may also be retrieved from the matcher once the match operation is complete.
*
+ * <a name="groupname">
+ * <h5> Group name </h5>
+ * <p>A capturing group can also be assigned a "name", a <tt>named-capturing group</tt>,
+ * and then be back-referenced later by the "name". Group names are composed of
+ * the following characters:
+ *
+ * <ul>
+ * <li> The uppercase letters <tt>'A'</tt> through <tt>'Z'</tt>
+ * (<tt>'&#92;u0041'</tt>&nbsp;through&nbsp;<tt>'&#92;u005a'</tt>),
+ * <li> The lowercase letters <tt>'a'</tt> through <tt>'z'</tt>
+ * (<tt>'&#92;u0061'</tt>&nbsp;through&nbsp;<tt>'&#92;u007a'</tt>),
+ * <li> The digits <tt>'0'</tt> through <tt>'9'</tt>
+ * (<tt>'&#92;u0030'</tt>&nbsp;through&nbsp;<tt>'&#92;u0039'</tt>),
+ * </ul>
+ *
+ * <p> A <tt>named-capturing group</tt> is still numbered as described in
+ * <a href="#gnumber">Group number</a>.
+ *
* <p> The captured input associated with a group is always the subsequence
* that the group most recently matched. If a group is evaluated a second time
* because of quantification then its previously-captured value, if any, will
@@ -479,9 +506,9 @@ import java.util.Arrays;
* group two set to <tt>"b"</tt>. All captured input is discarded at the
* beginning of each match.
*
- * <p> Groups beginning with <tt>(?</tt> are pure, <i>non-capturing</i> groups
- * that do not capture text and do not count towards the group total.
- *
+ * <p> Groups beginning with <tt>(?</tt> are either pure, <i>non-capturing</i> groups
+ * that do not capture text and do not count towards the group total, or
+ * <i>named-capturing</i> group.
*
* <h4> Unicode support </h4>
*
@@ -795,6 +822,12 @@ public final class Pattern
transient int[] buffer;
/**
+ * Map the "name" of the "named capturing group" to its group id
+ * node.
+ */
+ transient volatile Map<String, Integer> namedGroups;
+
+ /**
* Temporary storage used while parsing group references.
*/
transient GroupHead[] groupNodes;
@@ -1467,6 +1500,7 @@ loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
// Allocate all temporary objects here.
buffer = new int[32];
groupNodes = new GroupHead[10];
+ namedGroups = null;
if (has(LITERAL)) {
// Literal pattern handling
@@ -1505,6 +1539,12 @@ loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
compiled = true;
}
+ Map<String, Integer> namedGroups() {
+ if (namedGroups == null)
+ namedGroups = new HashMap<String, Integer>(2);
+ return namedGroups;
+ }
+
/**
* Used to print out a subtree of the Pattern to help with debugging.
*/
@@ -2156,7 +2196,22 @@ loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
case 'h':
case 'i':
case 'j':
+ break;
case 'k':
+ if (inclass)
+ break;
+ if (read() != '<')
+ throw error("\\k is not followed by '<' for named capturing group");
+ String name = groupname(read());
+ if (!namedGroups().containsKey(name))
+ throw error("(named capturing group <"+ name+"> does not exit");
+ if (create) {
+ if (has(CASE_INSENSITIVE))
+ root = new CIBackRef(namedGroups().get(name), has(UNICODE_CASE));
+ else
+ root = new BackRef(namedGroups().get(name));
+ }
+ return -1;
case 'l':
case 'm':
break;
@@ -2456,6 +2511,24 @@ loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
}
/**
+ * Parses and returns the name of a "named capturing group", the trailing
+ * ">" is consumed after parsing.
+ */
+ private String groupname(int ch) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Character.toChars(ch));
+ while (ASCII.isLower(ch=read()) || ASCII.isUpper(ch) ||
+ ASCII.isDigit(ch)) {
+ sb.append(Character.toChars(ch));
+ }
+ if (sb.length() == 0)
+ throw error("named capturing group has 0 length name");
+ if (ch != '>')
+ throw error("named capturing group is missing trailing '>'");
+ return sb.toString();
+ }
+
+ /**
* Parses a group and returns the head node of a set of nodes that process
* the group. Sometimes a double return system is used where the tail is
* returned in root.
@@ -2494,6 +2567,18 @@ loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
break;
case '<': // (?<xxx) look behind
ch = read();
+ if (Character.isLetter(ch)) { // named captured group
+ String name = groupname(ch);
+ if (namedGroups().containsKey(name))
+ throw error("Named capturing group <" + name
+ + "> is already defined");
+ capturingGroup = true;
+ head = createGroup(false);
+ tail = root;
+ namedGroups().put(name, capturingGroupCount-1);
+ head.next = expr(tail);
+ break;
+ }
int start = cursor;
head = createGroup(true);
tail = root;
diff --git a/src/share/classes/javax/swing/ImageIcon.java b/src/share/classes/javax/swing/ImageIcon.java
index 74788f1a8..7f1fd02e0 100644
--- a/src/share/classes/javax/swing/ImageIcon.java
+++ b/src/share/classes/javax/swing/ImageIcon.java
@@ -86,7 +86,7 @@ public class ImageIcon implements Icon, Serializable, Accessible {
static {
component = new Component() {};
- AccessController.doPrivileged(new PrivilegedAction() {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
// 6482575 - clear the appContext field so as not to leak it
diff --git a/src/share/classes/javax/swing/JRootPane.java b/src/share/classes/javax/swing/JRootPane.java
index c9696246e..a210c3791 100644
--- a/src/share/classes/javax/swing/JRootPane.java
+++ b/src/share/classes/javax/swing/JRootPane.java
@@ -34,6 +34,7 @@ import javax.swing.plaf.RootPaneUI;
import java.util.Vector;
import java.io.Serializable;
import javax.swing.border.*;
+import sun.awt.AWTAccessor;
import sun.security.action.GetBooleanAction;
@@ -688,6 +689,9 @@ public class JRootPane extends JComponent implements Accessible {
throw new NullPointerException("glassPane cannot be set to null.");
}
+ AWTAccessor.getComponentAccessor().setMixingCutoutShape(glass,
+ new Rectangle());
+
boolean visible = false;
if (glassPane != null && glassPane.getParent() == this) {
this.remove(glassPane);
diff --git a/src/share/classes/javax/swing/ProgressMonitor.java b/src/share/classes/javax/swing/ProgressMonitor.java
index 36366df17..73a2b656b 100644
--- a/src/share/classes/javax/swing/ProgressMonitor.java
+++ b/src/share/classes/javax/swing/ProgressMonitor.java
@@ -78,7 +78,7 @@ import javax.swing.text.*;
* @author James Gosling
* @author Lynn Monsanto (accessibility)
*/
-public class ProgressMonitor extends Object implements Accessible
+public class ProgressMonitor implements Accessible
{
private ProgressMonitor root;
private JDialog dialog;
@@ -186,7 +186,7 @@ public class ProgressMonitor extends Object implements Accessible
}
if (window instanceof SwingUtilities.SharedOwnerFrame) {
WindowListener ownerShutdownListener =
- (WindowListener)SwingUtilities.getSharedOwnerFrameShutdownListener();
+ SwingUtilities.getSharedOwnerFrameShutdownListener();
dialog.addWindowListener(ownerShutdownListener);
}
Container contentPane = dialog.getContentPane();
@@ -273,7 +273,7 @@ public class ProgressMonitor extends Object implements Accessible
if (dT >= millisToDecideToPopup) {
int predictedCompletionTime;
if (nv > min) {
- predictedCompletionTime = (int)((long)dT *
+ predictedCompletionTime = (int)(dT *
(max - min) /
(nv - min));
}
@@ -691,10 +691,7 @@ public class ProgressMonitor extends Object implements Accessible
* object does not have an Accessible parent
*/
public Accessible getAccessibleParent() {
- if (dialog != null) {
- return (Accessible)dialog;
- }
- return null;
+ return dialog;
}
/*
@@ -768,7 +765,7 @@ public class ProgressMonitor extends Object implements Accessible
if (myBar != null) {
Component c = myBar.getParent();
if (c instanceof Accessible) {
- return ((Accessible)c).getAccessibleContext();
+ return c.getAccessibleContext();
}
}
return null;
diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java
index f051acdc6..f51bf4ec2 100644
--- a/src/share/classes/javax/swing/RepaintManager.java
+++ b/src/share/classes/javax/swing/RepaintManager.java
@@ -1305,9 +1305,12 @@ public class RepaintManager
if (doubleBufferingEnabled && !nativeDoubleBuffering) {
switch (bufferStrategyType) {
case BUFFER_STRATEGY_NOT_SPECIFIED:
- if (((SunToolkit)Toolkit.getDefaultToolkit()).
- useBufferPerWindow()) {
- paintManager = new BufferStrategyPaintManager();
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ if (tk instanceof SunToolkit) {
+ SunToolkit stk = (SunToolkit) tk;
+ if (stk.useBufferPerWindow()) {
+ paintManager = new BufferStrategyPaintManager();
+ }
}
break;
case BUFFER_STRATEGY_SPECIFIED_ON:
@@ -1329,9 +1332,16 @@ public class RepaintManager
private void scheduleProcessingRunnable(AppContext context) {
if (processingRunnable.markPending()) {
- SunToolkit.getSystemEventQueueImplPP(context).
- postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
- processingRunnable));
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ if (tk instanceof SunToolkit) {
+ SunToolkit.getSystemEventQueueImplPP(context).
+ postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
+ processingRunnable));
+ } else {
+ Toolkit.getDefaultToolkit().getSystemEventQueue().
+ postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
+ processingRunnable));
+ }
}
}
diff --git a/src/share/classes/javax/swing/SwingWorker.java b/src/share/classes/javax/swing/SwingWorker.java
index de00ddc38..9eca7d535 100644
--- a/src/share/classes/javax/swing/SwingWorker.java
+++ b/src/share/classes/javax/swing/SwingWorker.java
@@ -282,7 +282,7 @@ public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
* is finished.
*/
DONE
- };
+ }
/**
* Constructs this {@code SwingWorker}.
@@ -825,7 +825,7 @@ public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
}
private static class DoSubmitAccumulativeRunnable
extends AccumulativeRunnable<Runnable> implements ActionListener {
- private final static int DELAY = (int) (1000 / 30);
+ private final static int DELAY = 1000 / 30;
@Override
protected void run(List<Runnable> args) {
for (Runnable runnable : args) {
diff --git a/src/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java b/src/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java
index c9ea9d774..3980a40a6 100644
--- a/src/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java
+++ b/src/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java
@@ -26,6 +26,8 @@
package javax.swing.colorchooser;
import java.awt.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.swing.*;
/**
@@ -47,6 +49,15 @@ import javax.swing.*;
*/
public abstract class AbstractColorChooserPanel extends JPanel {
+ private final PropertyChangeListener enabledListener = new PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ Object value = event.getNewValue();
+ if (value instanceof Boolean) {
+ setEnabled((Boolean) value);
+ }
+ }
+ };
+
/**
*
*/
@@ -142,6 +153,8 @@ public abstract class AbstractColorChooserPanel extends JPanel {
throw new RuntimeException ("This chooser panel is already installed");
}
chooser = enclosingChooser;
+ chooser.addPropertyChangeListener("enabled", enabledListener);
+ setEnabled(chooser.isEnabled());
buildChooser();
updateChooser();
}
@@ -151,6 +164,7 @@ public abstract class AbstractColorChooserPanel extends JPanel {
* If override this, be sure to call <code>super</code>.
*/
public void uninstallChooserPanel(JColorChooser enclosingChooser) {
+ chooser.removePropertyChangeListener("enabled", enabledListener);
chooser = null;
}
diff --git a/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java b/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java
index acd6cdcc3..cc4fa5d53 100644
--- a/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java
+++ b/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java
@@ -26,6 +26,8 @@
package javax.swing.colorchooser;
import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
@@ -57,6 +59,21 @@ final class ColorChooserPanel extends AbstractColorChooserPanel implements Prope
}
@Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ setEnabled(this, enabled);
+ }
+
+ private static void setEnabled(Container container, boolean enabled) {
+ for (Component component : container.getComponents()) {
+ component.setEnabled(enabled);
+ if (component instanceof Container) {
+ setEnabled((Container) component, enabled);
+ }
+ }
+ }
+
+ @Override
public void updateChooser() {
Color color = getColorFromModel();
if (color != null) {
diff --git a/src/share/classes/javax/swing/colorchooser/DefaultColorSelectionModel.java b/src/share/classes/javax/swing/colorchooser/DefaultColorSelectionModel.java
index e3cf261bd..e437dd96d 100644
--- a/src/share/classes/javax/swing/colorchooser/DefaultColorSelectionModel.java
+++ b/src/share/classes/javax/swing/colorchooser/DefaultColorSelectionModel.java
@@ -128,8 +128,7 @@ public class DefaultColorSelectionModel implements ColorSelectionModel, Serializ
* @since 1.4
*/
public ChangeListener[] getChangeListeners() {
- return (ChangeListener[])listenerList.getListeners(
- ChangeListener.class);
+ return listenerList.getListeners(ChangeListener.class);
}
/**
diff --git a/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java b/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java
index 3cbd0df3b..76896a90c 100644
--- a/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java
+++ b/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java
@@ -170,7 +170,6 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel {
superHolder.add(mainHolder, gbc);
gbc.insets = oldInsets;
- recentSwatchPanel.addMouseListener(recentSwatchListener);
recentSwatchPanel.setInheritsPopupMenu(true);
JPanel recentHolder = new JPanel( new BorderLayout() );
recentHolder.setBorder(border);
@@ -212,16 +211,20 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel {
class RecentSwatchListener extends MouseAdapter implements Serializable {
public void mousePressed(MouseEvent e) {
- Color color = recentSwatchPanel.getColorForLocation(e.getX(), e.getY());
- setSelectedColor(color);
+ if (isEnabled()) {
+ Color color = recentSwatchPanel.getColorForLocation(e.getX(), e.getY());
+ setSelectedColor(color);
+ }
}
}
class MainSwatchListener extends MouseAdapter implements Serializable {
public void mousePressed(MouseEvent e) {
- Color color = swatchPanel.getColorForLocation(e.getX(), e.getY());
- setSelectedColor(color);
- recentSwatchPanel.setMostRecentColor(color);
+ if (isEnabled()) {
+ Color color = swatchPanel.getColorForLocation(e.getX(), e.getY());
+ setSelectedColor(color);
+ recentSwatchPanel.setMostRecentColor(color);
+ }
}
}
diff --git a/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java b/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java
index 13d13cdce..44f99d6d3 100644
--- a/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java
+++ b/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java
@@ -1132,13 +1132,18 @@ public class BasicFileChooserUI extends FileChooserUI {
private void changeDirectory(File dir) {
JFileChooser fc = getFileChooser();
// Traverse shortcuts on Windows
- if (dir != null && File.separatorChar == '\\' && dir.getPath().endsWith(".lnk")) {
+ if (dir != null && FilePane.usesShellFolder(fc)) {
try {
- File linkedTo = ShellFolder.getShellFolder(dir).getLinkLocation();
- if (linkedTo != null && fc.isTraversable(linkedTo)) {
- dir = linkedTo;
- } else {
- return;
+ ShellFolder shellFolder = ShellFolder.getShellFolder(dir);
+
+ if (shellFolder.isLink()) {
+ File linkedTo = shellFolder.getLinkLocation();
+
+ if (linkedTo != null && fc.isTraversable(linkedTo)) {
+ dir = linkedTo;
+ } else {
+ return;
+ }
}
} catch (FileNotFoundException ex) {
return;
diff --git a/src/share/classes/javax/swing/plaf/basic/BasicSliderUI.java b/src/share/classes/javax/swing/plaf/basic/BasicSliderUI.java
index 1d16e1d88..864ad1326 100644
--- a/src/share/classes/javax/swing/plaf/basic/BasicSliderUI.java
+++ b/src/share/classes/javax/swing/plaf/basic/BasicSliderUI.java
@@ -836,18 +836,24 @@ public class BasicSliderUI extends SliderUI{
*/
protected Integer getHighestValue() {
Dictionary dictionary = slider.getLabelTable();
- if (dictionary != null) {
- Enumeration keys = dictionary.keys();
- int max = slider.getMinimum() - 1;
- while (keys.hasMoreElements()) {
- max = Math.max(max, ((Integer)keys.nextElement()).intValue());
- }
- if (max == slider.getMinimum() - 1) {
- return null;
+
+ if (dictionary == null) {
+ return null;
+ }
+
+ Enumeration keys = dictionary.keys();
+
+ Integer max = null;
+
+ while (keys.hasMoreElements()) {
+ Integer i = (Integer) keys.nextElement();
+
+ if (max == null || i > max) {
+ max = i;
}
- return max;
}
- return null;
+
+ return max;
}
/**
@@ -859,18 +865,24 @@ public class BasicSliderUI extends SliderUI{
*/
protected Integer getLowestValue() {
Dictionary dictionary = slider.getLabelTable();
- if (dictionary != null) {
- Enumeration keys = dictionary.keys();
- int min = slider.getMaximum() + 1;
- while (keys.hasMoreElements()) {
- min = Math.min(min, ((Integer)keys.nextElement()).intValue());
- }
- if (min == slider.getMaximum() + 1) {
- return null;
+
+ if (dictionary == null) {
+ return null;
+ }
+
+ Enumeration keys = dictionary.keys();
+
+ Integer min = null;
+
+ while (keys.hasMoreElements()) {
+ Integer i = (Integer) keys.nextElement();
+
+ if (min == null || i < min) {
+ min = i;
}
- return min;
}
- return null;
+
+ return min;
}
diff --git a/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java b/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java
index 763dfdc3f..4636e07c4 100644
--- a/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java
+++ b/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java
@@ -68,8 +68,6 @@ public class MetalFileChooserUI extends BasicFileChooserUI {
private JToggleButton listViewButton;
private JToggleButton detailsViewButton;
- private boolean useShellFolder;
-
private JButton approveButton;
private JButton cancelButton;
@@ -204,10 +202,6 @@ public class MetalFileChooserUI extends BasicFileChooserUI {
public ListSelectionListener createListSelectionListener() {
return MetalFileChooserUI.this.createListSelectionListener(getFileChooser());
}
-
- public boolean usesShellFolder() {
- return useShellFolder;
- }
}
public void installComponents(JFileChooser fc) {
@@ -219,8 +213,6 @@ public class MetalFileChooserUI extends BasicFileChooserUI {
filePane = new FilePane(new MetalFileChooserUIAccessor());
fc.addPropertyChangeListener(filePane);
- updateUseShellFolder();
-
// ********************************* //
// **** Construct the top panel **** //
// ********************************* //
@@ -448,19 +440,6 @@ public class MetalFileChooserUI extends BasicFileChooserUI {
groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel });
}
- private void updateUseShellFolder() {
- // Decide whether to use the ShellFolder class to populate shortcut
- // panel and combobox.
- JFileChooser fc = getFileChooser();
- Boolean prop =
- (Boolean)fc.getClientProperty("FileChooser.useShellFolder");
- if (prop != null) {
- useShellFolder = prop.booleanValue();
- } else {
- useShellFolder = fc.getFileSystemView().equals(FileSystemView.getFileSystemView());
- }
- }
-
protected JPanel getButtonPanel() {
if (buttonPanel == null) {
buttonPanel = new JPanel();
@@ -786,7 +765,6 @@ public class MetalFileChooserUI extends BasicFileChooserUI {
cc.applyComponentOrientation(o);
}
} else if (s == "FileChooser.useShellFolder") {
- updateUseShellFolder();
doDirectoryChanged(e);
} else if (s.equals("ancestor")) {
if (e.getOldValue() == null && e.getNewValue() != null) {
@@ -953,6 +931,8 @@ public class MetalFileChooserUI extends BasicFileChooserUI {
return;
}
+ boolean useShellFolder = FilePane.usesShellFolder(chooser);
+
directories.clear();
File[] baseFolders;
diff --git a/src/share/classes/javax/swing/plaf/synth/SynthParser.java b/src/share/classes/javax/swing/plaf/synth/SynthParser.java
index 0ff3307cd..7fe2278fd 100644
--- a/src/share/classes/javax/swing/plaf/synth/SynthParser.java
+++ b/src/share/classes/javax/swing/plaf/synth/SynthParser.java
@@ -59,16 +59,16 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
-import org.xml.sax.AttributeList;
-import org.xml.sax.HandlerBase;
+import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
-import com.sun.beans.ObjectHandler;
+import com.sun.beans.decoder.DocumentHandler;
-class SynthParser extends HandlerBase {
+class SynthParser extends DefaultHandler {
//
// Known element names
//
@@ -119,7 +119,7 @@ class SynthParser extends HandlerBase {
/**
* Lazily created, used for anything we don't understand.
*/
- private ObjectHandler _handler;
+ private DocumentHandler _handler;
/**
* Indicates the depth of how many elements we've encountered but don't
@@ -292,8 +292,9 @@ class SynthParser extends HandlerBase {
/**
* Handles beans persistance.
*/
- private ObjectHandler getHandler() {
+ private DocumentHandler getHandler() {
if (_handler == null) {
+ _handler = new DocumentHandler();
if (_urlResourceBase != null) {
// getHandler() is never called before parse() so it is safe
// to create a URLClassLoader with _resourceBase.
@@ -304,14 +305,13 @@ class SynthParser extends HandlerBase {
URL[] urls = new URL[] { getResource(".") };
ClassLoader parent = Thread.currentThread().getContextClassLoader();
ClassLoader urlLoader = new URLClassLoader(urls, parent);
- _handler = new ObjectHandler(null, urlLoader);
+ _handler.setClassLoader(urlLoader);
} else {
- _handler = new ObjectHandler(null,
- _classResourceBase.getClassLoader());
+ _handler.setClassLoader(_classResourceBase.getClassLoader());
}
for (String key : _mapping.keySet()) {
- _handler.register(key, _mapping.get(key));
+ _handler.setVariable(key, _mapping.get(key));
}
}
return _handler;
@@ -336,8 +336,8 @@ class SynthParser extends HandlerBase {
private Object lookup(String key, Class type) throws SAXException {
Object value;
if (_handler != null) {
- if ((value = _handler.lookup(key)) != null) {
- return checkCast(value, type);
+ if (_handler.hasVariable(key)) {
+ return checkCast(_handler.getVariable(key), type);
}
}
value = _mapping.get(key);
@@ -354,11 +354,11 @@ class SynthParser extends HandlerBase {
private void register(String key, Object value) throws SAXException {
if (key != null) {
if (_mapping.get(key) != null ||
- (_handler != null && _handler.lookup(key) != null)) {
+ (_handler != null && _handler.hasVariable(key))) {
throw new SAXException("ID " + key + " is already defined");
}
if (_handler != null) {
- _handler.register(key, value);
+ _handler.setVariable(key, value);
}
else {
_mapping.put(key, value);
@@ -400,12 +400,12 @@ class SynthParser extends HandlerBase {
// The following methods are invoked from startElement/stopElement
//
- private void startStyle(AttributeList attributes) throws SAXException {
+ private void startStyle(Attributes attributes) throws SAXException {
String id = null;
_style = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_CLONE)) {
_style = (ParsedSynthStyle)((ParsedSynthStyle)lookup(
attributes.getValue(i), ParsedSynthStyle.class)).
@@ -421,7 +421,7 @@ class SynthParser extends HandlerBase {
register(id, _style);
}
- private void endStyle() throws SAXException {
+ private void endStyle() {
int size = _stylePainters.size();
if (size > 0) {
_style.setPainters(_stylePainters.toArray(new ParsedSynthStyle.PainterInfo[size]));
@@ -435,14 +435,14 @@ class SynthParser extends HandlerBase {
_style = null;
}
- private void startState(AttributeList attributes) throws SAXException {
+ private void startState(Attributes attributes) throws SAXException {
ParsedSynthStyle.StateInfo stateInfo = null;
int state = 0;
String id = null;
_stateInfo = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_ID)) {
id = attributes.getValue(i);
}
@@ -496,7 +496,7 @@ class SynthParser extends HandlerBase {
_stateInfos.add(_stateInfo);
}
- private void endState() throws SAXException {
+ private void endState() {
int size = _statePainters.size();
if (size > 0) {
_stateInfo.setPainters(_statePainters.toArray(new ParsedSynthStyle.PainterInfo[size]));
@@ -505,7 +505,7 @@ class SynthParser extends HandlerBase {
_stateInfo = null;
}
- private void startFont(AttributeList attributes) throws SAXException {
+ private void startFont(Attributes attributes) throws SAXException {
Font font = null;
int style = Font.PLAIN;
int size = 0;
@@ -513,7 +513,7 @@ class SynthParser extends HandlerBase {
String name = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_ID)) {
id = attributes.getValue(i);
}
@@ -568,13 +568,13 @@ class SynthParser extends HandlerBase {
}
}
- private void startColor(AttributeList attributes) throws SAXException {
+ private void startColor(Attributes attributes) throws SAXException {
Color color = null;
String id = null;
_colorTypes.clear();
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_ID)) {
id = attributes.getValue(i);
}
@@ -697,7 +697,7 @@ class SynthParser extends HandlerBase {
}
}
- private void startProperty(AttributeList attributes,
+ private void startProperty(Attributes attributes,
Object property) throws SAXException {
Object value = null;
String key = null;
@@ -707,7 +707,7 @@ class SynthParser extends HandlerBase {
String aValue = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String aName = attributes.getName(i);
+ String aName = attributes.getQName(i);
if (aName.equals(ATTRIBUTE_TYPE)) {
String type = attributes.getValue(i).toUpperCase();
if (type.equals("IDREF")) {
@@ -795,11 +795,11 @@ class SynthParser extends HandlerBase {
}
}
- private void startGraphics(AttributeList attributes) throws SAXException {
+ private void startGraphics(Attributes attributes) throws SAXException {
SynthGraphicsUtils graphics = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_IDREF)) {
graphics = (SynthGraphicsUtils)lookup(attributes.getValue(i),
SynthGraphicsUtils.class);
@@ -813,7 +813,7 @@ class SynthParser extends HandlerBase {
}
}
- private void startInsets(AttributeList attributes) throws SAXException {
+ private void startInsets(Attributes attributes) throws SAXException {
int top = 0;
int bottom = 0;
int left = 0;
@@ -822,7 +822,7 @@ class SynthParser extends HandlerBase {
String id = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
try {
if (key.equals(ATTRIBUTE_IDREF)) {
@@ -858,13 +858,13 @@ class SynthParser extends HandlerBase {
}
}
- private void startBind(AttributeList attributes) throws SAXException {
+ private void startBind(Attributes attributes) throws SAXException {
ParsedSynthStyle style = null;
String path = null;
int type = -1;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_STYLE)) {
style = (ParsedSynthStyle)lookup(attributes.getValue(i),
@@ -899,7 +899,7 @@ class SynthParser extends HandlerBase {
}
}
- private void startPainter(AttributeList attributes, String type) throws SAXException {
+ private void startPainter(Attributes attributes, String type) throws SAXException {
Insets sourceInsets = null;
Insets destInsets = null;
String path = null;
@@ -915,7 +915,7 @@ class SynthParser extends HandlerBase {
boolean paintCenterSpecified = false;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
String value = attributes.getValue(i);
if (key.equals(ATTRIBUTE_ID)) {
@@ -1042,12 +1042,12 @@ class SynthParser extends HandlerBase {
painters.add(painterInfo);
}
- private void startImageIcon(AttributeList attributes) throws SAXException {
+ private void startImageIcon(Attributes attributes) throws SAXException {
String path = null;
String id = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_ID)) {
id = attributes.getValue(i);
@@ -1062,12 +1062,11 @@ class SynthParser extends HandlerBase {
register(id, new LazyImageIcon(getResource(path)));
}
- private void startOpaque(AttributeList attributes) throws
- SAXException {
+ private void startOpaque(Attributes attributes) {
if (_style != null) {
_style.setOpaque(true);
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_VALUE)) {
_style.setOpaque("true".equals(attributes.getValue(i).
@@ -1077,12 +1076,12 @@ class SynthParser extends HandlerBase {
}
}
- private void startInputMap(AttributeList attributes) throws SAXException {
+ private void startInputMap(Attributes attributes) throws SAXException {
_inputMapBindings.clear();
_inputMapID = null;
if (_style != null) {
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String key = attributes.getName(i);
+ String key = attributes.getQName(i);
if (key.equals(ATTRIBUTE_ID)) {
_inputMapID = attributes.getValue(i);
@@ -1101,7 +1100,7 @@ class SynthParser extends HandlerBase {
_inputMapID = null;
}
- private void startBindKey(AttributeList attributes) throws SAXException {
+ private void startBindKey(Attributes attributes) throws SAXException {
if (_inputMapID == null) {
// Not in an inputmap, bail.
return;
@@ -1110,7 +1109,7 @@ class SynthParser extends HandlerBase {
String key = null;
String value = null;
for(int i = attributes.getLength() - 1; i >= 0; i--) {
- String aKey = attributes.getName(i);
+ String aKey = attributes.getQName(i);
if (aKey.equals(ATTRIBUTE_KEY)) {
key = attributes.getValue(i);
@@ -1129,26 +1128,26 @@ class SynthParser extends HandlerBase {
}
//
- // SAX methods, these forward to the ObjectHandler if we don't know
+ // SAX methods, these forward to the DocumentHandler if we don't know
// the element name.
//
public InputSource resolveEntity(String publicId, String systemId)
- throws SAXException {
+ throws IOException, SAXException {
if (isForwarding()) {
return getHandler().resolveEntity(publicId, systemId);
}
return null;
}
- public void notationDecl(String name, String publicId, String systemId) {
+ public void notationDecl(String name, String publicId, String systemId) throws SAXException {
if (isForwarding()) {
getHandler().notationDecl(name, publicId, systemId);
}
}
public void unparsedEntityDecl(String name, String publicId,
- String systemId, String notationName) {
+ String systemId, String notationName) throws SAXException {
if (isForwarding()) {
getHandler().unparsedEntityDecl(name, publicId, systemId,
notationName);
@@ -1173,7 +1172,7 @@ class SynthParser extends HandlerBase {
}
}
- public void startElement(String name, AttributeList attributes)
+ public void startElement(String uri, String local, String name, Attributes attributes)
throws SAXException {
name = name.intern();
if (name == ELEMENT_STYLE) {
@@ -1223,18 +1222,18 @@ class SynthParser extends HandlerBase {
}
else if (name != ELEMENT_SYNTH) {
if (_depth++ == 0) {
- getHandler().reset();
+ getHandler().startDocument();
}
- getHandler().startElement(name, attributes);
+ getHandler().startElement(uri, local, name, attributes);
}
}
- public void endElement(String name) throws SAXException {
+ public void endElement(String uri, String local, String name) throws SAXException {
if (isForwarding()) {
- getHandler().endElement(name);
+ getHandler().endElement(uri, local, name);
_depth--;
if (!isForwarding()) {
- getHandler().reset();
+ getHandler().startDocument();
}
}
else {
diff --git a/src/share/classes/javax/swing/table/DefaultTableColumnModel.java b/src/share/classes/javax/swing/table/DefaultTableColumnModel.java
index 846001b35..d3ae8cdd6 100644
--- a/src/share/classes/javax/swing/table/DefaultTableColumnModel.java
+++ b/src/share/classes/javax/swing/table/DefaultTableColumnModel.java
@@ -190,7 +190,7 @@ public class DefaultTableColumnModel implements TableColumnModel,
fireColumnMoved(new TableColumnModelEvent(this, columnIndex, newIndex));
return;
}
- aColumn = (TableColumn)tableColumns.elementAt(columnIndex);
+ aColumn = tableColumns.elementAt(columnIndex);
tableColumns.removeElementAt(columnIndex);
boolean selected = selectionModel.isSelectedIndex(columnIndex);
@@ -291,7 +291,7 @@ public class DefaultTableColumnModel implements TableColumnModel,
* at <code>columnIndex</code>
*/
public TableColumn getColumn(int columnIndex) {
- return (TableColumn)tableColumns.elementAt(columnIndex);
+ return tableColumns.elementAt(columnIndex);
}
/**
@@ -504,8 +504,7 @@ public class DefaultTableColumnModel implements TableColumnModel,
* @since 1.4
*/
public TableColumnModelListener[] getColumnModelListeners() {
- return (TableColumnModelListener[])listenerList.getListeners(
- TableColumnModelListener.class);
+ return listenerList.getListeners(TableColumnModelListener.class);
}
//
diff --git a/src/share/classes/javax/swing/tree/DefaultMutableTreeNode.java b/src/share/classes/javax/swing/tree/DefaultMutableTreeNode.java
index 11ed3bdd5..34c36ba89 100644
--- a/src/share/classes/javax/swing/tree/DefaultMutableTreeNode.java
+++ b/src/share/classes/javax/swing/tree/DefaultMutableTreeNode.java
@@ -84,7 +84,7 @@ import java.util.*;
*
* @author Rob Davis
*/
-public class DefaultMutableTreeNode extends Object implements Cloneable,
+public class DefaultMutableTreeNode implements Cloneable,
MutableTreeNode, Serializable
{
private static final long serialVersionUID = -4298474751201349152L;
@@ -1251,7 +1251,7 @@ public class DefaultMutableTreeNode extends Object implements Cloneable,
* @return a copy of this node
*/
public Object clone() {
- DefaultMutableTreeNode newNode = null;
+ DefaultMutableTreeNode newNode;
try {
newNode = (DefaultMutableTreeNode)super.clone();
@@ -1297,24 +1297,22 @@ public class DefaultMutableTreeNode extends Object implements Cloneable,
userObject = tValues[1];
}
- final class PreorderEnumeration implements Enumeration<TreeNode> {
- protected Stack stack;
+ private final class PreorderEnumeration implements Enumeration<TreeNode> {
+ private final Stack<Enumeration> stack = new Stack<Enumeration>();
public PreorderEnumeration(TreeNode rootNode) {
super();
- Vector v = new Vector(1);
+ Vector<TreeNode> v = new Vector<TreeNode>(1);
v.addElement(rootNode); // PENDING: don't really need a vector
- stack = new Stack();
stack.push(v.elements());
}
public boolean hasMoreElements() {
- return (!stack.empty() &&
- ((Enumeration)stack.peek()).hasMoreElements());
+ return (!stack.empty() && stack.peek().hasMoreElements());
}
public TreeNode nextElement() {
- Enumeration enumer = (Enumeration)stack.peek();
+ Enumeration enumer = stack.peek();
TreeNode node = (TreeNode)enumer.nextElement();
Enumeration children = node.children();
@@ -1353,8 +1351,7 @@ public class DefaultMutableTreeNode extends Object implements Cloneable,
if (subtree.hasMoreElements()) {
retval = subtree.nextElement();
} else if (children.hasMoreElements()) {
- subtree = new PostorderEnumeration(
- (TreeNode)children.nextElement());
+ subtree = new PostorderEnumeration(children.nextElement());
retval = subtree.nextElement();
} else {
retval = root;
@@ -1373,7 +1370,7 @@ public class DefaultMutableTreeNode extends Object implements Cloneable,
public BreadthFirstEnumeration(TreeNode rootNode) {
super();
- Vector v = new Vector(1);
+ Vector<TreeNode> v = new Vector<TreeNode>(1);
v.addElement(rootNode); // PENDING: don't really need a vector
queue = new Queue();
queue.enqueue(v.elements());
diff --git a/src/share/classes/javax/swing/undo/CompoundEdit.java b/src/share/classes/javax/swing/undo/CompoundEdit.java
index 6bc661560..7aa21d3af 100644
--- a/src/share/classes/javax/swing/undo/CompoundEdit.java
+++ b/src/share/classes/javax/swing/undo/CompoundEdit.java
@@ -59,7 +59,7 @@ public class CompoundEdit extends AbstractUndoableEdit {
super.undo();
int i = edits.size();
while (i-- > 0) {
- UndoableEdit e = (UndoableEdit)edits.elementAt(i);
+ UndoableEdit e = edits.elementAt(i);
e.undo();
}
}
@@ -85,7 +85,7 @@ public class CompoundEdit extends AbstractUndoableEdit {
protected UndoableEdit lastEdit() {
int count = edits.size();
if (count > 0)
- return (UndoableEdit)edits.elementAt(count-1);
+ return edits.elementAt(count-1);
else
return null;
}
@@ -98,7 +98,7 @@ public class CompoundEdit extends AbstractUndoableEdit {
int size = edits.size();
for (int i = size-1; i >= 0; i--)
{
- UndoableEdit e = (UndoableEdit)edits.elementAt(i);
+ UndoableEdit e = edits.elementAt(i);
// System.out.println("CompoundEdit(" + i + "): Discarding " +
// e.getUndoPresentationName());
e.die();
diff --git a/src/share/classes/sun/awt/AWTAccessor.java b/src/share/classes/sun/awt/AWTAccessor.java
new file mode 100644
index 000000000..41b933c4a
--- /dev/null
+++ b/src/share/classes/sun/awt/AWTAccessor.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.awt;
+
+import java.awt.*;
+import sun.misc.Unsafe;
+
+/** The AWTAccessor utility class.
+ * The main purpose of this class is to enable accessing
+ * private and package-private fields of classes from
+ * different classes/packages. See sun.misc.SharedSecretes
+ * for another example.
+ */
+public final class AWTAccessor {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ /** We don't need any objects of this class.
+ * It's rather a collection of static methods
+ * and interfaces.
+ */
+ private AWTAccessor() {
+ }
+
+ /** An accessor for the java.awt.Component class.
+ */
+ public interface ComponentAccessor {
+ // See 6797587
+ // Also see: 6776743, 6768307, and 6768332.
+ /**
+ * Sets the shape of a lw component to cut out from hw components.
+ */
+ void setMixingCutoutShape(Component comp, Shape shape);
+ }
+
+ /* The java.awt.Component class accessor object.
+ */
+ private static ComponentAccessor componentAccessor;
+
+ /** Set an accessor object for the java.awt.Component class.
+ */
+ public static void setComponentAccessor(ComponentAccessor ca) {
+ componentAccessor = ca;
+ }
+
+ /** Retrieve the accessor object for the java.awt.Window class.
+ */
+ public static ComponentAccessor getComponentAccessor() {
+ if (componentAccessor == null) {
+ unsafe.ensureClassInitialized(Component.class);
+ }
+
+ return componentAccessor;
+ }
+}
diff --git a/src/share/classes/sun/awt/HeadlessToolkit.java b/src/share/classes/sun/awt/HeadlessToolkit.java
index 97a768660..18ccc446b 100644
--- a/src/share/classes/sun/awt/HeadlessToolkit.java
+++ b/src/share/classes/sun/awt/HeadlessToolkit.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2008 Sun Microsystems, Inc. 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
@@ -456,6 +456,10 @@ public class HeadlessToolkit extends Toolkit
return tk.getAWTEventListeners();
}
+ public AWTEventListener[] getAWTEventListeners(long eventMask) {
+ return tk.getAWTEventListeners(eventMask);
+ }
+
public boolean isDesktopSupported() {
return false;
}
@@ -464,4 +468,8 @@ public class HeadlessToolkit extends Toolkit
throws HeadlessException{
throw new HeadlessException();
}
+
+ public boolean areExtraMouseButtonsEnabled() throws HeadlessException{
+ throw new HeadlessException();
+ }
}
diff --git a/src/share/classes/sun/awt/SunToolkit.java b/src/share/classes/sun/awt/SunToolkit.java
index 20da8ffe7..7c6f74856 100644
--- a/src/share/classes/sun/awt/SunToolkit.java
+++ b/src/share/classes/sun/awt/SunToolkit.java
@@ -1972,6 +1972,21 @@ public abstract class SunToolkit extends Toolkit
AWTAutoShutdown.getInstance().dumpPeers(aLog);
}
+ private static Boolean sunAwtDisableMixing = null;
+
+ /**
+ * Returns the value of "sun.awt.disableMixing" property. Default
+ * value is {@code false}.
+ */
+ public synchronized static boolean getSunAwtDisableMixing() {
+ if (sunAwtDisableMixing == null) {
+ sunAwtDisableMixing = Boolean.valueOf(
+ AccessController.doPrivileged(
+ new GetBooleanAction("sun.awt.disableMixing")));
+ }
+ return sunAwtDisableMixing.booleanValue();
+ }
+
/**
* Returns true if the native GTK libraries are available. The
* default implementation returns false, but UNIXToolkit overrides this
@@ -2008,26 +2023,12 @@ class PostEventQueue {
/*
* Continually post pending AWTEvents to the Java EventQueue.
*/
- public void flush() {
- if (queueHead != null) {
- EventQueueItem tempQueue;
- /*
- * We have to execute the loop inside the synchronized block
- * to ensure that the flush is completed before a new event
- * can be posted to this queue.
- */
- synchronized (this) {
- tempQueue = queueHead;
- queueHead = queueTail = null;
- /*
- * If this PostEventQueue is flushed in parallel on two
- * different threads tempQueue will be null for one of them.
- */
- while (tempQueue != null) {
- eventQueue.postEvent(tempQueue.event);
- tempQueue = tempQueue.next;
- }
- }
+ public synchronized void flush() {
+ EventQueueItem tempQueue = queueHead;
+ queueHead = queueTail = null;
+ while (tempQueue != null) {
+ eventQueue.postEvent(tempQueue.event);
+ tempQueue = tempQueue.next;
}
}
diff --git a/src/share/classes/sun/beans/editors/EnumEditor.java b/src/share/classes/sun/beans/editors/EnumEditor.java
index 6a465785c..201c6f24b 100644
--- a/src/share/classes/sun/beans/editors/EnumEditor.java
+++ b/src/share/classes/sun/beans/editors/EnumEditor.java
@@ -67,7 +67,7 @@ public final class EnumEditor implements PropertyEditor {
}
public void setValue( Object value ) {
- if ( ( value != null ) && ( this.type != value.getClass() ) ) {
+ if ( ( value != null ) && !this.type.isInstance( value ) ) {
throw new IllegalArgumentException( "Unsupported value: " + value );
}
Object oldValue;
diff --git a/src/share/classes/sun/java2d/pipe/Region.java b/src/share/classes/sun/java2d/pipe/Region.java
index 765c42510..9f395c3b5 100644
--- a/src/share/classes/sun/java2d/pipe/Region.java
+++ b/src/share/classes/sun/java2d/pipe/Region.java
@@ -28,6 +28,7 @@ package sun.java2d.pipe;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
+import java.awt.geom.RectangularShape;
/**
* This class encapsulates a definition of a two dimensional region which
@@ -63,11 +64,28 @@ public class Region {
static final int INIT_SIZE = 50;
static final int GROW_SIZE = 50;
- static final Region EMPTY_REGION = new Region(0, 0, 0, 0);
- static final Region WHOLE_REGION = new Region(Integer.MIN_VALUE,
- Integer.MIN_VALUE,
- Integer.MAX_VALUE,
- Integer.MAX_VALUE);
+ /**
+ * Immutable Region.
+ */
+ private static final class ImmutableRegion extends Region {
+ protected ImmutableRegion(int lox, int loy, int hix, int hiy) {
+ super(lox, loy, hix, hiy);
+ }
+
+ // Override all the methods that mutate the object
+ public void appendSpans(sun.java2d.pipe.SpanIterator si) {}
+ public void setOutputArea(java.awt.Rectangle r) {}
+ public void setOutputAreaXYWH(int x, int y, int w, int h) {}
+ public void setOutputArea(int[] box) {}
+ public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {}
+ }
+
+ public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0);
+ public static final Region WHOLE_REGION = new ImmutableRegion(
+ Integer.MIN_VALUE,
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE);
int lox;
int loy;
@@ -113,7 +131,7 @@ public class Region {
return newv;
}
- private Region(int lox, int loy, int hix, int hiy) {
+ protected Region(int lox, int loy, int hix, int hiy) {
this.lox = lox;
this.loy = loy;
this.hix = hix;
@@ -194,6 +212,13 @@ public class Region {
public static Region getInstance(Region devBounds, boolean normalize,
Shape s, AffineTransform at)
{
+ // Optimize for empty shapes to avoid involving the SpanIterator
+ if (s instanceof RectangularShape &&
+ ((RectangularShape)s).isEmpty())
+ {
+ return EMPTY_REGION;
+ }
+
int box[] = new int[4];
ShapeSpanIterator sr = new ShapeSpanIterator(normalize);
try {
@@ -1206,7 +1231,7 @@ public class Region {
return false;
}
if (r.lox != this.lox || r.loy != this.loy ||
- r.hiy != this.hiy || r.hiy != this.hiy)
+ r.hix != this.hix || r.hiy != this.hiy)
{
return false;
}
diff --git a/src/share/classes/sun/misc/URLClassPath.java b/src/share/classes/sun/misc/URLClassPath.java
index 5e38c7671..28b4441d3 100644
--- a/src/share/classes/sun/misc/URLClassPath.java
+++ b/src/share/classes/sun/misc/URLClassPath.java
@@ -25,17 +25,7 @@
package sun.misc;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-import java.util.Stack;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.StringTokenizer;
-import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.*;
import java.util.jar.JarFile;
import sun.misc.JarIndex;
import sun.misc.InvalidJarIndexException;
@@ -52,12 +42,7 @@ import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
+import java.io.*;
import java.security.AccessController;
import java.security.AccessControlException;
import java.security.CodeSigner;
@@ -100,6 +85,9 @@ public class URLClassPath {
/* The jar protocol handler to use when creating new URLs */
private URLStreamHandler jarHandler;
+ /* Whether this URLClassLoader has been closed yet */
+ private boolean closed = false;
+
/**
* Creates a new URLClassPath for the given URLs. The URLs will be
* searched in the order specified for classes and resources. A URL
@@ -124,6 +112,22 @@ public class URLClassPath {
this(urls, null);
}
+ public synchronized List<IOException> closeLoaders() {
+ if (closed) {
+ return Collections.emptyList();
+ }
+ List<IOException> result = new LinkedList<IOException>();
+ for (Loader loader : loaders) {
+ try {
+ loader.close();
+ } catch (IOException e) {
+ result.add (e);
+ }
+ }
+ closed = true;
+ return result;
+ }
+
/**
* Appends the specified URL to the search path of directory and JAR
* file URLs from which to load classes and resources.
@@ -293,6 +297,9 @@ public class URLClassPath {
* if the specified index is out of range.
*/
private synchronized Loader getLoader(int index) {
+ if (closed) {
+ return null;
+ }
// Expand URL search path until the request can be satisfied
// or the URL stack is empty.
while (loaders.size() < index + 1) {
@@ -453,7 +460,7 @@ public class URLClassPath {
* Inner class used to represent a loader of resources and classes
* from a base URL.
*/
- private static class Loader {
+ private static class Loader implements Closeable {
private final URL base;
/*
@@ -545,6 +552,12 @@ public class URLClassPath {
}
/*
+ * close this loader and release all resources
+ * method overridden in sub-classes
+ */
+ public void close () throws IOException {}
+
+ /*
* Returns the local class path for this loader, or null if none.
*/
URL[] getClassPath() throws IOException {
@@ -562,6 +575,7 @@ public class URLClassPath {
private MetaIndex metaIndex;
private URLStreamHandler handler;
private HashMap<URL, Loader> lmap;
+ private boolean closed = false;
/*
* Creates a new JarLoader for the specified URL referring to
@@ -604,6 +618,17 @@ public class URLClassPath {
}
}
+ @Override
+ public void close () throws IOException {
+ // closing is synchronized at higher level
+ if (!closed) {
+ closed = true;
+ // in case not already open.
+ ensureOpen();
+ jar.close();
+ }
+ }
+
JarFile getJarFile () {
return jar;
}
diff --git a/src/share/classes/sun/nio/ch/AbstractFuture.java b/src/share/classes/sun/nio/ch/AbstractFuture.java
new file mode 100644
index 000000000..f79dc7abb
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/AbstractFuture.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.AsynchronousChannel;
+import java.util.concurrent.Future;
+
+/**
+ * Base implementation of Future used for asynchronous I/O
+ */
+
+abstract class AbstractFuture<V,A>
+ implements Future<V>
+{
+ private final AsynchronousChannel channel;
+ private final A attachment;
+
+ protected AbstractFuture(AsynchronousChannel channel, A attachment) {
+ this.channel = channel;
+ this.attachment = attachment;
+ }
+
+ final AsynchronousChannel channel() {
+ return channel;
+ }
+
+ final A attachment() {
+ return attachment;
+ }
+
+ /**
+ * Returns the result of the operation if it has completed successfully.
+ */
+ abstract V value();
+
+ /**
+ * Returns the exception if the operation has failed.
+ */
+ abstract Throwable exception();
+}
diff --git a/src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java b/src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java
new file mode 100644
index 000000000..14e33ffe9
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.Channel;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.Queue;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.security.PrivilegedAction;
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import sun.security.action.GetIntegerAction;
+
+/**
+ * Base implementation of AsynchronousChannelGroup
+ */
+
+abstract class AsynchronousChannelGroupImpl
+ extends AsynchronousChannelGroup implements Executor
+{
+ // number of internal threads handling I/O events when using an unbounded
+ // thread pool. Internal threads do not dispatch to completion handlers.
+ private static final int internalThreadCount = AccessController.doPrivileged(
+ new GetIntegerAction("sun.nio.ch.internalThreadPoolSize", 1));
+
+ // associated thread pool
+ private final ThreadPool pool;
+
+ // number of tasks running (including internal)
+ private final AtomicInteger threadCount = new AtomicInteger();
+
+ // associated Executor for timeouts
+ private ScheduledThreadPoolExecutor timeoutExecutor;
+
+ // task queue for when using a fixed thread pool. In that case, thread
+ // waiting on I/O events must be awokon to poll tasks from this queue.
+ private final Queue<Runnable> taskQueue;
+
+ // group shutdown
+ // shutdownLock is RW lock so as to allow for concurrent queuing of tasks
+ // when using a fixed thread pool.
+ private final ReadWriteLock shutdownLock = new ReentrantReadWriteLock();
+ private final Object shutdownNowLock = new Object();
+ private volatile boolean shutdown;
+ private volatile boolean terminateInitiated;
+
+ AsynchronousChannelGroupImpl(AsynchronousChannelProvider provider,
+ ThreadPool pool)
+ {
+ super(provider);
+ this.pool = pool;
+
+ if (pool.isFixedThreadPool()) {
+ taskQueue = new ConcurrentLinkedQueue<Runnable>();
+ } else {
+ taskQueue = null; // not used
+ }
+
+ // use default thread factory as thread should not be visible to
+ // application (it doesn't execute completion handlers).
+ this.timeoutExecutor = (ScheduledThreadPoolExecutor)
+ Executors.newScheduledThreadPool(1, ThreadPool.defaultThreadFactory());
+ this.timeoutExecutor.setRemoveOnCancelPolicy(true);
+ }
+
+ final ExecutorService executor() {
+ return pool.executor();
+ }
+
+ final boolean isFixedThreadPool() {
+ return pool.isFixedThreadPool();
+ }
+
+ final int fixedThreadCount() {
+ if (isFixedThreadPool()) {
+ return pool.poolSize();
+ } else {
+ return pool.poolSize() + internalThreadCount;
+ }
+ }
+
+ private Runnable bindToGroup(final Runnable task) {
+ final AsynchronousChannelGroupImpl thisGroup = this;
+ return new Runnable() {
+ public void run() {
+ Invoker.bindToGroup(thisGroup);
+ task.run();
+ }
+ };
+ }
+
+ private void startInternalThread(final Runnable task) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ // internal threads should not be visible to application so
+ // cannot use user-supplied thread factory
+ ThreadPool.defaultThreadFactory().newThread(task).start();
+ return null;
+ }
+ });
+ }
+
+ protected final void startThreads(Runnable task) {
+ if (!isFixedThreadPool()) {
+ for (int i=0; i<internalThreadCount; i++) {
+ startInternalThread(task);
+ threadCount.incrementAndGet();
+ }
+ }
+ if (pool.poolSize() > 0) {
+ task = bindToGroup(task);
+ try {
+ for (int i=0; i<pool.poolSize(); i++) {
+ pool.executor().execute(task);
+ threadCount.incrementAndGet();
+ }
+ } catch (RejectedExecutionException x) {
+ // nothing we can do
+ }
+ }
+ }
+
+ final int threadCount() {
+ return threadCount.get();
+ }
+
+ /**
+ * Invoked by tasks as they terminate
+ */
+ final int threadExit(Runnable task, boolean replaceMe) {
+ if (replaceMe) {
+ try {
+ if (Invoker.isBoundToAnyGroup()) {
+ // submit new task to replace this thread
+ pool.executor().execute(bindToGroup(task));
+ } else {
+ // replace internal thread
+ startInternalThread(task);
+ }
+ return threadCount.get();
+ } catch (RejectedExecutionException x) {
+ // unable to replace
+ }
+ }
+ return threadCount.decrementAndGet();
+ }
+
+ /**
+ * Wakes up a thread waiting for I/O events to execute the given task.
+ */
+ abstract void executeOnHandlerTask(Runnable task);
+
+ /**
+ * For a fixed thread pool the task is queued to a thread waiting on I/O
+ * events. For other thread pools we simply submit the task to the thread
+ * pool.
+ */
+ final void executeOnPooledThread(Runnable task) {
+ if (isFixedThreadPool()) {
+ executeOnHandlerTask(task);
+ } else {
+ pool.executor().execute(bindToGroup(task));
+ }
+ }
+
+ final void offerTask(Runnable task) {
+ taskQueue.offer(task);
+ }
+
+ final Runnable pollTask() {
+ return (taskQueue == null) ? null : taskQueue.poll();
+ }
+
+ final Future<?> schedule(Runnable task, long timeout, TimeUnit unit) {
+ try {
+ return timeoutExecutor.schedule(task, timeout, unit);
+ } catch (RejectedExecutionException rej) {
+ if (terminateInitiated) {
+ // no timeout scheduled as group is terminating
+ return null;
+ }
+ throw new AssertionError(rej);
+ }
+ }
+
+ @Override
+ public final boolean isShutdown() {
+ return shutdown;
+ }
+
+ @Override
+ public final boolean isTerminated() {
+ return pool.executor().isTerminated();
+ }
+
+ /**
+ * Returns true if there are no channels in the group
+ */
+ abstract boolean isEmpty();
+
+ /**
+ * Attaches a foreign channel to this group.
+ */
+ abstract Object attachForeignChannel(Channel channel, FileDescriptor fdo)
+ throws IOException;
+
+ /**
+ * Detaches a foreign channel from this group.
+ */
+ abstract void detachForeignChannel(Object key);
+
+ /**
+ * Closes all channels in the group
+ */
+ abstract void closeAllChannels() throws IOException;
+
+ /**
+ * Shutdown all tasks waiting for I/O events.
+ */
+ abstract void shutdownHandlerTasks();
+
+ private void shutdownExecutors() {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ pool.executor().shutdown();
+ timeoutExecutor.shutdown();
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public final void shutdown() {
+ shutdownLock.writeLock().lock();
+ try {
+ if (shutdown) {
+ // already shutdown
+ return;
+ }
+ shutdown = true;
+ } finally {
+ shutdownLock.writeLock().unlock();
+ }
+
+ // if there are channels in the group then shutdown will continue
+ // when the last channel is closed
+ if (!isEmpty()) {
+ return;
+ }
+ // initiate termination (acquire shutdownNowLock to ensure that other
+ // threads invoking shutdownNow will block).
+ synchronized (shutdownNowLock) {
+ if (!terminateInitiated) {
+ terminateInitiated = true;
+ shutdownHandlerTasks();
+ shutdownExecutors();
+ }
+ }
+ }
+
+ @Override
+ public final void shutdownNow() throws IOException {
+ shutdownLock.writeLock().lock();
+ try {
+ shutdown = true;
+ } finally {
+ shutdownLock.writeLock().unlock();
+ }
+ synchronized (shutdownNowLock) {
+ if (!terminateInitiated) {
+ terminateInitiated = true;
+ closeAllChannels();
+ shutdownHandlerTasks();
+ shutdownExecutors();
+ }
+ }
+ }
+
+ @Override
+ public final boolean awaitTermination(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ return pool.executor().awaitTermination(timeout, unit);
+ }
+
+ /**
+ * Executes the given command on one of the channel group's pooled threads.
+ */
+ @Override
+ public final void execute(Runnable task) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // when a security manager is installed then the user's task
+ // must be run with the current calling context
+ final AccessControlContext acc = AccessController.getContext();
+ final Runnable delegate = task;
+ task = new Runnable() {
+ @Override
+ public void run() {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ delegate.run();
+ return null;
+ }
+ }, acc);
+ }
+ };
+ }
+ executeOnPooledThread(task);
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/AsynchronousFileChannelImpl.java b/src/share/classes/sun/nio/ch/AsynchronousFileChannelImpl.java
new file mode 100644
index 000000000..2735a5a29
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/AsynchronousFileChannelImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.locks.*;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Base implementation of AsynchronousFileChannel.
+ */
+
+abstract class AsynchronousFileChannelImpl
+ extends AsynchronousFileChannel
+{
+ // close support
+ protected final ReadWriteLock closeLock = new ReentrantReadWriteLock();
+ protected volatile boolean closed;
+
+ // file descriptor
+ protected final FileDescriptor fdObj;
+
+ // indicates if open for reading/writing
+ protected final boolean reading;
+ protected final boolean writing;
+
+ // associated Executor
+ protected final ExecutorService executor;
+
+ protected AsynchronousFileChannelImpl(FileDescriptor fdObj,
+ boolean reading,
+ boolean writing,
+ ExecutorService executor)
+ {
+ this.fdObj = fdObj;
+ this.reading = reading;
+ this.writing = writing;
+ this.executor = executor;
+ }
+
+ final ExecutorService executor() {
+ return executor;
+ }
+
+ @Override
+ public final boolean isOpen() {
+ return !closed;
+ }
+
+ /**
+ * Marks the beginning of an I/O operation.
+ *
+ * @throws ClosedChannelException If channel is closed
+ */
+ protected final void begin() throws IOException {
+ closeLock.readLock().lock();
+ if (closed)
+ throw new ClosedChannelException();
+ }
+
+ /**
+ * Marks the end of an I/O operation.
+ */
+ protected final void end() {
+ closeLock.readLock().unlock();
+ }
+
+ /**
+ * Marks end of I/O operation
+ */
+ protected final void end(boolean completed) throws IOException {
+ end();
+ if (!completed && !isOpen())
+ throw new AsynchronousCloseException();
+ }
+
+ // -- file locking --
+
+ private volatile FileLockTable fileLockTable;
+
+ final void ensureFileLockTableInitialized() throws IOException {
+ if (fileLockTable == null) {
+ synchronized (this) {
+ if (fileLockTable == null) {
+ fileLockTable = FileLockTable.newSharedFileLockTable(this, fdObj);
+ }
+ }
+ }
+ }
+
+ final void invalidateAllLocks() {
+ if (fileLockTable != null) {
+ try {
+ fileLockTable.removeAll( new FileLockTable.Releaser() {
+ public void release(FileLock fl) {
+ ((FileLockImpl)fl).invalidate();
+ }
+ });
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+
+ /**
+ * Adds region to lock table
+ */
+ protected final FileLockImpl addToFileLockTable(long position, long size, boolean shared) {
+ final FileLockImpl fli;
+ try {
+ // like begin() but returns null instead of exception
+ closeLock.readLock().lock();
+ if (closed)
+ return null;
+
+ try {
+ ensureFileLockTableInitialized();
+ } catch (IOException x) {
+ // should not happen
+ throw new AssertionError(x);
+ }
+ fli = new FileLockImpl(this, position, size, shared);
+ // may throw OverlappedFileLockException
+ fileLockTable.add(fli);
+ } finally {
+ end();
+ }
+ return fli;
+ }
+
+ protected final void removeFromFileLockTable(FileLockImpl fli) {
+ fileLockTable.remove(fli);
+ }
+
+ /**
+ * Invoked by FileLockImpl to release lock acquired by this channel.
+ */
+ abstract void release(FileLockImpl fli) throws IOException;
+}
diff --git a/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java
new file mode 100644
index 000000000..b4fcb9c2c
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.net.StandardSocketOption;
+import java.net.InetSocketAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Base implementation of AsynchronousServerSocketChannel.
+ */
+
+abstract class AsynchronousServerSocketChannelImpl
+ extends AsynchronousServerSocketChannel
+ implements Cancellable, Groupable
+{
+ protected final FileDescriptor fd;
+
+ // the local address to which the channel's socket is bound
+ protected volatile SocketAddress localAddress = null;
+
+ // need this lock to set local address
+ private final Object stateLock = new Object();
+
+ // close support
+ private ReadWriteLock closeLock = new ReentrantReadWriteLock();
+ private volatile boolean open = true;
+
+ // set true when accept operation is cancelled
+ private volatile boolean acceptKilled;
+
+
+ AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group) {
+ super(group.provider());
+ this.fd = Net.serverSocket(true);
+ }
+
+ @Override
+ public final boolean isOpen() {
+ return open;
+ }
+
+ /**
+ * Marks beginning of access to file descriptor/handle
+ */
+ final void begin() throws IOException {
+ closeLock.readLock().lock();
+ if (!isOpen())
+ throw new ClosedChannelException();
+ }
+
+ /**
+ * Marks end of access to file descriptor/handle
+ */
+ final void end() {
+ closeLock.readLock().unlock();
+ }
+
+ /**
+ * Invoked to close file descriptor/handle.
+ */
+ abstract void implClose() throws IOException;
+
+ @Override
+ public final void close() throws IOException {
+ // synchronize with any threads using file descriptor/handle
+ closeLock.writeLock().lock();
+ try {
+ if (!open)
+ return; // already closed
+ open = false;
+ } finally {
+ closeLock.writeLock().unlock();
+ }
+ implClose();
+ }
+
+ final boolean isAcceptKilled() {
+ return acceptKilled;
+ }
+
+ @Override
+ public final void onCancel(PendingFuture<?,?> task) {
+ acceptKilled = true;
+ }
+
+ @Override
+ public final AsynchronousServerSocketChannel bind(SocketAddress local, int backlog)
+ throws IOException
+ {
+ InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
+ Net.checkAddress(local);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkListen(isa.getPort());
+
+ try {
+ begin();
+ synchronized (stateLock) {
+ if (localAddress != null)
+ throw new AlreadyBoundException();
+ Net.bind(fd, isa.getAddress(), isa.getPort());
+ Net.listen(fd, backlog < 1 ? 50 : backlog);
+ localAddress = Net.localAddress(fd);
+ }
+ } finally {
+ end();
+ }
+ return this;
+ }
+
+ @Override
+ public final SocketAddress getLocalAddress() throws IOException {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ return localAddress;
+ }
+
+ @Override
+ public final <T> AsynchronousServerSocketChannel setOption(SocketOption<T> name,
+ T value)
+ throws IOException
+ {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ try {
+ begin();
+ Net.setSocketOption(fd, Net.UNSPEC, name, value);
+ return this;
+ } finally {
+ end();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <T> T getOption(SocketOption<T> name) throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ try {
+ begin();
+ return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
+ } finally {
+ end();
+ }
+ }
+
+ private static class DefaultOptionsHolder {
+ static final Set<SocketOption<?>> defaultOptions = defaultOptions();
+
+ private static Set<SocketOption<?>> defaultOptions() {
+ HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);
+ set.add(StandardSocketOption.SO_RCVBUF);
+ set.add(StandardSocketOption.SO_REUSEADDR);
+ return Collections.unmodifiableSet(set);
+ }
+ }
+
+ @Override
+ public final Set<SocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getClass().getName());
+ sb.append('[');
+ if (!isOpen())
+ sb.append("closed");
+ else {
+ if (localAddress == null) {
+ sb.append("unbound");
+ } else {
+ sb.append(localAddress.toString());
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java
new file mode 100644
index 000000000..09637fbae
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.net.SocketOption;
+import java.net.StandardSocketOption;
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+
+/**
+ * Base implementation of AsynchronousSocketChannel
+ */
+
+abstract class AsynchronousSocketChannelImpl
+ extends AsynchronousSocketChannel
+ implements Cancellable, Groupable
+{
+ protected final FileDescriptor fd;
+
+ // protects state, localAddress, and remoteAddress
+ protected final Object stateLock = new Object();
+
+ protected volatile SocketAddress localAddress = null;
+ protected volatile SocketAddress remoteAddress = null;
+
+ // State, increases monotonically
+ static final int ST_UNINITIALIZED = -1;
+ static final int ST_UNCONNECTED = 0;
+ static final int ST_PENDING = 1;
+ static final int ST_CONNECTED = 2;
+ protected volatile int state = ST_UNINITIALIZED;
+
+ // reading state
+ private final Object readLock = new Object();
+ private boolean reading;
+ private boolean readShutdown;
+ private boolean readKilled; // further reading disallowed due to timeout
+
+ // writing state
+ private final Object writeLock = new Object();
+ private boolean writing;
+ private boolean writeShutdown;
+ private boolean writeKilled; // further writing disallowed due to timeout
+
+ // close support
+ private final ReadWriteLock closeLock = new ReentrantReadWriteLock();
+ private volatile boolean open = true;
+
+ AsynchronousSocketChannelImpl(AsynchronousChannelGroupImpl group)
+ throws IOException
+ {
+ super(group.provider());
+ this.fd = Net.socket(true);
+ this.state = ST_UNCONNECTED;
+ }
+
+ // Constructor for sockets obtained from AsynchronousServerSocketChannelImpl
+ AsynchronousSocketChannelImpl(AsynchronousChannelGroupImpl group,
+ FileDescriptor fd,
+ InetSocketAddress remote)
+ throws IOException
+ {
+ super(group.provider());
+ this.fd = fd;
+ this.state = ST_CONNECTED;
+ this.localAddress = Net.localAddress(fd);
+ this.remoteAddress = remote;
+ }
+
+ @Override
+ public final boolean isOpen() {
+ return open;
+ }
+
+ /**
+ * Marks beginning of access to file descriptor/handle
+ */
+ final void begin() throws IOException {
+ closeLock.readLock().lock();
+ if (!isOpen())
+ throw new ClosedChannelException();
+ }
+
+ /**
+ * Marks end of access to file descriptor/handle
+ */
+ final void end() {
+ closeLock.readLock().unlock();
+ }
+
+ /**
+ * Invoked to close socket and release other resources.
+ */
+ abstract void implClose() throws IOException;
+
+ @Override
+ public final void close() throws IOException {
+ // synchronize with any threads initiating asynchronous operations
+ closeLock.writeLock().lock();
+ try {
+ if (!open)
+ return; // already closed
+ open = false;
+ } finally {
+ closeLock.writeLock().unlock();
+ }
+ implClose();
+ }
+
+ final void enableReading(boolean killed) {
+ synchronized (readLock) {
+ reading = false;
+ if (killed)
+ readKilled = true;
+ }
+ }
+
+ final void enableReading() {
+ enableReading(false);
+ }
+
+ final void enableWriting(boolean killed) {
+ synchronized (writeLock) {
+ writing = false;
+ if (killed)
+ writeKilled = true;
+ }
+ }
+
+ final void enableWriting() {
+ enableWriting(false);
+ }
+
+ final void killReading() {
+ synchronized (readLock) {
+ readKilled = true;
+ }
+ }
+
+ final void killWriting() {
+ synchronized (writeLock) {
+ writeKilled = true;
+ }
+ }
+
+ final void killConnect() {
+ // when a connect is cancelled then the connection may have been
+ // established so prevent reading or writing.
+ killReading();
+ killWriting();
+ }
+
+ /**
+ * Invoked by read to initiate the I/O operation.
+ */
+ abstract <V extends Number,A> Future<V> readImpl(ByteBuffer[] dsts,
+ boolean isScatteringRead,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler);
+
+ @SuppressWarnings("unchecked")
+ private <V extends Number,A> Future<V> read(ByteBuffer[] dsts,
+ boolean isScatteringRead,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler)
+ {
+ if (!isOpen()) {
+ CompletedFuture<V,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ if (remoteAddress == null)
+ throw new NotYetConnectedException();
+ if (timeout < 0L)
+ throw new IllegalArgumentException("Negative timeout");
+
+ boolean hasSpaceToRead = isScatteringRead || dsts[0].hasRemaining();
+ boolean shutdown = false;
+
+ // check and update state
+ synchronized (readLock) {
+ if (readKilled)
+ throw new RuntimeException("Reading not allowed due to timeout or cancellation");
+ if (reading)
+ throw new ReadPendingException();
+ if (readShutdown) {
+ shutdown = true;
+ } else {
+ if (hasSpaceToRead) {
+ reading = true;
+ }
+ }
+ }
+
+ // immediately complete with -1 if shutdown for read
+ // immediately complete with 0 if no space remaining
+ if (shutdown || !hasSpaceToRead) {
+ CompletedFuture<V,A> result;
+ if (isScatteringRead) {
+ Long value = (shutdown) ? Long.valueOf(-1L) : Long.valueOf(0L);
+ result = (CompletedFuture<V,A>)CompletedFuture.withResult(this, value, attachment);
+ } else {
+ int value = (shutdown) ? -1 : 0;
+ result = (CompletedFuture<V,A>)CompletedFuture.withResult(this, value, attachment);
+ }
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ return readImpl(dsts, isScatteringRead, timeout, unit, attachment, handler);
+ }
+
+ @Override
+ public final <A> Future<Integer> read(ByteBuffer dst,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ if (dst.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+ ByteBuffer[] bufs = new ByteBuffer[1];
+ bufs[0] = dst;
+ return read(bufs, false, timeout, unit, attachment, handler);
+ }
+
+ @Override
+ public final <A> Future<Long> read(ByteBuffer[] dsts,
+ int offset,
+ int length,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Long,? super A> handler)
+ {
+ if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
+ throw new IndexOutOfBoundsException();
+ ByteBuffer[] bufs = Util.subsequence(dsts, offset, length);
+ for (int i=0; i<bufs.length; i++) {
+ if (bufs[i].isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+ }
+ return read(bufs, true, timeout, unit, attachment, handler);
+ }
+
+ /**
+ * Invoked by write to initiate the I/O operation.
+ */
+ abstract <V extends Number,A> Future<V> writeImpl(ByteBuffer[] srcs,
+ boolean isGatheringWrite,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler);
+
+ @SuppressWarnings("unchecked")
+ private <V extends Number,A> Future<V> write(ByteBuffer[] srcs,
+ boolean isGatheringWrite,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler)
+ {
+ boolean hasDataToWrite = isGatheringWrite || srcs[0].hasRemaining();
+
+ boolean closed = false;
+ if (isOpen()) {
+ if (remoteAddress == null)
+ throw new NotYetConnectedException();
+ if (timeout < 0L)
+ throw new IllegalArgumentException("Negative timeout");
+ // check and update state
+ synchronized (writeLock) {
+ if (writeKilled)
+ throw new RuntimeException("Writing not allowed due to timeout or cancellation");
+ if (writing)
+ throw new WritePendingException();
+ if (writeShutdown) {
+ closed = true;
+ } else {
+ if (hasDataToWrite)
+ writing = true;
+ }
+ }
+ } else {
+ closed = true;
+ }
+
+ // channel is closed or shutdown for write
+ if (closed) {
+ CompletedFuture<V,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ // nothing to write so complete immediately
+ if (!hasDataToWrite) {
+ CompletedFuture<V,A> result;
+ if (isGatheringWrite) {
+ result = (CompletedFuture<V,A>)CompletedFuture.withResult(this, 0L, attachment);
+ } else {
+ result = (CompletedFuture<V,A>)CompletedFuture.withResult(this, 0, attachment);
+ }
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ return writeImpl(srcs, isGatheringWrite, timeout, unit, attachment, handler);
+ }
+
+ @Override
+ public final <A> Future<Integer> write(ByteBuffer src,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ ByteBuffer[] bufs = new ByteBuffer[1];
+ bufs[0] = src;
+ return write(bufs, false, timeout, unit, attachment, handler);
+ }
+
+ @Override
+ public final <A> Future<Long> write(ByteBuffer[] srcs,
+ int offset,
+ int length,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Long,? super A> handler)
+ {
+ if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
+ throw new IndexOutOfBoundsException();
+ srcs = Util.subsequence(srcs, offset, length);
+ return write(srcs, true, timeout, unit, attachment, handler);
+ }
+
+ @Override
+ public final AsynchronousSocketChannel bind(SocketAddress local)
+ throws IOException
+ {
+ try {
+ begin();
+ synchronized (stateLock) {
+ if (state == ST_PENDING)
+ throw new ConnectionPendingException();
+ if (localAddress != null)
+ throw new AlreadyBoundException();
+ InetSocketAddress isa = (local == null) ?
+ new InetSocketAddress(0) : Net.checkAddress(local);
+ Net.bind(fd, isa.getAddress(), isa.getPort());
+ localAddress = Net.localAddress(fd);
+ }
+ } finally {
+ end();
+ }
+ return this;
+ }
+
+ @Override
+ public final SocketAddress getLocalAddress() throws IOException {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ return localAddress;
+ }
+
+ @Override
+ public final <T> AsynchronousSocketChannel setOption(SocketOption<T> name, T value)
+ throws IOException
+ {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ try {
+ begin();
+ if (writeShutdown)
+ throw new IOException("Connection has been shutdown for writing");
+ Net.setSocketOption(fd, Net.UNSPEC, name, value);
+ return this;
+ } finally {
+ end();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <T> T getOption(SocketOption<T> name) throws IOException {
+ if (name == null)
+ throw new NullPointerException();
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
+
+ try {
+ begin();
+ return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
+ } finally {
+ end();
+ }
+ }
+
+ private static class DefaultOptionsHolder {
+ static final Set<SocketOption<?>> defaultOptions = defaultOptions();
+
+ private static Set<SocketOption<?>> defaultOptions() {
+ HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(5);
+ set.add(StandardSocketOption.SO_SNDBUF);
+ set.add(StandardSocketOption.SO_RCVBUF);
+ set.add(StandardSocketOption.SO_KEEPALIVE);
+ set.add(StandardSocketOption.SO_REUSEADDR);
+ set.add(StandardSocketOption.TCP_NODELAY);
+ return Collections.unmodifiableSet(set);
+ }
+ }
+
+ @Override
+ public final Set<SocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final SocketAddress getRemoteAddress() throws IOException {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ return remoteAddress;
+ }
+
+ @Override
+ public final AsynchronousSocketChannel shutdownInput() throws IOException {
+ try {
+ begin();
+ if (remoteAddress == null)
+ throw new NotYetConnectedException();
+ synchronized (readLock) {
+ if (!readShutdown) {
+ Net.shutdown(fd, Net.SHUT_RD);
+ readShutdown = true;
+ }
+ }
+ } finally {
+ end();
+ }
+ return this;
+ }
+
+ @Override
+ public final AsynchronousSocketChannel shutdownOutput() throws IOException {
+ try {
+ begin();
+ if (remoteAddress == null)
+ throw new NotYetConnectedException();
+ synchronized (writeLock) {
+ if (!writeShutdown) {
+ Net.shutdown(fd, Net.SHUT_WR);
+ writeShutdown = true;
+ }
+ }
+ } finally {
+ end();
+ }
+ return this;
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getClass().getName());
+ sb.append('[');
+ synchronized (stateLock) {
+ if (!isOpen()) {
+ sb.append("closed");
+ } else {
+ switch (state) {
+ case ST_UNCONNECTED:
+ sb.append("unconnected");
+ break;
+ case ST_PENDING:
+ sb.append("connection-pending");
+ break;
+ case ST_CONNECTED:
+ sb.append("connected");
+ if (readShutdown)
+ sb.append(" ishut");
+ if (writeShutdown)
+ sb.append(" oshut");
+ break;
+ }
+ if (localAddress != null) {
+ sb.append(" local=");
+ sb.append(localAddress.toString());
+ }
+ if (remoteAddress != null) {
+ sb.append(" remote=");
+ sb.append(remoteAddress.toString());
+ }
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/Cancellable.java b/src/share/classes/sun/nio/ch/Cancellable.java
new file mode 100644
index 000000000..ae08ed9eb
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/Cancellable.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+/**
+ * Implemented by asynchronous channels that require notification when an I/O
+ * operation is cancelled.
+ */
+
+interface Cancellable {
+ /**
+ * Invoked to notify channel that cancel has been invoked while holding
+ * the Future's lock.
+ */
+ void onCancel(PendingFuture<?,?> task);
+}
diff --git a/src/share/classes/sun/nio/ch/CompletedFuture.java b/src/share/classes/sun/nio/ch/CompletedFuture.java
new file mode 100644
index 000000000..221b9f8f5
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/CompletedFuture.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.AsynchronousChannel;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutionException;
+import java.io.IOException;
+
+/**
+ * A Future representing the result of an I/O operation that has already
+ * completed.
+ */
+
+final class CompletedFuture<V,A>
+ extends AbstractFuture<V,A>
+{
+ private final V result;
+ private final Throwable exc;
+
+ private CompletedFuture(AsynchronousChannel channel,
+ V result,
+ Throwable exc,
+ A attachment)
+ {
+ super(channel, attachment);
+ this.result = result;
+ this.exc = exc;
+ }
+
+ @SuppressWarnings("unchecked")
+ static <V,A> CompletedFuture<V,A> withResult(AsynchronousChannel channel,
+ V result,
+ A attachment)
+ {
+ return new CompletedFuture<V,A>(channel, result, null, attachment);
+ }
+
+ @SuppressWarnings("unchecked")
+ static <V,A> CompletedFuture<V,A> withFailure(AsynchronousChannel channel,
+ Throwable exc,
+ A attachment)
+ {
+ // exception must be IOException or SecurityException
+ if (!(exc instanceof IOException) && !(exc instanceof SecurityException))
+ exc = new IOException(exc);
+ return new CompletedFuture(channel, null, exc, attachment);
+ }
+
+ @Override
+ public V get() throws ExecutionException {
+ if (exc != null)
+ throw new ExecutionException(exc);
+ return result;
+ }
+
+ @Override
+ public V get(long timeout, TimeUnit unit) throws ExecutionException {
+ if (unit == null)
+ throw new NullPointerException();
+ if (exc != null)
+ throw new ExecutionException(exc);
+ return result;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ Throwable exception() {
+ return exc;
+ }
+
+ @Override
+ V value() {
+ return result;
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java
index 54cbfba16..e5ec5e063 100644
--- a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java
+++ b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -111,8 +111,12 @@ class DatagramChannelImpl
public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) {
super(sp);
if ((family != StandardProtocolFamily.INET) &&
- (family != StandardProtocolFamily.INET6)) {
- throw new UnsupportedOperationException("Protocol family not supported");
+ (family != StandardProtocolFamily.INET6))
+ {
+ if (family == null)
+ throw new NullPointerException("'family' is null");
+ else
+ throw new UnsupportedOperationException("Protocol family not supported");
}
if (family == StandardProtocolFamily.INET6) {
if (!Net.isIPv6Available()) {
@@ -149,28 +153,28 @@ class DatagramChannelImpl
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
- return null;
+ throw new ClosedChannelException();
return localAddress;
}
}
@Override
- public SocketAddress getConnectedAddress() throws IOException {
+ public SocketAddress getRemoteAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
- return null;
+ throw new ClosedChannelException();
return remoteAddress;
}
}
@Override
- public DatagramChannel setOption(SocketOption name, Object value)
+ public <T> DatagramChannel setOption(SocketOption<T> name, T value)
throws IOException
{
if (name == null)
throw new NullPointerException();
- if (!options().contains(name))
- throw new IllegalArgumentException("Invalid option name");
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
ensureOpen();
@@ -224,8 +228,8 @@ class DatagramChannelImpl
{
if (name == null)
throw new NullPointerException();
- if (!options().contains(name))
- throw new IllegalArgumentException("Invalid option name");
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
ensureOpen();
@@ -273,7 +277,7 @@ class DatagramChannelImpl
}
}
- private static class LazyInitialization {
+ private static class DefaultOptionsHolder {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
@@ -291,8 +295,8 @@ class DatagramChannelImpl
}
@Override
- public final Set<SocketOption<?>> options() {
- return LazyInitialization.defaultOptions;
+ public final Set<SocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
}
private void ensureOpen() throws ClosedChannelException {
@@ -864,23 +868,26 @@ class DatagramChannelImpl
}
// package-private
- void drop(MembershipKeyImpl key)
- throws IOException
- {
- assert key.getChannel() == this;
+ void drop(MembershipKeyImpl key) {
+ assert key.channel() == this;
synchronized (stateLock) {
if (!key.isValid())
return;
- if (family == StandardProtocolFamily.INET6) {
- MembershipKeyImpl.Type6 key6 =
- (MembershipKeyImpl.Type6)key;
- Net.drop6(fd, key6.group(), key6.index(), key6.source());
- } else {
- MembershipKeyImpl.Type4 key4 =
- (MembershipKeyImpl.Type4)key;
- Net.drop4(fd, key4.group(), key4.interfaceAddress(), key4.source());
+ try {
+ if (family == StandardProtocolFamily.INET6) {
+ MembershipKeyImpl.Type6 key6 =
+ (MembershipKeyImpl.Type6)key;
+ Net.drop6(fd, key6.groupAddress(), key6.index(), key6.source());
+ } else {
+ MembershipKeyImpl.Type4 key4 = (MembershipKeyImpl.Type4)key;
+ Net.drop4(fd, key4.groupAddress(), key4.interfaceAddress(),
+ key4.source());
+ }
+ } catch (IOException ioe) {
+ // should not happen
+ throw new AssertionError(ioe);
}
key.invalidate();
@@ -895,8 +902,8 @@ class DatagramChannelImpl
void block(MembershipKeyImpl key, InetAddress source)
throws IOException
{
- assert key.getChannel() == this;
- assert key.getSourceAddress() == null;
+ assert key.channel() == this;
+ assert key.sourceAddress() == null;
synchronized (stateLock) {
if (!key.isValid())
@@ -905,19 +912,19 @@ class DatagramChannelImpl
throw new IllegalArgumentException("Source address is a wildcard address");
if (source.isMulticastAddress())
throw new IllegalArgumentException("Source address is multicast address");
- if (source.getClass() != key.getGroup().getClass())
+ if (source.getClass() != key.group().getClass())
throw new IllegalArgumentException("Source address is different type to group");
int n;
if (family == StandardProtocolFamily.INET6) {
MembershipKeyImpl.Type6 key6 =
(MembershipKeyImpl.Type6)key;
- n = Net.block6(fd, key6.group(), key6.index(),
+ n = Net.block6(fd, key6.groupAddress(), key6.index(),
Net.inet6AsByteArray(source));
} else {
MembershipKeyImpl.Type4 key4 =
(MembershipKeyImpl.Type4)key;
- n = Net.block4(fd, key4.group(), key4.interfaceAddress(),
+ n = Net.block4(fd, key4.groupAddress(), key4.interfaceAddress(),
Net.inet4AsInt(source));
}
if (n == IOStatus.UNAVAILABLE) {
@@ -930,26 +937,29 @@ class DatagramChannelImpl
/**
* Unblock given source.
*/
- void unblock(MembershipKeyImpl key, InetAddress source)
- throws IOException
- {
- assert key.getChannel() == this;
- assert key.getSourceAddress() == null;
+ void unblock(MembershipKeyImpl key, InetAddress source) {
+ assert key.channel() == this;
+ assert key.sourceAddress() == null;
synchronized (stateLock) {
if (!key.isValid())
throw new IllegalStateException("key is no longer valid");
- if (family == StandardProtocolFamily.INET6) {
- MembershipKeyImpl.Type6 key6 =
- (MembershipKeyImpl.Type6)key;
- Net.unblock6(fd, key6.group(), key6.index(),
- Net.inet6AsByteArray(source));
- } else {
- MembershipKeyImpl.Type4 key4 =
- (MembershipKeyImpl.Type4)key;
- Net.unblock4(fd, key4.group(), key4.interfaceAddress(),
- Net.inet4AsInt(source));
+ try {
+ if (family == StandardProtocolFamily.INET6) {
+ MembershipKeyImpl.Type6 key6 =
+ (MembershipKeyImpl.Type6)key;
+ Net.unblock6(fd, key6.groupAddress(), key6.index(),
+ Net.inet6AsByteArray(source));
+ } else {
+ MembershipKeyImpl.Type4 key4 =
+ (MembershipKeyImpl.Type4)key;
+ Net.unblock4(fd, key4.groupAddress(), key4.interfaceAddress(),
+ Net.inet4AsInt(source));
+ }
+ } catch (IOException ioe) {
+ // should not happen
+ throw new AssertionError(ioe);
}
}
}
diff --git a/src/share/classes/sun/nio/ch/ExtendedSocketOption.java b/src/share/classes/sun/nio/ch/ExtendedSocketOption.java
index 86e787ef8..d695fd2a4 100644
--- a/src/share/classes/sun/nio/ch/ExtendedSocketOption.java
+++ b/src/share/classes/sun/nio/ch/ExtendedSocketOption.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008 Sun Microsystems, Inc. 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
diff --git a/src/share/classes/sun/nio/ch/FileChannelImpl.java b/src/share/classes/sun/nio/ch/FileChannelImpl.java
index 894c489cd..1e622e1f4 100644
--- a/src/share/classes/sun/nio/ch/FileChannelImpl.java
+++ b/src/share/classes/sun/nio/ch/FileChannelImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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,27 +26,18 @@
package sun.nio.ch;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.BufferPoolMXBean;
import java.nio.channels.*;
-import java.nio.channels.spi.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.lang.ref.WeakReference;
-import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
import java.security.AccessController;
-import java.security.PrivilegedAction;
import javax.management.ObjectName;
import javax.management.MalformedObjectNameException;
-
import sun.misc.Cleaner;
import sun.security.action.GetPropertyAction;
@@ -55,14 +46,11 @@ public class FileChannelImpl
{
// Used to make native read and write calls
- private static final NativeDispatcher nd;
+ private static final FileDispatcher nd;
// Memory allocation size for mapping buffers
private static final long allocationGranularity;
- // Cached field for MappedByteBuffer.isAMappedBuffer
- private static final Field isAMappedBufferField;
-
// File descriptor
private final FileDescriptor fd;
@@ -107,20 +95,19 @@ public class FileChannelImpl
// -- Standard channel operations --
protected void implCloseChannel() throws IOException {
-
- nd.preClose(fd);
- threads.signal();
-
// Invalidate and release any locks that we still hold
if (fileLockTable != null) {
fileLockTable.removeAll( new FileLockTable.Releaser() {
public void release(FileLock fl) throws IOException {
((FileLockImpl)fl).invalidate();
- release0(fd, fl.position(), fl.size());
+ nd.release(fd, fl.position(), fl.size());
}
});
}
+ nd.preClose(fd);
+ threads.signalAndWait();
+
if (parent != null) {
// Close the fd via the parent stream's close method. The parent
@@ -141,12 +128,11 @@ public class FileChannelImpl
throw new NonReadableChannelException();
synchronized (positionLock) {
int n = 0;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return 0;
- ti = threads.add();
do {
n = IOUtil.read(fd, dst, -1, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -165,12 +151,11 @@ public class FileChannelImpl
throw new NonReadableChannelException();
synchronized (positionLock) {
long n = 0;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return 0;
- ti = threads.add();
do {
n = IOUtil.read(fd, dsts, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -198,12 +183,11 @@ public class FileChannelImpl
throw new NonWritableChannelException();
synchronized (positionLock) {
int n = 0;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return 0;
- ti = threads.add();
do {
n = IOUtil.write(fd, src, -1, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -222,12 +206,11 @@ public class FileChannelImpl
throw new NonWritableChannelException();
synchronized (positionLock) {
long n = 0;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return 0;
- ti = threads.add();
do {
n = IOUtil.write(fd, srcs, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -256,12 +239,11 @@ public class FileChannelImpl
ensureOpen();
synchronized (positionLock) {
long p = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return 0;
- ti = threads.add();
do {
p = position0(fd, -1);
} while ((p == IOStatus.INTERRUPTED) && isOpen());
@@ -280,12 +262,11 @@ public class FileChannelImpl
throw new IllegalArgumentException();
synchronized (positionLock) {
long p = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return null;
- ti = threads.add();
do {
p = position0(fd, newPosition);
} while ((p == IOStatus.INTERRUPTED) && isOpen());
@@ -302,14 +283,13 @@ public class FileChannelImpl
ensureOpen();
synchronized (positionLock) {
long s = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return -1;
- ti = threads.add();
do {
- s = size0(fd);
+ s = nd.size(fd);
} while ((s == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(s);
} finally {
@@ -331,12 +311,11 @@ public class FileChannelImpl
synchronized (positionLock) {
int rv = -1;
long p = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return null;
- ti = threads.add();
// get current position
do {
@@ -348,7 +327,7 @@ public class FileChannelImpl
// truncate file
do {
- rv = truncate0(fd, size);
+ rv = nd.truncate(fd, size);
} while ((rv == IOStatus.INTERRUPTED) && isOpen());
if (!isOpen())
return null;
@@ -371,14 +350,13 @@ public class FileChannelImpl
public void force(boolean metaData) throws IOException {
ensureOpen();
int rv = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return;
- ti = threads.add();
do {
- rv = force0(fd, metaData);
+ rv = nd.force(fd, metaData);
} while ((rv == IOStatus.INTERRUPTED) && isOpen());
} finally {
threads.remove(ti);
@@ -428,12 +406,11 @@ public class FileChannelImpl
return IOStatus.UNSUPPORTED;
long n = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return -1;
- ti = threads.add();
do {
n = transferTo0(thisFDVal, position, icount, targetFDVal);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -635,12 +612,11 @@ public class FileChannelImpl
throw new NonReadableChannelException();
ensureOpen();
int n = 0;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return -1;
- ti = threads.add();
do {
n = IOUtil.read(fd, dst, position, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -661,12 +637,11 @@ public class FileChannelImpl
throw new NonWritableChannelException();
ensureOpen();
int n = 0;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return -1;
- ti = threads.add();
do {
n = IOUtil.write(fd, src, position, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -756,12 +731,11 @@ public class FileChannelImpl
throw new NonReadableChannelException();
long addr = -1;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return null;
- ti = threads.add();
if (size() < position + size) { // Extend file size
if (!writable) {
throw new IOException("Channel not open for writing " +
@@ -769,7 +743,7 @@ public class FileChannelImpl
}
int rv;
do {
- rv = truncate0(fd, position + size);
+ rv = nd.truncate(fd, position + size);
} while ((rv == IOStatus.INTERRUPTED) && isOpen());
}
if (size == 0) {
@@ -863,10 +837,7 @@ public class FileChannelImpl
// -- Locks --
- public static final int NO_LOCK = -1; // Failed to lock
- public static final int LOCKED = 0; // Obtained requested lock
- public static final int RET_EX_LOCK = 1; // Obtained exclusive lock
- public static final int INTERRUPTED = 2; // Request interrupted
+
// keeps track of locks on this file
private volatile FileLockTable fileLockTable;
@@ -896,12 +867,21 @@ public class FileChannelImpl
return isSharedFileLockTable;
}
- private FileLockTable fileLockTable() {
+ private FileLockTable fileLockTable() throws IOException {
if (fileLockTable == null) {
synchronized (this) {
if (fileLockTable == null) {
- fileLockTable = isSharedFileLockTable() ?
- new SharedFileLockTable(this) : new SimpleFileLockTable();
+ if (isSharedFileLockTable()) {
+ int ti = threads.add();
+ try {
+ ensureOpen();
+ fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);
+ } finally {
+ threads.remove(ti);
+ }
+ } else {
+ fileLockTable = new SimpleFileLockTable();
+ }
}
}
}
@@ -920,21 +900,20 @@ public class FileChannelImpl
FileLockTable flt = fileLockTable();
flt.add(fli);
boolean i = true;
- int ti = -1;
+ int ti = threads.add();
try {
begin();
if (!isOpen())
return null;
- ti = threads.add();
- int result = lock0(fd, true, position, size, shared);
- if (result == RET_EX_LOCK) {
+ int result = nd.lock(fd, true, position, size, shared);
+ if (result == FileDispatcher.RET_EX_LOCK) {
assert shared;
FileLockImpl fli2 = new FileLockImpl(this, position, size,
false);
flt.replace(fli, fli2);
return fli2;
}
- if (result == INTERRUPTED || result == NO_LOCK) {
+ if (result == FileDispatcher.INTERRUPTED || result == FileDispatcher.NO_LOCK) {
flt.remove(fli);
i = false;
}
@@ -963,77 +942,54 @@ public class FileChannelImpl
FileLockImpl fli = new FileLockImpl(this, position, size, shared);
FileLockTable flt = fileLockTable();
flt.add(fli);
- int result = lock0(fd, false, position, size, shared);
- if (result == NO_LOCK) {
- flt.remove(fli);
- return null;
- }
- if (result == RET_EX_LOCK) {
- assert shared;
- FileLockImpl fli2 = new FileLockImpl(this, position, size,
- false);
- flt.replace(fli, fli2);
- return fli2;
+ int result;
+
+ int ti = threads.add();
+ try {
+ try {
+ ensureOpen();
+ result = nd.lock(fd, false, position, size, shared);
+ } catch (IOException e) {
+ flt.remove(fli);
+ throw e;
+ }
+ if (result == FileDispatcher.NO_LOCK) {
+ flt.remove(fli);
+ return null;
+ }
+ if (result == FileDispatcher.RET_EX_LOCK) {
+ assert shared;
+ FileLockImpl fli2 = new FileLockImpl(this, position, size,
+ false);
+ flt.replace(fli, fli2);
+ return fli2;
+ }
+ return fli;
+ } finally {
+ threads.remove(ti);
}
- return fli;
}
void release(FileLockImpl fli) throws IOException {
ensureOpen();
- release0(fd, fli.position(), fli.size());
+ int ti = threads.add();
+ try {
+ ensureOpen();
+ nd.release(fd, fli.position(), fli.size());
+ } finally {
+ threads.remove(ti);
+ }
assert fileLockTable != null;
fileLockTable.remove(fli);
}
-
- // -- File lock support --
-
- /**
- * A table of FileLocks.
- */
- private interface FileLockTable {
- /**
- * Adds a file lock to the table.
- *
- * @throws OverlappingFileLockException if the file lock overlaps
- * with an existing file lock in the table
- */
- void add(FileLock fl) throws OverlappingFileLockException;
-
- /**
- * Remove an existing file lock from the table.
- */
- void remove(FileLock fl);
-
- /**
- * An implementation of this interface releases a given file lock.
- * Used with removeAll.
- */
- interface Releaser {
- void release(FileLock fl) throws IOException;
- }
-
- /**
- * Removes all file locks from the table.
- * <p>
- * The Releaser#release method is invoked for each file lock before
- * it is removed.
- *
- * @throws IOException if the release method throws IOException
- */
- void removeAll(Releaser r) throws IOException;
-
- /**
- * Replaces an existing file lock in the table.
- */
- void replace(FileLock fl1, FileLock fl2);
- }
+ // -- File lock support --
/**
* A simple file lock table that maintains a list of FileLocks obtained by a
* FileChannel. Use to get 1.4/5.0 behaviour.
*/
- private static class SimpleFileLockTable implements FileLockTable {
+ private static class SimpleFileLockTable extends FileLockTable {
// synchronize on list for access
private List<FileLock> lockList = new ArrayList<FileLock>(2);
@@ -1083,207 +1039,8 @@ public class FileChannelImpl
}
}
- /**
- * A weak reference to a FileLock.
- * <p>
- * SharedFileLockTable uses a list of file lock references to avoid keeping the
- * FileLock (and FileChannel) alive.
- */
- private static class FileLockReference extends WeakReference<FileLock> {
- private FileKey fileKey;
-
- FileLockReference(FileLock referent,
- ReferenceQueue<FileLock> queue,
- FileKey key) {
- super(referent, queue);
- this.fileKey = key;
- }
-
- private FileKey fileKey() {
- return fileKey;
- }
- }
-
- /**
- * A file lock table that is over a system-wide map of all file locks.
- */
- private static class SharedFileLockTable implements FileLockTable {
- // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
- // The map value is a list of file locks represented by FileLockReferences.
- // All access to the list must be synchronized on the list.
- private static ConcurrentHashMap<FileKey, ArrayList<FileLockReference>> lockMap =
- new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
-
- // reference queue for cleared refs
- private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
-
- // the enclosing file channel
- private FileChannelImpl fci;
-
- // File key for the file that this channel is connected to
- private FileKey fileKey;
-
- public SharedFileLockTable(FileChannelImpl fci) {
- this.fci = fci;
- this.fileKey = FileKey.create(fci.fd);
- }
-
- public void add(FileLock fl) throws OverlappingFileLockException {
- ArrayList<FileLockReference> list = lockMap.get(fileKey);
-
- for (;;) {
-
- // The key isn't in the map so we try to create it atomically
- if (list == null) {
- list = new ArrayList<FileLockReference>(2);
- ArrayList<FileLockReference> prev;
- synchronized (list) {
- prev = lockMap.putIfAbsent(fileKey, list);
- if (prev == null) {
- // we successfully created the key so we add the file lock
- list.add(new FileLockReference(fl, queue, fileKey));
- break;
- }
- }
- // someone else got there first
- list = prev;
- }
-
- // There is already a key. It is possible that some other thread
- // is removing it so we re-fetch the value from the map. If it
- // hasn't changed then we check the list for overlapping locks
- // and add the new lock to the list.
- synchronized (list) {
- ArrayList<FileLockReference> current = lockMap.get(fileKey);
- if (list == current) {
- checkList(list, fl.position(), fl.size());
- list.add(new FileLockReference(fl, queue, fileKey));
- break;
- }
- list = current;
- }
-
- }
-
- // process any stale entries pending in the reference queue
- removeStaleEntries();
- }
-
- private void removeKeyIfEmpty(FileKey fk, ArrayList<FileLockReference> list) {
- assert Thread.holdsLock(list);
- assert lockMap.get(fk) == list;
- if (list.isEmpty()) {
- lockMap.remove(fk);
- }
- }
-
- public void remove(FileLock fl) {
- assert fl != null;
-
- // the lock must exist so the list of locks must be present
- ArrayList<FileLockReference> list = lockMap.get(fileKey);
- assert list != null;
-
- synchronized (list) {
- int index = 0;
- while (index < list.size()) {
- FileLockReference ref = list.get(index);
- FileLock lock = ref.get();
- if (lock == fl) {
- assert (lock != null) && (lock.channel() == fci);
- ref.clear();
- list.remove(index);
- break;
- }
- index++;
- }
- }
- }
-
- public void removeAll(Releaser releaser) throws IOException {
- ArrayList<FileLockReference> list = lockMap.get(fileKey);
- if (list != null) {
- synchronized (list) {
- int index = 0;
- while (index < list.size()) {
- FileLockReference ref = list.get(index);
- FileLock lock = ref.get();
-
- // remove locks obtained by this channel
- if (lock != null && lock.channel() == fci) {
- // invoke the releaser to invalidate/release the lock
- releaser.release(lock);
-
- // remove the lock from the list
- ref.clear();
- list.remove(index);
- } else {
- index++;
- }
- }
-
- // once the lock list is empty we remove it from the map
- removeKeyIfEmpty(fileKey, list);
- }
- }
- }
-
- public void replace(FileLock fromLock, FileLock toLock) {
- // the lock must exist so there must be a list
- ArrayList<FileLockReference> list = lockMap.get(fileKey);
- assert list != null;
-
- synchronized (list) {
- for (int index=0; index<list.size(); index++) {
- FileLockReference ref = list.get(index);
- FileLock lock = ref.get();
- if (lock == fromLock) {
- ref.clear();
- list.set(index, new FileLockReference(toLock, queue, fileKey));
- break;
- }
- }
- }
- }
-
- // Check for overlapping file locks
- private void checkList(List<FileLockReference> list, long position, long size)
- throws OverlappingFileLockException
- {
- assert Thread.holdsLock(list);
- for (FileLockReference ref: list) {
- FileLock fl = ref.get();
- if (fl != null && fl.overlaps(position, size))
- throw new OverlappingFileLockException();
- }
- }
-
- // Process the reference queue
- private void removeStaleEntries() {
- FileLockReference ref;
- while ((ref = (FileLockReference)queue.poll()) != null) {
- FileKey fk = ref.fileKey();
- ArrayList<FileLockReference> list = lockMap.get(fk);
- if (list != null) {
- synchronized (list) {
- list.remove(ref);
- removeKeyIfEmpty(fk, list);
- }
- }
- }
- }
- }
-
// -- Native methods --
- // Grabs a file lock
- native int lock0(FileDescriptor fd, boolean blocking, long pos, long size,
- boolean shared) throws IOException;
-
- // Releases a file lock
- native void release0(FileDescriptor fd, long pos, long size)
- throws IOException;
-
// Creates a new mapping
private native long map0(int prot, long position, long length)
throws IOException;
@@ -1291,12 +1048,6 @@ public class FileChannelImpl
// Removes an existing mapping
private static native int unmap0(long address, long length);
- // Forces output to device
- private native int force0(FileDescriptor fd, boolean metaData);
-
- // Truncates a file
- private native int truncate0(FileDescriptor fd, long size);
-
// Transfers from src to dst, or returns -2 if kernel can't do that
private native long transferTo0(int src, long position, long count, int dst);
@@ -1305,18 +1056,13 @@ public class FileChannelImpl
// otherwise the position is set to offset
private native long position0(FileDescriptor fd, long offset);
- // Reports this file's size
- private native long size0(FileDescriptor fd);
-
// Caches fieldIDs
private static native long initIDs();
static {
Util.load();
allocationGranularity = initIDs();
- nd = new FileDispatcher();
- isAMappedBufferField = Reflect.lookupField("java.nio.MappedByteBuffer",
- "isAMappedBuffer");
+ nd = new FileDispatcherImpl();
}
}
diff --git a/src/share/classes/sun/nio/ch/FileDispatcher.java b/src/share/classes/sun/nio/ch/FileDispatcher.java
new file mode 100644
index 000000000..9f8a0f79e
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/FileDispatcher.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.io.*;
+
+abstract class FileDispatcher extends NativeDispatcher {
+
+ public static final int NO_LOCK = -1; // Failed to lock
+ public static final int LOCKED = 0; // Obtained requested lock
+ public static final int RET_EX_LOCK = 1; // Obtained exclusive lock
+ public static final int INTERRUPTED = 2; // Request interrupted
+
+ abstract int force(FileDescriptor fd, boolean metaData) throws IOException;
+
+ abstract int truncate(FileDescriptor fd, long size) throws IOException;
+
+ abstract long size(FileDescriptor fd) throws IOException;
+
+ abstract int lock(FileDescriptor fd, boolean blocking, long pos, long size,
+ boolean shared) throws IOException;
+
+ abstract void release(FileDescriptor fd, long pos, long size)
+ throws IOException;
+}
diff --git a/src/share/classes/sun/nio/ch/FileLockImpl.java b/src/share/classes/sun/nio/ch/FileLockImpl.java
index 721faf1a0..9efd1532b 100644
--- a/src/share/classes/sun/nio/ch/FileLockImpl.java
+++ b/src/share/classes/sun/nio/ch/FileLockImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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,9 +26,7 @@
package sun.nio.ch;
import java.io.IOException;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.FileLock;
-import java.nio.channels.FileChannel;
+import java.nio.channels.*;
public class FileLockImpl
extends FileLock
@@ -41,6 +39,12 @@ public class FileLockImpl
this.valid = true;
}
+ FileLockImpl(AsynchronousFileChannel channel, long position, long size, boolean shared)
+ {
+ super(channel, position, size, shared);
+ this.valid = true;
+ }
+
public synchronized boolean isValid() {
return valid;
}
@@ -50,10 +54,15 @@ public class FileLockImpl
}
public synchronized void release() throws IOException {
- if (!channel().isOpen())
+ Channel ch = acquiredBy();
+ if (!ch.isOpen())
throw new ClosedChannelException();
if (valid) {
- ((FileChannelImpl)channel()).release(this);
+ if (ch instanceof FileChannelImpl)
+ ((FileChannelImpl)ch).release(this);
+ else if (ch instanceof AsynchronousFileChannelImpl)
+ ((AsynchronousFileChannelImpl)ch).release(this);
+ else throw new AssertionError();
valid = false;
}
}
diff --git a/src/share/classes/sun/nio/ch/FileLockTable.java b/src/share/classes/sun/nio/ch/FileLockTable.java
new file mode 100644
index 000000000..137ab8887
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/FileLockTable.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2005-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.lang.ref.*;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+abstract class FileLockTable {
+ protected FileLockTable() {
+ }
+
+ /**
+ * Creates and returns a file lock table for a channel that is connected to
+ * the a system-wide map of all file locks for the Java virtual machine.
+ */
+ public static FileLockTable newSharedFileLockTable(Channel channel,
+ FileDescriptor fd)
+ throws IOException
+ {
+ return new SharedFileLockTable(channel, fd);
+ }
+
+ /**
+ * Adds a file lock to the table.
+ *
+ * @throws OverlappingFileLockException if the file lock overlaps
+ * with an existing file lock in the table
+ */
+ public abstract void add(FileLock fl) throws OverlappingFileLockException;
+
+ /**
+ * Remove an existing file lock from the table.
+ */
+ public abstract void remove(FileLock fl);
+
+ /**
+ * An implementation of this interface releases a given file lock.
+ * Used with removeAll.
+ */
+ public abstract interface Releaser {
+ void release(FileLock fl) throws IOException;
+ }
+
+ /**
+ * Removes all file locks from the table.
+ * <p>
+ * The Releaser#release method is invoked for each file lock before
+ * it is removed.
+ *
+ * @throws IOException if the release method throws IOException
+ */
+ public abstract void removeAll(Releaser r) throws IOException;
+
+ /**
+ * Replaces an existing file lock in the table.
+ */
+ public abstract void replace(FileLock fl1, FileLock fl2);
+}
+
+
+/**
+ * A file lock table that is over a system-wide map of all file locks.
+ */
+class SharedFileLockTable extends FileLockTable {
+
+ /**
+ * A weak reference to a FileLock.
+ * <p>
+ * SharedFileLockTable uses a list of file lock references to avoid keeping the
+ * FileLock (and FileChannel) alive.
+ */
+ private static class FileLockReference extends WeakReference<FileLock> {
+ private FileKey fileKey;
+
+ FileLockReference(FileLock referent,
+ ReferenceQueue<FileLock> queue,
+ FileKey key) {
+ super(referent, queue);
+ this.fileKey = key;
+ }
+
+ FileKey fileKey() {
+ return fileKey;
+ }
+ }
+
+ // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
+ // The map value is a list of file locks represented by FileLockReferences.
+ // All access to the list must be synchronized on the list.
+ private static ConcurrentHashMap<FileKey, List<FileLockReference>> lockMap =
+ new ConcurrentHashMap<FileKey, List<FileLockReference>>();
+
+ // reference queue for cleared refs
+ private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
+
+ // The connection to which this table is connected
+ private final Channel channel;
+
+ // File key for the file that this channel is connected to
+ private final FileKey fileKey;
+
+ SharedFileLockTable(Channel channel, FileDescriptor fd) throws IOException {
+ this.channel = channel;
+ this.fileKey = FileKey.create(fd);
+ }
+
+ @Override
+ public void add(FileLock fl) throws OverlappingFileLockException {
+ List<FileLockReference> list = lockMap.get(fileKey);
+
+ for (;;) {
+
+ // The key isn't in the map so we try to create it atomically
+ if (list == null) {
+ list = new ArrayList<FileLockReference>(2);
+ List<FileLockReference> prev;
+ synchronized (list) {
+ prev = lockMap.putIfAbsent(fileKey, list);
+ if (prev == null) {
+ // we successfully created the key so we add the file lock
+ list.add(new FileLockReference(fl, queue, fileKey));
+ break;
+ }
+ }
+ // someone else got there first
+ list = prev;
+ }
+
+ // There is already a key. It is possible that some other thread
+ // is removing it so we re-fetch the value from the map. If it
+ // hasn't changed then we check the list for overlapping locks
+ // and add the new lock to the list.
+ synchronized (list) {
+ List<FileLockReference> current = lockMap.get(fileKey);
+ if (list == current) {
+ checkList(list, fl.position(), fl.size());
+ list.add(new FileLockReference(fl, queue, fileKey));
+ break;
+ }
+ list = current;
+ }
+
+ }
+
+ // process any stale entries pending in the reference queue
+ removeStaleEntries();
+ }
+
+ private void removeKeyIfEmpty(FileKey fk, List<FileLockReference> list) {
+ assert Thread.holdsLock(list);
+ assert lockMap.get(fk) == list;
+ if (list.isEmpty()) {
+ lockMap.remove(fk);
+ }
+ }
+
+ @Override
+ public void remove(FileLock fl) {
+ assert fl != null;
+
+ // the lock must exist so the list of locks must be present
+ List<FileLockReference> list = lockMap.get(fileKey);
+ if (list == null) return;
+
+ synchronized (list) {
+ int index = 0;
+ while (index < list.size()) {
+ FileLockReference ref = list.get(index);
+ FileLock lock = ref.get();
+ if (lock == fl) {
+ assert (lock != null) && (lock.channel() == channel);
+ ref.clear();
+ list.remove(index);
+ break;
+ }
+ index++;
+ }
+ }
+ }
+
+ @Override
+ public void removeAll(Releaser releaser) throws IOException {
+ List<FileLockReference> list = lockMap.get(fileKey);
+ if (list != null) {
+ synchronized (list) {
+ int index = 0;
+ while (index < list.size()) {
+ FileLockReference ref = list.get(index);
+ FileLock lock = ref.get();
+
+ // remove locks obtained by this channel
+ if (lock != null && lock.channel() == channel) {
+ // invoke the releaser to invalidate/release the lock
+ releaser.release(lock);
+
+ // remove the lock from the list
+ ref.clear();
+ list.remove(index);
+ } else {
+ index++;
+ }
+ }
+
+ // once the lock list is empty we remove it from the map
+ removeKeyIfEmpty(fileKey, list);
+ }
+ }
+ }
+
+ @Override
+ public void replace(FileLock fromLock, FileLock toLock) {
+ // the lock must exist so there must be a list
+ List<FileLockReference> list = lockMap.get(fileKey);
+ assert list != null;
+
+ synchronized (list) {
+ for (int index=0; index<list.size(); index++) {
+ FileLockReference ref = list.get(index);
+ FileLock lock = ref.get();
+ if (lock == fromLock) {
+ ref.clear();
+ list.set(index, new FileLockReference(toLock, queue, fileKey));
+ break;
+ }
+ }
+ }
+ }
+
+ // Check for overlapping file locks
+ private void checkList(List<FileLockReference> list, long position, long size)
+ throws OverlappingFileLockException
+ {
+ assert Thread.holdsLock(list);
+ for (FileLockReference ref: list) {
+ FileLock fl = ref.get();
+ if (fl != null && fl.overlaps(position, size))
+ throw new OverlappingFileLockException();
+ }
+ }
+
+ // Process the reference queue
+ private void removeStaleEntries() {
+ FileLockReference ref;
+ while ((ref = (FileLockReference)queue.poll()) != null) {
+ FileKey fk = ref.fileKey();
+ List<FileLockReference> list = lockMap.get(fk);
+ if (list != null) {
+ synchronized (list) {
+ list.remove(ref);
+ removeKeyIfEmpty(fk, list);
+ }
+ }
+ }
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/Groupable.java b/src/share/classes/sun/nio/ch/Groupable.java
new file mode 100644
index 000000000..b1760152b
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/Groupable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+/**
+ * Implemented by asynchronous channels that can be associated with an
+ * asynchronous channel group.
+ */
+
+interface Groupable {
+ AsynchronousChannelGroupImpl group();
+}
diff --git a/src/share/classes/sun/nio/ch/IOUtil.java b/src/share/classes/sun/nio/ch/IOUtil.java
index f57b724b5..8d5bb13a2 100644
--- a/src/share/classes/sun/nio/ch/IOUtil.java
+++ b/src/share/classes/sun/nio/ch/IOUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -27,10 +27,7 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.*;
import java.nio.ByteBuffer;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
/**
@@ -47,7 +44,6 @@ class IOUtil {
*/
private static int remaining(ByteBuffer[] bufs) {
int numBufs = bufs.length;
- boolean remaining = false;
for (int i=0; i<numBufs; i++) {
if (bufs[i].hasRemaining()) {
return i;
@@ -138,74 +134,82 @@ class IOUtil {
bufs = skipBufs(bufs, nextWithRemaining);
int numBufs = bufs.length;
- int bytesReadyToWrite = 0;
// Create shadow to ensure DirectByteBuffers are used
ByteBuffer[] shadow = new ByteBuffer[numBufs];
- for (int i=0; i<numBufs; i++) {
- if (!(bufs[i] instanceof DirectBuffer)) {
- int pos = bufs[i].position();
- int lim = bufs[i].limit();
- assert (pos <= lim);
- int rem = (pos <= lim ? lim - pos : 0);
-
- ByteBuffer bb = ByteBuffer.allocateDirect(rem);
- shadow[i] = bb;
- // Leave slow buffer position untouched; it will be updated
- // after we see how many bytes were really written out
- bb.put(bufs[i]);
- bufs[i].position(pos);
- bb.flip();
- } else {
- shadow[i] = bufs[i];
- }
- }
-
- IOVecWrapper vec = null;
- long bytesWritten = 0;
try {
- // Create a native iovec array
- vec= new IOVecWrapper(numBufs);
-
- // Fill in the iovec array with appropriate data
for (int i=0; i<numBufs; i++) {
- ByteBuffer nextBuffer = shadow[i];
- // put in the buffer addresses
- long pos = nextBuffer.position();
- long len = nextBuffer.limit() - pos;
- bytesReadyToWrite += len;
- vec.putBase(i, ((DirectBuffer)nextBuffer).address() + pos);
- vec.putLen(i, len);
+ if (!(bufs[i] instanceof DirectBuffer)) {
+ int pos = bufs[i].position();
+ int lim = bufs[i].limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+ shadow[i] = bb;
+ // Leave slow buffer position untouched; it will be updated
+ // after we see how many bytes were really written out
+ bb.put(bufs[i]);
+ bufs[i].position(pos);
+ bb.flip();
+ } else {
+ shadow[i] = bufs[i];
+ }
}
- // Invoke native call to fill the buffers
- bytesWritten = nd.writev(fd, vec.address, numBufs);
- } finally {
- vec.free();
- }
- long returnVal = bytesWritten;
+ IOVecWrapper vec = null;
+ long bytesWritten = 0;
+ try {
+ // Create a native iovec array
+ vec= new IOVecWrapper(numBufs);
+
+ // Fill in the iovec array with appropriate data
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer nextBuffer = shadow[i];
+ // put in the buffer addresses
+ long pos = nextBuffer.position();
+ long len = nextBuffer.limit() - pos;
+ vec.putBase(i, ((DirectBuffer)nextBuffer).address() + pos);
+ vec.putLen(i, len);
+ }
- // Notify the buffers how many bytes were taken
- for (int i=0; i<numBufs; i++) {
- ByteBuffer nextBuffer = bufs[i];
- int pos = nextBuffer.position();
- int lim = nextBuffer.limit();
- assert (pos <= lim);
- int len = (pos <= lim ? lim - pos : lim);
- if (bytesWritten >= len) {
- bytesWritten -= len;
- int newPosition = pos + len;
- nextBuffer.position(newPosition);
- } else { // Buffers not completely filled
- if (bytesWritten > 0) {
- assert(pos + bytesWritten < (long)Integer.MAX_VALUE);
- int newPosition = (int)(pos + bytesWritten);
+ // Invoke native call to fill the buffers
+ bytesWritten = nd.writev(fd, vec.address, numBufs);
+ } finally {
+ vec.free();
+ }
+ long returnVal = bytesWritten;
+
+ // Notify the buffers how many bytes were taken
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer nextBuffer = bufs[i];
+ int pos = nextBuffer.position();
+ int lim = nextBuffer.limit();
+ assert (pos <= lim);
+ int len = (pos <= lim ? lim - pos : lim);
+ if (bytesWritten >= len) {
+ bytesWritten -= len;
+ int newPosition = pos + len;
nextBuffer.position(newPosition);
+ } else { // Buffers not completely filled
+ if (bytesWritten > 0) {
+ assert(pos + bytesWritten < (long)Integer.MAX_VALUE);
+ int newPosition = (int)(pos + bytesWritten);
+ nextBuffer.position(newPosition);
+ }
+ break;
+ }
+ }
+ return returnVal;
+ } finally {
+ // return any substituted buffers to cache
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer bb = shadow[i];
+ if (bb != null && bb != bufs[i]) {
+ Util.releaseTemporaryDirectBuffer(bb);
}
- break;
}
}
- return returnVal;
}
static int read(FileDescriptor fd, ByteBuffer dst, long position,
@@ -270,68 +274,83 @@ class IOUtil {
// Read into the shadow to ensure DirectByteBuffers are used
ByteBuffer[] shadow = new ByteBuffer[numBufs];
- for (int i=0; i<numBufs; i++) {
- if (bufs[i].isReadOnly())
- throw new IllegalArgumentException("Read-only buffer");
- if (!(bufs[i] instanceof DirectBuffer)) {
- shadow[i] = ByteBuffer.allocateDirect(bufs[i].remaining());
- } else {
- shadow[i] = bufs[i];
- }
- }
-
- IOVecWrapper vec = null;
- long bytesRead = 0;
+ boolean usingSlowBuffers = false;
try {
- // Create a native iovec array
- vec = new IOVecWrapper(numBufs);
-
- // Fill in the iovec array with appropriate data
for (int i=0; i<numBufs; i++) {
- ByteBuffer nextBuffer = shadow[i];
- // put in the buffer addresses
- long pos = nextBuffer.position();
- long len = nextBuffer.remaining();
- vec.putBase(i, ((DirectBuffer)nextBuffer).address() + pos);
- vec.putLen(i, len);
+ if (bufs[i].isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+ if (!(bufs[i] instanceof DirectBuffer)) {
+ shadow[i] = Util.getTemporaryDirectBuffer(bufs[i].remaining());
+ usingSlowBuffers = true;
+ } else {
+ shadow[i] = bufs[i];
+ }
}
- // Invoke native call to fill the buffers
- bytesRead = nd.readv(fd, vec.address, numBufs);
- } finally {
- vec.free();
- }
- long returnVal = bytesRead;
+ IOVecWrapper vec = null;
+ long bytesRead = 0;
+ try {
+ // Create a native iovec array
+ vec = new IOVecWrapper(numBufs);
+
+ // Fill in the iovec array with appropriate data
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer nextBuffer = shadow[i];
+ // put in the buffer addresses
+ long pos = nextBuffer.position();
+ long len = nextBuffer.remaining();
+ vec.putBase(i, ((DirectBuffer)nextBuffer).address() + pos);
+ vec.putLen(i, len);
+ }
- // Notify the buffers how many bytes were read
- for (int i=0; i<numBufs; i++) {
- ByteBuffer nextBuffer = shadow[i];
- // Note: should this have been cached from above?
- int pos = nextBuffer.position();
- int len = nextBuffer.remaining();
- if (bytesRead >= len) {
- bytesRead -= len;
- int newPosition = pos + len;
- nextBuffer.position(newPosition);
- } else { // Buffers not completely filled
- if (bytesRead > 0) {
- assert(pos + bytesRead < (long)Integer.MAX_VALUE);
- int newPosition = (int)(pos + bytesRead);
+ // Invoke native call to fill the buffers
+ bytesRead = nd.readv(fd, vec.address, numBufs);
+ } finally {
+ vec.free();
+ }
+ long returnVal = bytesRead;
+
+ // Notify the buffers how many bytes were read
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer nextBuffer = shadow[i];
+ // Note: should this have been cached from above?
+ int pos = nextBuffer.position();
+ int len = nextBuffer.remaining();
+ if (bytesRead >= len) {
+ bytesRead -= len;
+ int newPosition = pos + len;
nextBuffer.position(newPosition);
+ } else { // Buffers not completely filled
+ if (bytesRead > 0) {
+ assert(pos + bytesRead < (long)Integer.MAX_VALUE);
+ int newPosition = (int)(pos + bytesRead);
+ nextBuffer.position(newPosition);
+ }
+ break;
}
- break;
}
- }
- // Put results from shadow into the slow buffers
- for (int i=0; i<numBufs; i++) {
- if (!(bufs[i] instanceof DirectBuffer)) {
- shadow[i].flip();
- bufs[i].put(shadow[i]);
+ // Put results from shadow into the slow buffers
+ if (usingSlowBuffers) {
+ for (int i=0; i<numBufs; i++) {
+ if (!(bufs[i] instanceof DirectBuffer)) {
+ shadow[i].flip();
+ bufs[i].put(shadow[i]);
+ }
+ }
+ }
+ return returnVal;
+ } finally {
+ // return any substituted buffers to cache
+ if (usingSlowBuffers) {
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer bb = shadow[i];
+ if (bb != null && bb != bufs[i]) {
+ Util.releaseTemporaryDirectBuffer(bb);
+ }
+ }
}
}
-
- return returnVal;
}
static FileDescriptor newFD(int i) {
diff --git a/src/share/classes/sun/nio/ch/Invoker.java b/src/share/classes/sun/nio/ch/Invoker.java
new file mode 100644
index 000000000..182f7989a
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/Invoker.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.security.AccessController;
+import sun.security.action.GetIntegerAction;
+
+/**
+ * Defines static methods to invoke a completion handler or arbitrary task.
+ */
+
+class Invoker {
+ private Invoker() { }
+
+ // maximum number of completion handlers that may be invoked on the current
+ // thread before it re-directs invocations to the thread pool. This helps
+ // avoid stack overflow and lessens the risk of starvation.
+ private static final int maxHandlerInvokeCount = AccessController.doPrivileged(
+ new GetIntegerAction("sun.nio.ch.maxCompletionHandlersOnStack", 16));
+
+ // Per-thread object with reference to channel group and a counter for
+ // the number of completion handlers invoked. This should be reset to 0
+ // when all completion handlers have completed.
+ static class GroupAndInvokeCount {
+ private final AsynchronousChannelGroupImpl group;
+ private int handlerInvokeCount;
+ GroupAndInvokeCount(AsynchronousChannelGroupImpl group) {
+ this.group = group;
+ }
+ AsynchronousChannelGroupImpl group() {
+ return group;
+ }
+ int invokeCount() {
+ return handlerInvokeCount;
+ }
+ void setInvokeCount(int value) {
+ handlerInvokeCount = value;
+ }
+ void resetInvokeCount() {
+ handlerInvokeCount = 0;
+ }
+ void incrementInvokeCount() {
+ handlerInvokeCount++;
+ }
+ }
+ private static final ThreadLocal<GroupAndInvokeCount> myGroupAndInvokeCount =
+ new ThreadLocal<GroupAndInvokeCount>() {
+ @Override protected GroupAndInvokeCount initialValue() {
+ return null;
+ }
+ };
+
+ /**
+ * Binds this thread to the given group
+ */
+ static void bindToGroup(AsynchronousChannelGroupImpl group) {
+ myGroupAndInvokeCount.set(new GroupAndInvokeCount(group));
+ }
+
+ /**
+ * Returns the GroupAndInvokeCount object for this thread.
+ */
+ static GroupAndInvokeCount getGroupAndInvokeCount() {
+ return myGroupAndInvokeCount.get();
+ }
+
+ /**
+ * Returns true if the current thread is in a channel group's thread pool
+ */
+ static boolean isBoundToAnyGroup() {
+ return myGroupAndInvokeCount.get() != null;
+ }
+
+ /**
+ * Returns true if the current thread is in the given channel's thread
+ * pool and we haven't exceeded the maximum number of handler frames on
+ * the stack.
+ */
+ static boolean mayInvokeDirect(GroupAndInvokeCount myGroupAndInvokeCount,
+ AsynchronousChannelGroupImpl group)
+ {
+ if ((myGroupAndInvokeCount != null) &&
+ (myGroupAndInvokeCount.group() == group) &&
+ (myGroupAndInvokeCount.invokeCount() < maxHandlerInvokeCount))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Invoke handler without checking the thread identity or number of handlers
+ * on the thread stack.
+ */
+ @SuppressWarnings("unchecked")
+ static <V,A> void invokeUnchecked(CompletionHandler<V,? super A> handler,
+ AbstractFuture<V,A> result)
+ {
+ if (handler != null && !result.isCancelled()) {
+ Throwable exc = result.exception();
+ if (exc == null) {
+ handler.completed(result.value(), result.attachment());
+ } else {
+ handler.failed(exc, result.attachment());
+ }
+
+ // clear interrupt
+ Thread.interrupted();
+ }
+ }
+
+
+ /**
+ * Invoke handler after incrementing the invoke count.
+ */
+ static <V,A> void invokeDirect(GroupAndInvokeCount myGroupAndInvokeCount,
+ CompletionHandler<V,? super A> handler,
+ AbstractFuture<V,A> result)
+ {
+ myGroupAndInvokeCount.incrementInvokeCount();
+ invokeUnchecked(handler, result);
+ }
+
+ /**
+ * Invokes the handler. If the current thread is in the channel group's
+ * thread pool then the handler is invoked directly, otherwise it is
+ * invoked indirectly.
+ */
+ static <V,A> void invoke(CompletionHandler<V,? super A> handler,
+ AbstractFuture<V,A> result)
+ {
+ if (handler != null) {
+ boolean invokeDirect = false;
+ boolean identityOkay = false;
+ GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get();
+ if (thisGroupAndInvokeCount != null) {
+ AsynchronousChannel channel = result.channel();
+ if ((thisGroupAndInvokeCount.group() == ((Groupable)channel).group()))
+ identityOkay = true;
+ if (identityOkay &&
+ (thisGroupAndInvokeCount.invokeCount() < maxHandlerInvokeCount))
+ {
+ // group match
+ invokeDirect = true;
+ }
+ }
+ if (invokeDirect) {
+ thisGroupAndInvokeCount.incrementInvokeCount();
+ invokeUnchecked(handler, result);
+ } else {
+ try {
+ invokeIndirectly(handler, result);
+ } catch (RejectedExecutionException ree) {
+ // channel group shutdown; fallback to invoking directly
+ // if the current thread has the right identity.
+ if (identityOkay) {
+ invokeUnchecked(handler, result);
+ } else {
+ throw new ShutdownChannelGroupException();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Invokes the handler "indirectly" in the channel group's thread pool.
+ */
+ static <V,A> void invokeIndirectly(final CompletionHandler<V,? super A> handler,
+ final AbstractFuture<V,A> result)
+ {
+ if (handler != null) {
+ AsynchronousChannel channel = result.channel();
+ try {
+ ((Groupable)channel).group().executeOnPooledThread(new Runnable() {
+ public void run() {
+ GroupAndInvokeCount thisGroupAndInvokeCount =
+ myGroupAndInvokeCount.get();
+ if (thisGroupAndInvokeCount != null)
+ thisGroupAndInvokeCount.setInvokeCount(1);
+ invokeUnchecked(handler, result);
+ }
+ });
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ }
+ }
+
+ /**
+ * Invokes the handler "indirectly" in the given Executor
+ */
+ static <V,A> void invokeIndirectly(final CompletionHandler<V,? super A> handler,
+ final AbstractFuture<V,A> result,
+ Executor executor)
+ {
+ if (handler != null) {
+ try {
+ executor.execute(new Runnable() {
+ public void run() {
+ invokeUnchecked(handler, result);
+ }
+ });
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ }
+ }
+
+ /**
+ * Invokes the given task on the thread pool associated with the given
+ * channel. If the current thread is in the thread pool then the task is
+ * invoked directly.
+ */
+ static void invokeOnThreadInThreadPool(Groupable channel,
+ Runnable task)
+ {
+ boolean invokeDirect;
+ GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get();
+ AsynchronousChannelGroupImpl targetGroup = channel.group();
+ if (thisGroupAndInvokeCount == null) {
+ invokeDirect = false;
+ } else {
+ invokeDirect = (thisGroupAndInvokeCount.group == targetGroup);
+ }
+ try {
+ if (invokeDirect) {
+ task.run();
+ } else {
+ targetGroup.executeOnPooledThread(task);
+ }
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/MembershipKeyImpl.java b/src/share/classes/sun/nio/ch/MembershipKeyImpl.java
index 687f79c00..0089ffd44 100644
--- a/src/share/classes/sun/nio/ch/MembershipKeyImpl.java
+++ b/src/share/classes/sun/nio/ch/MembershipKeyImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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
@@ -85,7 +85,7 @@ class MembershipKeyImpl
this.sourceAddress = sourceAddress;
}
- int group() {
+ int groupAddress() {
return groupAddress;
}
@@ -120,7 +120,7 @@ class MembershipKeyImpl
this.sourceAddress = sourceAddress;
}
- byte[] group() {
+ byte[] groupAddress() {
return groupAddress;
}
@@ -142,28 +142,28 @@ class MembershipKeyImpl
valid = false;
}
- public void drop() throws IOException {
+ public void drop() {
// delegate to channel
((DatagramChannelImpl)ch).drop(this);
}
@Override
- public MulticastChannel getChannel() {
+ public MulticastChannel channel() {
return ch;
}
@Override
- public InetAddress getGroup() {
+ public InetAddress group() {
return group;
}
@Override
- public NetworkInterface getNetworkInterface() {
+ public NetworkInterface networkInterface() {
return interf;
}
@Override
- public InetAddress getSourceAddress() {
+ public InetAddress sourceAddress() {
return source;
}
@@ -191,9 +191,7 @@ class MembershipKeyImpl
}
@Override
- public MembershipKey unblock(InetAddress toUnblock)
- throws IOException
- {
+ public MembershipKey unblock(InetAddress toUnblock) {
synchronized (stateLock) {
if ((blockedSet == null) || !blockedSet.contains(toUnblock))
throw new IllegalStateException("not blocked");
diff --git a/src/share/classes/sun/nio/ch/MembershipRegistry.java b/src/share/classes/sun/nio/ch/MembershipRegistry.java
index 5a2a8ef74..fc90be39d 100644
--- a/src/share/classes/sun/nio/ch/MembershipRegistry.java
+++ b/src/share/classes/sun/nio/ch/MembershipRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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
@@ -55,20 +55,20 @@ class MembershipRegistry {
List<MembershipKeyImpl> keys = groups.get(group);
if (keys != null) {
for (MembershipKeyImpl key: keys) {
- if (key.getNetworkInterface().equals(interf)) {
+ if (key.networkInterface().equals(interf)) {
// already a member to receive all packets so return
// existing key or detect conflict
if (source == null) {
- if (key.getSourceAddress() == null)
+ if (key.sourceAddress() == null)
return key;
throw new IllegalStateException("Already a member to receive all packets");
}
// already have source-specific membership so return key
// or detect conflict
- if (key.getSourceAddress() == null)
+ if (key.sourceAddress() == null)
throw new IllegalStateException("Already have source-specific membership");
- if (source.equals(key.getSourceAddress()))
+ if (source.equals(key.sourceAddress()))
return key;
}
}
@@ -81,7 +81,7 @@ class MembershipRegistry {
* Add membership to the registry, returning a new membership key.
*/
void add(MembershipKeyImpl key) {
- InetAddress group = key.getGroup();
+ InetAddress group = key.group();
List<MembershipKeyImpl> keys;
if (groups == null) {
groups = new HashMap<InetAddress,List<MembershipKeyImpl>>();
@@ -100,7 +100,7 @@ class MembershipRegistry {
* Remove a key from the registry
*/
void remove(MembershipKeyImpl key) {
- InetAddress group = key.getGroup();
+ InetAddress group = key.group();
List<MembershipKeyImpl> keys = groups.get(group);
if (keys != null) {
Iterator<MembershipKeyImpl> i = keys.iterator();
@@ -120,9 +120,11 @@ class MembershipRegistry {
* Invalidate all keys in the registry
*/
void invalidateAll() {
- for (InetAddress group: groups.keySet()) {
- for (MembershipKeyImpl key: groups.get(group)) {
- key.invalidate();
+ if (groups != null) {
+ for (InetAddress group: groups.keySet()) {
+ for (MembershipKeyImpl key: groups.get(group)) {
+ key.invalidate();
+ }
}
}
}
diff --git a/src/share/classes/sun/nio/ch/NativeThreadSet.java b/src/share/classes/sun/nio/ch/NativeThreadSet.java
index 612befcbb..a009541d9 100644
--- a/src/share/classes/sun/nio/ch/NativeThreadSet.java
+++ b/src/share/classes/sun/nio/ch/NativeThreadSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2009 Sun Microsystems, Inc. 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
@@ -33,6 +33,7 @@ class NativeThreadSet {
private long[] elts;
private int used = 0;
+ private boolean waitingToEmpty;
NativeThreadSet(int n) {
elts = new long[n];
@@ -75,12 +76,14 @@ class NativeThreadSet {
synchronized (this) {
elts[i] = 0;
used--;
+ if (used == 0 && waitingToEmpty)
+ notifyAll();
}
}
// Signals all threads in this set.
//
- void signal() {
+ void signalAndWait() {
synchronized (this) {
int u = used;
int n = elts.length;
@@ -92,7 +95,12 @@ class NativeThreadSet {
if (--u == 0)
break;
}
+ waitingToEmpty = true;
+ while (used > 0) {
+ try {
+ wait();
+ } catch (InterruptedException ignore) { }
+ }
}
}
-
}
diff --git a/src/share/classes/sun/nio/ch/Net.java b/src/share/classes/sun/nio/ch/Net.java
index ba0ba0bdc..f910c5db0 100644
--- a/src/share/classes/sun/nio/ch/Net.java
+++ b/src/share/classes/sun/nio/ch/Net.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -207,7 +207,7 @@ class Net { // package-private
// -- Socket options
static void setSocketOption(FileDescriptor fd, ProtocolFamily family,
- SocketOption name, Object value)
+ SocketOption<?> name, Object value)
throws IOException
{
if (value == null)
@@ -262,7 +262,7 @@ class Net { // package-private
}
static Object getSocketOption(FileDescriptor fd, ProtocolFamily family,
- SocketOption name)
+ SocketOption<?> name)
throws IOException
{
Class<?> type = name.type();
diff --git a/src/share/classes/sun/nio/ch/OptionKey.java b/src/share/classes/sun/nio/ch/OptionKey.java
index 70ba8a6fa..884745335 100644
--- a/src/share/classes/sun/nio/ch/OptionKey.java
+++ b/src/share/classes/sun/nio/ch/OptionKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008 Sun Microsystems, Inc. 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
diff --git a/src/share/classes/sun/nio/ch/PendingFuture.java b/src/share/classes/sun/nio/ch/PendingFuture.java
new file mode 100644
index 000000000..d88cf233a
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/PendingFuture.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.io.IOException;
+
+/**
+ * A Future for a pending I/O operation. A PendingFuture allows for the
+ * attachment of an additional arbitrary context object and a timer task.
+ */
+
+final class PendingFuture<V,A>
+ extends AbstractFuture<V,A>
+{
+ private static final CancellationException CANCELLED =
+ new CancellationException();
+
+ private final CompletionHandler<V,? super A> handler;
+
+ // true if result (or exception) is available
+ private volatile boolean haveResult;
+ private volatile V result;
+ private volatile Throwable exc;
+
+ // latch for waiting (created lazily if needed)
+ private CountDownLatch latch;
+
+ // optional timer task that is cancelled when result becomes available
+ private Future<?> timeoutTask;
+
+ // optional context object
+ private volatile Object context;
+
+
+ PendingFuture(AsynchronousChannel channel,
+ CompletionHandler<V,? super A> handler,
+ A attachment,
+ Object context)
+ {
+ super(channel, attachment);
+ this.handler = handler;
+ this.context = context;
+ }
+
+ PendingFuture(AsynchronousChannel channel,
+ CompletionHandler<V,? super A> handler,
+ A attachment)
+ {
+ super(channel, attachment);
+ this.handler = handler;
+ }
+
+ CompletionHandler<V,? super A> handler() {
+ return handler;
+ }
+
+ void setContext(Object context) {
+ this.context = context;
+ }
+
+ Object getContext() {
+ return context;
+ }
+
+ void setTimeoutTask(Future<?> task) {
+ synchronized (this) {
+ if (haveResult) {
+ task.cancel(false);
+ } else {
+ this.timeoutTask = task;
+ }
+ }
+ }
+
+ // creates latch if required; return true if caller needs to wait
+ private boolean prepareForWait() {
+ synchronized (this) {
+ if (haveResult) {
+ return false;
+ } else {
+ if (latch == null)
+ latch = new CountDownLatch(1);
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Sets the result, or a no-op if the result or exception is already set.
+ */
+ boolean setResult(V res) {
+ synchronized (this) {
+ if (haveResult)
+ return false;
+ result = res;
+ haveResult = true;
+ if (timeoutTask != null)
+ timeoutTask.cancel(false);
+ if (latch != null)
+ latch.countDown();
+ return true;
+ }
+ }
+
+ /**
+ * Sets the result, or a no-op if the result or exception is already set.
+ */
+ boolean setFailure(Throwable x) {
+ if (!(x instanceof IOException) && !(x instanceof SecurityException))
+ x = new IOException(x);
+ synchronized (this) {
+ if (haveResult)
+ return false;
+ exc = x;
+ haveResult = true;
+ if (timeoutTask != null)
+ timeoutTask.cancel(false);
+ if (latch != null)
+ latch.countDown();
+ return true;
+ }
+ }
+
+ @Override
+ public V get() throws ExecutionException, InterruptedException {
+ if (!haveResult) {
+ boolean needToWait = prepareForWait();
+ if (needToWait)
+ latch.await();
+ }
+ if (exc != null) {
+ if (exc == CANCELLED)
+ throw new CancellationException();
+ throw new ExecutionException(exc);
+ }
+ return result;
+ }
+
+ @Override
+ public V get(long timeout, TimeUnit unit)
+ throws ExecutionException, InterruptedException, TimeoutException
+ {
+ if (!haveResult) {
+ boolean needToWait = prepareForWait();
+ if (needToWait)
+ if (!latch.await(timeout, unit)) throw new TimeoutException();
+ }
+ if (exc != null) {
+ if (exc == CANCELLED)
+ throw new CancellationException();
+ throw new ExecutionException(exc);
+ }
+ return result;
+ }
+
+ @Override
+ Throwable exception() {
+ return (exc != CANCELLED) ? exc : null;
+ }
+
+ @Override
+ V value() {
+ return result;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return (exc == CANCELLED);
+ }
+
+ @Override
+ public boolean isDone() {
+ return haveResult;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ synchronized (this) {
+ if (haveResult)
+ return false; // already completed
+
+ // A shutdown of the channel group will close all channels and
+ // shutdown the executor. To ensure that the completion handler
+ // is executed we queue the task while holding the lock.
+ if (handler != null) {
+ prepareForWait();
+ Runnable cancelTask = new Runnable() {
+ public void run() {
+ while (!haveResult) {
+ try {
+ latch.await();
+ } catch (InterruptedException ignore) { }
+ }
+ handler.cancelled(attachment());
+ }
+ };
+ AsynchronousChannel ch = channel();
+ if (ch instanceof Groupable) {
+ ((Groupable)ch).group().executeOnPooledThread(cancelTask);
+ } else {
+ if (ch instanceof AsynchronousFileChannelImpl) {
+ ((AsynchronousFileChannelImpl)ch).executor().execute(cancelTask);
+ } else {
+ throw new AssertionError("Should not get here");
+ }
+ }
+ }
+
+ // notify channel
+ if (channel() instanceof Cancellable)
+ ((Cancellable)channel()).onCancel(this);
+
+ // set result and cancel timer
+ exc = CANCELLED;
+ haveResult = true;
+ if (timeoutTask != null)
+ timeoutTask.cancel(false);
+ }
+
+ // close channel if forceful cancel
+ if (mayInterruptIfRunning) {
+ try {
+ channel().close();
+ } catch (IOException ignore) { }
+ }
+
+ // release waiters (this also releases the invoker)
+ if (latch != null)
+ latch.countDown();
+ return true;
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/Reflect.java b/src/share/classes/sun/nio/ch/Reflect.java
index 913357fcd..cc4716eb1 100644
--- a/src/share/classes/sun/nio/ch/Reflect.java
+++ b/src/share/classes/sun/nio/ch/Reflect.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -55,7 +55,7 @@ class Reflect { // package-private
{
try {
Class<?> cl = Class.forName(className);
- Constructor c = cl.getDeclaredConstructor(paramTypes);
+ Constructor<?> c = cl.getDeclaredConstructor(paramTypes);
setAccessible(c);
return c;
} catch (ClassNotFoundException x) {
@@ -79,7 +79,7 @@ class Reflect { // package-private
static Method lookupMethod(String className,
String methodName,
- Class[] paramTypes)
+ Class... paramTypes)
{
try {
Class<?> cl = Class.forName(className);
diff --git a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
index fd532980b..d509198ae 100644
--- a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
+++ b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -27,12 +27,9 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.reflect.*;
import java.net.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.*;
@@ -111,19 +108,19 @@ class ServerSocketChannelImpl
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
- return null;
+ throw new ClosedChannelException();
return localAddress;
}
}
@Override
- public ServerSocketChannel setOption(SocketOption name, Object value)
+ public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
throws IOException
{
if (name == null)
throw new NullPointerException();
- if (!options().contains(name))
- throw new IllegalArgumentException("invalid option name");
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
@@ -142,8 +139,8 @@ class ServerSocketChannelImpl
{
if (name == null)
throw new NullPointerException();
- if (!options().contains(name))
- throw new IllegalArgumentException("invalid option name");
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
@@ -154,7 +151,7 @@ class ServerSocketChannelImpl
}
}
- private static class LazyInitialization {
+ private static class DefaultOptionsHolder {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
@@ -166,8 +163,8 @@ class ServerSocketChannelImpl
}
@Override
- public final Set<SocketOption<?>> options() {
- return LazyInitialization.defaultOptions;
+ public final Set<SocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
}
public boolean isBound() {
diff --git a/src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java b/src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java
new file mode 100644
index 000000000..47c3b2abd
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.net.*;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.*;
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+
+/**
+ * A prototype implementation of AsynchronousDatagramChannel, used to aid
+ * test and spec development.
+ */
+
+class SimpleAsynchronousDatagramChannelImpl
+ extends AsynchronousDatagramChannel implements Groupable, Cancellable
+{
+ private final DatagramChannel dc;
+ private final AsynchronousChannelGroupImpl group;
+ private final Object attachKey;
+ private boolean closed;
+
+ // used to coordinate timed and blocking reads
+ private final Object readLock = new Object();
+
+ // channel blocking mode (requires readLock)
+ private boolean isBlocking = true;
+
+ // number of blocking readers (requires readLock)
+ private int blockingReaderCount;
+
+ // true if timed read attempted while blocking read in progress (requires readLock)
+ private boolean transitionToNonBlocking;
+
+ // true if a blocking read is cancelled (requires readLock)
+ private boolean blockingReadKilledByCancel;
+
+ // temporary Selectors used by timed reads (requires readLock)
+ private Selector firstReader;
+ private Set<Selector> otherReaders;
+
+ SimpleAsynchronousDatagramChannelImpl(ProtocolFamily family,
+ AsynchronousChannelGroupImpl group)
+ throws IOException
+ {
+ super(group.provider());
+ this.dc = (family == null) ?
+ DatagramChannel.open() : DatagramChannel.open(family);
+ this.group = group;
+
+ // attach this channel to the group as foreign channel
+ boolean registered = false;
+ try {
+ if (!(dc instanceof DatagramChannelImpl))
+ throw new UnsupportedOperationException();
+ attachKey = group
+ .attachForeignChannel(this, ((DatagramChannelImpl)dc).getFD());
+ registered = true;
+ } finally {
+ if (!registered)
+ dc.close();
+ }
+ }
+
+ // throws RuntimeException if blocking read has been cancelled
+ private void ensureBlockingReadNotKilled() {
+ assert Thread.holdsLock(readLock);
+ if (blockingReadKilledByCancel)
+ throw new RuntimeException("Reading not allowed due to cancellation");
+ }
+
+ // invoke prior to non-timed read/receive
+ private void beginNoTimeoutRead() {
+ synchronized (readLock) {
+ ensureBlockingReadNotKilled();
+ if (isBlocking)
+ blockingReaderCount++;
+ }
+ }
+
+ // invoke after non-timed read/receive has completed
+ private void endNoTimeoutRead() {
+ synchronized (readLock) {
+ if (isBlocking) {
+ if (--blockingReaderCount == 0 && transitionToNonBlocking) {
+ // notify any threads waiting to make channel non-blocking
+ readLock.notifyAll();
+ }
+ }
+ }
+ }
+
+ // invoke prior to timed read
+ // returns the timeout remaining
+ private long prepareForTimedRead(PendingFuture<?,?> result, long timeout)
+ throws IOException
+ {
+ synchronized (readLock) {
+ ensureBlockingReadNotKilled();
+ if (isBlocking) {
+ transitionToNonBlocking = true;
+ while (blockingReaderCount > 0 &&
+ timeout > 0L &&
+ !result.isCancelled())
+ {
+ long st = System.currentTimeMillis();
+ try {
+ readLock.wait(timeout);
+ } catch (InterruptedException e) { }
+ timeout -= System.currentTimeMillis() - st;
+ }
+ if (blockingReaderCount == 0) {
+ // re-check that blocked read wasn't cancelled
+ ensureBlockingReadNotKilled();
+ // no blocking reads so change channel to non-blocking
+ dc.configureBlocking(false);
+ isBlocking = false;
+ }
+ }
+ return timeout;
+ }
+ }
+
+ // returns a temporary Selector
+ private Selector getSelector() throws IOException {
+ Selector sel = Util.getTemporarySelector(dc);
+ synchronized (readLock) {
+ if (firstReader == null) {
+ firstReader = sel;
+ } else {
+ if (otherReaders == null)
+ otherReaders = new HashSet<Selector>();
+ otherReaders.add(sel);
+ }
+ }
+ return sel;
+ }
+
+ // releases a temporary Selector
+ private void releaseSelector(Selector sel) throws IOException {
+ synchronized (readLock) {
+ if (firstReader == sel) {
+ firstReader = null;
+ } else {
+ otherReaders.remove(sel);
+ }
+ }
+ Util.releaseTemporarySelector(sel);
+ }
+
+ // wakeup all Selectors currently in use
+ private void wakeupSelectors() {
+ synchronized (readLock) {
+ if (firstReader != null)
+ firstReader.wakeup();
+ if (otherReaders != null) {
+ for (Selector sel: otherReaders) {
+ sel.wakeup();
+ }
+ }
+ }
+ }
+
+ @Override
+ public AsynchronousChannelGroupImpl group() {
+ return group;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return dc.isOpen();
+ }
+
+ @Override
+ public void onCancel(PendingFuture<?,?> task) {
+ synchronized (readLock) {
+ if (blockingReaderCount > 0) {
+ blockingReadKilledByCancel = true;
+ readLock.notifyAll();
+ return;
+ }
+ }
+ wakeupSelectors();
+ }
+
+ @Override
+ public void close() throws IOException {
+ synchronized (dc) {
+ if (closed)
+ return;
+ closed = true;
+ }
+ // detach from group and close underlying channel
+ group.detachForeignChannel(attachKey);
+ dc.close();
+
+ // wakeup any threads blocked in timed read/receives
+ wakeupSelectors();
+ }
+
+ @Override
+ public AsynchronousDatagramChannel connect(SocketAddress remote)
+ throws IOException
+ {
+ dc.connect(remote);
+ return this;
+ }
+
+ @Override
+ public AsynchronousDatagramChannel disconnect() throws IOException {
+ dc.disconnect();
+ return this;
+ }
+
+ private static class WrappedMembershipKey extends MembershipKey {
+ private final MulticastChannel channel;
+ private final MembershipKey key;
+
+ WrappedMembershipKey(MulticastChannel channel, MembershipKey key) {
+ this.channel = channel;
+ this.key = key;
+ }
+
+ @Override
+ public boolean isValid() {
+ return key.isValid();
+ }
+
+ @Override
+ public void drop() {
+ key.drop();
+ }
+
+ @Override
+ public MulticastChannel channel() {
+ return channel;
+ }
+
+ @Override
+ public InetAddress group() {
+ return key.group();
+ }
+
+ @Override
+ public NetworkInterface networkInterface() {
+ return key.networkInterface();
+ }
+
+ @Override
+ public InetAddress sourceAddress() {
+ return key.sourceAddress();
+ }
+
+ @Override
+ public MembershipKey block(InetAddress toBlock) throws IOException {
+ key.block(toBlock);
+ return this;
+ }
+
+ @Override
+ public MembershipKey unblock(InetAddress toUnblock) {
+ key.unblock(toUnblock);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return key.toString();
+ }
+ }
+
+ @Override
+ public MembershipKey join(InetAddress group,
+ NetworkInterface interf)
+ throws IOException
+ {
+ MembershipKey key = ((MulticastChannel)dc).join(group, interf);
+ return new WrappedMembershipKey(this, key);
+ }
+
+ @Override
+ public MembershipKey join(InetAddress group,
+ NetworkInterface interf,
+ InetAddress source)
+ throws IOException
+ {
+ MembershipKey key = ((MulticastChannel)dc).join(group, interf, source);
+ return new WrappedMembershipKey(this, key);
+ }
+
+ @Override
+ public <A> Future<Integer> send(ByteBuffer src,
+ SocketAddress target,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ if (timeout < 0L)
+ throw new IllegalArgumentException("Negative timeout");
+ if (unit == null)
+ throw new NullPointerException();
+
+ CompletedFuture<Integer,A> result;
+ try {
+ int n = dc.send(src, target);
+ result = CompletedFuture.withResult(this, n, attachment);
+ } catch (IOException ioe) {
+ result = CompletedFuture.withFailure(this, ioe, attachment);
+ }
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ @Override
+ public <A> Future<Integer> write(ByteBuffer src,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ if (timeout < 0L)
+ throw new IllegalArgumentException("Negative timeout");
+ if (unit == null)
+ throw new NullPointerException();
+
+ CompletedFuture<Integer,A> result;
+ try {
+ int n = dc.write(src);
+ result = CompletedFuture.withResult(this, n, attachment);
+ } catch (IOException ioe) {
+ result = CompletedFuture.withFailure(this, ioe, attachment);
+ }
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ /**
+ * Receive into the given buffer with privileges enabled and restricted by
+ * the given AccessControlContext (can be null).
+ */
+ private SocketAddress doRestrictedReceive(final ByteBuffer dst,
+ AccessControlContext acc)
+ throws IOException
+ {
+ if (acc == null) {
+ return dc.receive(dst);
+ } else {
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<SocketAddress>() {
+ public SocketAddress run() throws IOException {
+ return dc.receive(dst);
+ }}, acc);
+ } catch (PrivilegedActionException pae) {
+ Exception cause = pae.getException();
+ if (cause instanceof SecurityException)
+ throw (SecurityException)cause;
+ throw (IOException)cause;
+ }
+ }
+ }
+
+ @Override
+ public <A> Future<SocketAddress> receive(final ByteBuffer dst,
+ final long timeout,
+ final TimeUnit unit,
+ A attachment,
+ final CompletionHandler<SocketAddress,? super A> handler)
+ {
+ if (dst.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+ if (timeout < 0L)
+ throw new IllegalArgumentException("Negative timeout");
+ if (unit == null)
+ throw new NullPointerException();
+
+ // complete immediately if channel closed
+ if (!isOpen()) {
+ CompletedFuture<SocketAddress,A> result = CompletedFuture.withFailure(this,
+ new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ final AccessControlContext acc = (System.getSecurityManager() == null) ?
+ null : AccessController.getContext();
+ final PendingFuture<SocketAddress,A> result =
+ new PendingFuture<SocketAddress,A>(this, handler, attachment);
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ SocketAddress remote = null;
+ long to;
+ if (timeout == 0L) {
+ beginNoTimeoutRead();
+ try {
+ remote = doRestrictedReceive(dst, acc);
+ } finally {
+ endNoTimeoutRead();
+ }
+ to = 0L;
+ } else {
+ to = prepareForTimedRead(result, unit.toMillis(timeout));
+ if (to <= 0L)
+ throw new InterruptedByTimeoutException();
+ remote = doRestrictedReceive(dst, acc);
+ }
+ if (remote == null) {
+ Selector sel = getSelector();
+ SelectionKey sk = null;
+ try {
+ sk = dc.register(sel, SelectionKey.OP_READ);
+ for (;;) {
+ if (!dc.isOpen())
+ throw new AsynchronousCloseException();
+ if (result.isCancelled())
+ break;
+ long st = System.currentTimeMillis();
+ int ns = sel.select(to);
+ if (ns > 0) {
+ remote = doRestrictedReceive(dst, acc);
+ if (remote != null)
+ break;
+ }
+ sel.selectedKeys().remove(sk);
+ if (timeout != 0L) {
+ to -= System.currentTimeMillis() - st;
+ if (to <= 0)
+ throw new InterruptedByTimeoutException();
+ }
+ }
+ } finally {
+ if (sk != null)
+ sk.cancel();
+ releaseSelector(sel);
+ }
+ }
+ result.setResult(remote);
+ } catch (Throwable x) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ }
+ Invoker.invokeUnchecked(handler, result);
+ }
+ };
+ try {
+ group.executeOnPooledThread(task);
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ return result;
+ }
+
+ @Override
+ public <A> Future<Integer> read(final ByteBuffer dst,
+ final long timeout,
+ final TimeUnit unit,
+ A attachment,
+ final CompletionHandler<Integer,? super A> handler)
+ {
+ if (dst.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+ if (timeout < 0L)
+ throw new IllegalArgumentException("Negative timeout");
+ if (unit == null)
+ throw new NullPointerException();
+ // another thread may disconnect before read is initiated
+ if (!dc.isConnected())
+ throw new NotYetConnectedException();
+
+ // complete immediately if channel closed
+ if (!isOpen()) {
+ CompletedFuture<Integer,A> result = CompletedFuture.withFailure(this,
+ new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ final PendingFuture<Integer,A> result =
+ new PendingFuture<Integer,A>(this, handler, attachment);
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ int n = 0;
+ long to;
+ if (timeout == 0L) {
+ beginNoTimeoutRead();
+ try {
+ n = dc.read(dst);
+ } finally {
+ endNoTimeoutRead();
+ }
+ to = 0L;
+ } else {
+ to = prepareForTimedRead(result, unit.toMillis(timeout));
+ if (to <= 0L)
+ throw new InterruptedByTimeoutException();
+ n = dc.read(dst);
+ }
+ if (n == 0) {
+ Selector sel = getSelector();
+ SelectionKey sk = null;
+ try {
+ sk = dc.register(sel, SelectionKey.OP_READ);
+ for (;;) {
+ if (!dc.isOpen())
+ throw new AsynchronousCloseException();
+ if (result.isCancelled())
+ break;
+ long st = System.currentTimeMillis();
+ int ns = sel.select(to);
+ if (ns > 0) {
+ if ((n = dc.read(dst)) != 0)
+ break;
+ }
+ sel.selectedKeys().remove(sk);
+ if (timeout != 0L) {
+ to -= System.currentTimeMillis() - st;
+ if (to <= 0)
+ throw new InterruptedByTimeoutException();
+ }
+ }
+ } finally {
+ if (sk != null)
+ sk.cancel();
+ releaseSelector(sel);
+ }
+ }
+ result.setResult(n);
+ } catch (Throwable x) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ }
+ Invoker.invokeUnchecked(handler, result);
+ }
+ };
+ try {
+ group.executeOnPooledThread(task);
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ return result;
+ }
+
+ @Override
+ public AsynchronousDatagramChannel bind(SocketAddress local)
+ throws IOException
+ {
+ dc.bind(local);
+ return this;
+ }
+
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ return dc.getLocalAddress();
+ }
+
+ @Override
+ public <T> AsynchronousDatagramChannel setOption(SocketOption<T> name, T value)
+ throws IOException
+ {
+ dc.setOption(name, value);
+ return this;
+ }
+
+ @Override
+ public <T> T getOption(SocketOption<T> name) throws IOException {
+ return dc.getOption(name);
+ }
+
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return dc.supportedOptions();
+ }
+
+ @Override
+ public SocketAddress getRemoteAddress() throws IOException {
+ return dc.getRemoteAddress();
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java b/src/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java
new file mode 100644
index 000000000..52fe5a280
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * "Portable" implementation of AsynchronousFileChannel for use on operating
+ * systems that don't support asynchronous file I/O.
+ */
+
+public class SimpleAsynchronousFileChannelImpl
+ extends AsynchronousFileChannelImpl
+{
+ // lazy initialization of default thread pool for file I/O
+ private static class DefaultExecutorHolder {
+ static final ExecutorService defaultExecutor =
+ ThreadPool.createDefault().executor();
+ }
+
+ // Used to make native read and write calls
+ private static final FileDispatcher nd = new FileDispatcherImpl();
+
+ // indicates if the associated thread pool is the default thread pool
+ private final boolean isDefaultExecutor;
+
+ // Thread-safe set of IDs of native threads, for signalling
+ private final NativeThreadSet threads = new NativeThreadSet(2);
+
+
+ SimpleAsynchronousFileChannelImpl(FileDescriptor fdObj,
+ boolean reading,
+ boolean writing,
+ ExecutorService executor,
+ boolean isDefaultexecutor)
+ {
+ super(fdObj, reading, writing, executor);
+ this.isDefaultExecutor = isDefaultexecutor;
+ }
+
+ public static AsynchronousFileChannel open(FileDescriptor fdo,
+ boolean reading,
+ boolean writing,
+ ThreadPool pool)
+ {
+ // Executor is either default or based on pool parameters
+ ExecutorService executor;
+ boolean isDefaultexecutor;
+ if (pool == null) {
+ executor = DefaultExecutorHolder.defaultExecutor;
+ isDefaultexecutor = true;
+ } else {
+ executor = pool.executor();
+ isDefaultexecutor = false;
+ }
+ return new SimpleAsynchronousFileChannelImpl(fdo,
+ reading, writing, executor, isDefaultexecutor);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // mark channel as closed
+ synchronized (fdObj) {
+ if (closed)
+ return; // already closed
+ closed = true;
+ // from this point on, if another thread invokes the begin() method
+ // then it will throw ClosedChannelException
+ }
+
+ // signal any threads blocked on this channel
+ nd.preClose(fdObj);
+ threads.signalAndWait();
+
+ // wait until all async I/O operations have completely gracefully
+ closeLock.writeLock().lock();
+ try {
+ // do nothing
+ } finally {
+ closeLock.writeLock().unlock();
+ }
+
+ // Invalidate and release any locks that we still hold
+ invalidateAllLocks();
+
+ // close file
+ nd.close(fdObj);
+
+ // shutdown executor if specific to this channel
+ if (!isDefaultExecutor) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ executor.shutdown();
+ return null;
+ }
+ });
+ }
+ }
+
+ @Override
+ public long size() throws IOException {
+ int ti = threads.add();
+ try {
+ long n = 0L;
+ try {
+ begin();
+ do {
+ n = nd.size(fdObj);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+ return n;
+ } finally {
+ end(n >= 0L);
+ }
+ } finally {
+ threads.remove(ti);
+ }
+ }
+
+ @Override
+ public AsynchronousFileChannel truncate(long size) throws IOException {
+ if (size < 0L)
+ throw new IllegalArgumentException("Negative size");
+ if (!writing)
+ throw new NonWritableChannelException();
+ int ti = threads.add();
+ try {
+ long n = 0L;
+ try {
+ begin();
+ do {
+ n = nd.size(fdObj);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+ // truncate file if 'size' less than current size
+ if (size < n && isOpen()) {
+ do {
+ n = nd.truncate(fdObj, size);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+ }
+ return this;
+ } finally {
+ end(n > 0);
+ }
+ } finally {
+ threads.remove(ti);
+ }
+ }
+
+ @Override
+ public void force(boolean metaData) throws IOException {
+ int ti = threads.add();
+ try {
+ int n = 0;
+ try {
+ begin();
+ do {
+ n = nd.force(fdObj, metaData);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+ } finally {
+ end(n >= 0);
+ }
+ } finally {
+ threads.remove(ti);
+ }
+ }
+
+ @Override
+ public <A> Future<FileLock> lock(final long position,
+ final long size,
+ final boolean shared,
+ A attachment,
+ final CompletionHandler<FileLock,? super A> handler)
+ {
+ if (shared && !reading)
+ throw new NonReadableChannelException();
+ if (!shared && !writing)
+ throw new NonWritableChannelException();
+
+ // add to lock table
+ final FileLockImpl fli = addToFileLockTable(position, size, shared);
+ if (fli == null) {
+ CompletedFuture<FileLock,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invokeIndirectly(handler, result, executor);
+ return result;
+ }
+
+ final PendingFuture<FileLock,A> result =
+ new PendingFuture<FileLock,A>(this, handler, attachment);
+ Runnable task = new Runnable() {
+ public void run() {
+ int ti = threads.add();
+ try {
+ int n;
+ try {
+ begin();
+ do {
+ n = nd.lock(fdObj, true, position, size, shared);
+ } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
+ if (n == FileDispatcher.LOCKED) {
+ result.setResult(fli);
+ } else {
+ if (n != FileDispatcher.INTERRUPTED)
+ throw new AssertionError();
+ throw new AsynchronousCloseException();
+ }
+ } catch (IOException x) {
+ removeFromFileLockTable(fli);
+ if (!isOpen())
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ } finally {
+ end();
+ }
+ } finally {
+ threads.remove(ti);
+ }
+ Invoker.invokeUnchecked(handler, result);
+ }
+ };
+ try {
+ executor.execute(task);
+ } catch (RejectedExecutionException ree) {
+ // rollback
+ removeFromFileLockTable(fli);
+ throw new ShutdownChannelGroupException();
+ }
+ return result;
+ }
+
+ @Override
+ public FileLock tryLock(long position, long size, boolean shared)
+ throws IOException
+ {
+ if (shared && !reading)
+ throw new NonReadableChannelException();
+ if (!shared && !writing)
+ throw new NonWritableChannelException();
+
+ // add to lock table
+ FileLockImpl fli = addToFileLockTable(position, size, shared);
+ if (fli == null)
+ throw new ClosedChannelException();
+
+ int ti = threads.add();
+ boolean gotLock = false;
+ try {
+ begin();
+ int n;
+ do {
+ n = nd.lock(fdObj, false, position, size, shared);
+ } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
+ if (n != FileDispatcher.LOCKED) {
+ if (n == FileDispatcher.NO_LOCK)
+ return null; // locked by someone else
+ if (n == FileDispatcher.INTERRUPTED)
+ throw new AsynchronousCloseException();
+ // should not get here
+ throw new AssertionError();
+ }
+ gotLock = true;
+ return fli;
+ } finally {
+ if (!gotLock)
+ removeFromFileLockTable(fli);
+ end();
+ threads.remove(ti);
+ }
+ }
+
+ @Override
+ void release(FileLockImpl fli) throws IOException {
+ try {
+ begin();
+ nd.release(fdObj, fli.position(), fli.size());
+ removeFromFileLockTable(fli);
+ } finally {
+ end();
+ }
+ }
+
+ @Override
+ public <A> Future<Integer> read(final ByteBuffer dst,
+ final long position,
+ A attachment,
+ final CompletionHandler<Integer,? super A> handler)
+ {
+ if (position < 0)
+ throw new IllegalArgumentException("Negative position");
+ if (!reading)
+ throw new NonReadableChannelException();
+ if (dst.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+
+ // complete immediately if channel closed or no space remaining
+ if (!isOpen() || (dst.remaining() == 0)) {
+ CompletedFuture<Integer,A> result;
+ if (isOpen()) {
+ result = CompletedFuture.withResult(this, 0, attachment);
+ } else {
+ result = CompletedFuture.withFailure(this,
+ new ClosedChannelException(), attachment);
+ }
+ Invoker.invokeIndirectly(handler, result, executor);
+ return result;
+ }
+
+ final PendingFuture<Integer,A> result =
+ new PendingFuture<Integer,A>(this, handler, attachment);
+ Runnable task = new Runnable() {
+ public void run() {
+ int ti = threads.add();
+ try {
+ begin();
+ int n;
+ do {
+ n = IOUtil.read(fdObj, dst, position, nd, null);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+ if (n < 0 && !isOpen())
+ throw new AsynchronousCloseException();
+ result.setResult(n);
+ } catch (IOException x) {
+ if (!isOpen())
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ } finally {
+ end();
+ threads.remove(ti);
+ }
+ Invoker.invokeUnchecked(handler, result);
+ }
+ };
+ try {
+ executor.execute(task);
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ return result;
+ }
+
+ @Override
+ public <A> Future<Integer> write(final ByteBuffer src,
+ final long position,
+ A attachment,
+ final CompletionHandler<Integer,? super A> handler)
+ {
+ if (position < 0)
+ throw new IllegalArgumentException("Negative position");
+ if (!writing)
+ throw new NonWritableChannelException();
+
+ // complete immediately if channel is closed or no bytes remaining
+ if (!isOpen() || (src.remaining() == 0)) {
+ CompletedFuture<Integer,A> result;
+ if (isOpen()) {
+ result = CompletedFuture.withResult(this, 0, attachment);
+ } else {
+ result = CompletedFuture.withFailure(this,
+ new ClosedChannelException(), attachment);
+ }
+ Invoker.invokeIndirectly(handler, result, executor);
+ return result;
+ }
+
+ final PendingFuture<Integer,A> result =
+ new PendingFuture<Integer,A>(this, handler, attachment);
+ Runnable task = new Runnable() {
+ public void run() {
+ int ti = threads.add();
+ try {
+ begin();
+ int n;
+ do {
+ n = IOUtil.write(fdObj, src, position, nd, null);
+ } while ((n == IOStatus.INTERRUPTED) && isOpen());
+ if (n < 0 && !isOpen())
+ throw new AsynchronousCloseException();
+ result.setResult(n);
+ } catch (IOException x) {
+ if (!isOpen())
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ } finally {
+ end();
+ threads.remove(ti);
+ }
+ Invoker.invokeUnchecked(handler, result);
+ }
+ };
+ try {
+ executor.execute(task);
+ } catch (RejectedExecutionException ree) {
+ throw new ShutdownChannelGroupException();
+ }
+ return result;
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/share/classes/sun/nio/ch/SocketChannelImpl.java
index 11567ba2a..20944774d 100644
--- a/src/share/classes/sun/nio/ch/SocketChannelImpl.java
+++ b/src/share/classes/sun/nio/ch/SocketChannelImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -128,28 +128,28 @@ class SocketChannelImpl
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
- return null;
+ throw new ClosedChannelException();
return localAddress;
}
}
@Override
- public SocketAddress getConnectedAddress() throws IOException {
+ public SocketAddress getRemoteAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
- return null;
+ throw new ClosedChannelException();
return remoteAddress;
}
}
@Override
- public SocketChannel setOption(SocketOption name, Object value)
+ public <T> SocketChannel setOption(SocketOption<T> name, T value)
throws IOException
{
if (name == null)
throw new NullPointerException();
- if (!options().contains(name))
- throw new IllegalArgumentException("Invalid option name");
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
@@ -175,8 +175,8 @@ class SocketChannelImpl
{
if (name == null)
throw new NullPointerException();
- if (!options().contains(name))
- throw new IllegalArgumentException("Invalid option name");
+ if (!supportedOptions().contains(name))
+ throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
@@ -193,7 +193,7 @@ class SocketChannelImpl
}
}
- private static class LazyInitialization {
+ private static class DefaultOptionsHolder {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
@@ -212,8 +212,8 @@ class SocketChannelImpl
}
@Override
- public final Set<SocketOption<?>> options() {
- return LazyInitialization.defaultOptions;
+ public final Set<SocketOption<?>> supportedOptions() {
+ return DefaultOptionsHolder.defaultOptions;
}
private boolean ensureReadOpen() throws ClosedChannelException {
diff --git a/src/share/classes/sun/nio/ch/ThreadPool.java b/src/share/classes/sun/nio/ch/ThreadPool.java
new file mode 100644
index 000000000..37e6a80a0
--- /dev/null
+++ b/src/share/classes/sun/nio/ch/ThreadPool.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.util.concurrent.*;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+import sun.security.action.GetIntegerAction;
+
+/**
+ * Encapsulates a thread pool associated with a channel group.
+ */
+
+public class ThreadPool {
+ private static final String DEFAULT_THREAD_POOL_THREAD_FACTORY =
+ "java.nio.channels.DefaultThreadPool.threadFactory";
+ private static final String DEFAULT_THREAD_POOL_INITIAL_SIZE =
+ "java.nio.channels.DefaultThreadPool.initialSize";
+ private static final ThreadFactory defaultThreadFactory = new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ return t;
+ }
+ };
+
+ private final ExecutorService executor;
+
+ // indicates if thread pool is fixed size
+ private final boolean isFixed;
+
+ // indicates the pool size (for a fixed thread pool configuratin this is
+ // the maximum pool size; for other thread pools it is the initial size)
+ private final int poolSize;
+
+ private ThreadPool(ExecutorService executor,
+ boolean isFixed,
+ int poolSize)
+ {
+ this.executor = executor;
+ this.isFixed = isFixed;
+ this.poolSize = poolSize;
+ }
+
+ ExecutorService executor() {
+ return executor;
+ }
+
+ boolean isFixedThreadPool() {
+ return isFixed;
+ }
+
+ int poolSize() {
+ return poolSize;
+ }
+
+ static ThreadFactory defaultThreadFactory() {
+ return defaultThreadFactory;
+ }
+
+ private static class DefaultThreadPoolHolder {
+ final static ThreadPool defaultThreadPool = createDefault();
+ }
+
+ // return the default (system-wide) thread pool
+ static ThreadPool getDefault() {
+ return DefaultThreadPoolHolder.defaultThreadPool;
+ }
+
+ // create thread using default settings (configured by system properties)
+ static ThreadPool createDefault() {
+ // default the number of fixed threads to the hardware core count
+ int initialSize = getDefaultThreadPoolInitialSize();
+ if (initialSize < 0)
+ initialSize = Runtime.getRuntime().availableProcessors();
+ // default to thread factory that creates daemon threads
+ ThreadFactory threadFactory = getDefaultThreadPoolThreadFactory();
+ if (threadFactory == null)
+ threadFactory = defaultThreadFactory;
+ // create thread pool
+ ExecutorService executor =
+ new ThreadPoolExecutor(0, Integer.MAX_VALUE,
+ Long.MAX_VALUE, TimeUnit.MILLISECONDS,
+ new SynchronousQueue<Runnable>(),
+ threadFactory);
+ return new ThreadPool(executor, false, initialSize);
+ }
+
+ // create using given parameters
+ static ThreadPool create(int nThreads, ThreadFactory factory) {
+ if (nThreads <= 0)
+ throw new IllegalArgumentException("'nThreads' must be > 0");
+ ExecutorService executor = Executors.newFixedThreadPool(nThreads, factory);
+ return new ThreadPool(executor, true, nThreads);
+ }
+
+ // wrap a user-supplied executor
+ public static ThreadPool wrap(ExecutorService executor, int initialSize) {
+ if (executor == null)
+ throw new NullPointerException("'executor' is null");
+ // attempt to check if cached thread pool
+ if (executor instanceof ThreadPoolExecutor) {
+ int max = ((ThreadPoolExecutor)executor).getMaximumPoolSize();
+ if (max == Integer.MAX_VALUE) {
+ if (initialSize < 0) {
+ initialSize = Runtime.getRuntime().availableProcessors();
+ } else {
+ // not a cached thread pool so ignore initial size
+ initialSize = 0;
+ }
+ }
+ } else {
+ // some other type of thread pool
+ if (initialSize < 0)
+ initialSize = 0;
+ }
+ return new ThreadPool(executor, false, initialSize);
+ }
+
+ private static int getDefaultThreadPoolInitialSize() {
+ String propValue = AccessController.doPrivileged(new
+ GetPropertyAction(DEFAULT_THREAD_POOL_INITIAL_SIZE));
+ if (propValue != null) {
+ try {
+ return Integer.parseInt(propValue);
+ } catch (NumberFormatException x) {
+ throw new Error("Value of property '" + DEFAULT_THREAD_POOL_INITIAL_SIZE +
+ "' is invalid: " + x);
+ }
+ }
+ return -1;
+ }
+
+ private static ThreadFactory getDefaultThreadPoolThreadFactory() {
+ String propValue = AccessController.doPrivileged(new
+ GetPropertyAction(DEFAULT_THREAD_POOL_THREAD_FACTORY));
+ if (propValue != null) {
+ try {
+ Class<?> c = Class
+ .forName(propValue, true, ClassLoader.getSystemClassLoader());
+ return ((ThreadFactory)c.newInstance());
+ } catch (ClassNotFoundException x) {
+ throw new Error(x);
+ } catch (InstantiationException x) {
+ throw new Error(x);
+ } catch (IllegalAccessException x) {
+ throw new Error(x);
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/share/classes/sun/nio/ch/Util.java b/src/share/classes/sun/nio/ch/Util.java
index 534548ff7..efdce4a4b 100644
--- a/src/share/classes/sun/nio/ch/Util.java
+++ b/src/share/classes/sun/nio/ch/Util.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -31,7 +31,6 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.*;
-import java.nio.channels.spi.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
@@ -100,6 +99,9 @@ class Util {
return;
}
}
+
+ // release memory
+ ((DirectBuffer)buf).cleaner().clean();
}
private static class SelectorWrapper {
diff --git a/src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java b/src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java
new file mode 100644
index 000000000..053e3dcfb
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Base implementation of AclFileAttributeView
+ */
+
+abstract class AbstractAclFileAttributeView
+ implements AclFileAttributeView
+{
+ private static final String OWNER_NAME = "owner";
+ private static final String ACL_NAME = "acl";
+
+ @Override
+ public final String name() {
+ return "acl";
+ }
+
+ @Override
+ public final Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(OWNER_NAME))
+ return getOwner();
+ if (attribute.equals(ACL_NAME))
+ return getAcl();
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(OWNER_NAME)) {
+ setOwner((UserPrincipal)value);
+ return;
+ }
+ if (attribute.equals(ACL_NAME)) {
+ setAcl((List<AclEntry>)value);
+ return;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ boolean acl = false;
+ boolean owner = false;
+
+ if (first.equals(ACL_NAME)) acl = true;
+ else if (first.equals(OWNER_NAME)) owner = true;
+ else if (first.equals("*")) {
+ owner = true;
+ acl = true;
+ }
+
+ if (!acl || !owner) {
+ for (String attribute: rest) {
+ if (attribute.equals("*")) {
+ owner = true;
+ acl = true;
+ break;
+ }
+ if (attribute.equals(ACL_NAME)) {
+ acl = true;
+ continue;
+ }
+ if (attribute.equals(OWNER_NAME)) {
+ owner = true;
+ continue;
+ }
+ }
+ }
+ Map<String,Object> result = new HashMap<String,Object>(2);
+ if (acl)
+ result.put(ACL_NAME, getAcl());
+ if (owner)
+ result.put(OWNER_NAME, getOwner());
+ return Collections.unmodifiableMap(result);
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java b/src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java
new file mode 100644
index 000000000..18cf00d50
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base implementation of BasicFileAttributeView
+ */
+
+abstract class AbstractBasicFileAttributeView
+ implements BasicFileAttributeView
+{
+ private static final String SIZE_NAME = "size";
+ private static final String CREATION_TIME_NAME = "creationTime";
+ private static final String LAST_ACCESS_TIME_NAME = "lastAccessTime";
+ private static final String LAST_MODIFIED_TIME_NAME = "lastModifiedTime";
+ private static final String RESOLUTION_NAME = "resolution";
+ private static final String FILE_KEY_NAME = "fileKey";
+ private static final String LINK_COUNT_NAME = "linkCount";
+ private static final String IS_DIRECTORY_NAME = "isDirectory";
+ private static final String IS_REGULAR_FILE_NAME = "isRegularFile";
+ private static final String IS_SYMBOLIC_LINK_NAME = "isSymbolicLink";
+ private static final String IS_OTHER_NAME = "isOther";
+
+ protected AbstractBasicFileAttributeView() { }
+
+ @Override
+ public String name() {
+ return "basic";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ BasicFileAttributes attrs = readAttributes();
+ if (attribute.equals(SIZE_NAME))
+ return attrs.size();
+ if (attribute.equals(CREATION_TIME_NAME))
+ return attrs.creationTime();
+ if (attribute.equals(LAST_ACCESS_TIME_NAME))
+ return attrs.lastAccessTime();
+ if (attribute.equals(LAST_MODIFIED_TIME_NAME))
+ return attrs.lastModifiedTime();
+ if (attribute.equals(RESOLUTION_NAME))
+ return attrs.resolution();
+ if (attribute.equals(FILE_KEY_NAME))
+ return attrs.fileKey();
+ if (attribute.equals(LINK_COUNT_NAME))
+ return attrs.linkCount();
+ if (attribute.equals(IS_DIRECTORY_NAME))
+ return attrs.isDirectory();
+ if (attribute.equals(IS_REGULAR_FILE_NAME))
+ return attrs.isRegularFile();
+ if (attribute.equals(IS_SYMBOLIC_LINK_NAME))
+ return attrs.isSymbolicLink();
+ if (attribute.equals(IS_OTHER_NAME))
+ return attrs.isOther();
+ return null;
+ }
+
+ private Long toTimeValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Long time = (Long)value;
+ if (time < 0L && time != -1L)
+ throw new IllegalArgumentException("time value cannot be negative");
+ return time;
+ }
+
+ @Override
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(LAST_MODIFIED_TIME_NAME)) {
+ setTimes(toTimeValue(value), null, null, TimeUnit.MILLISECONDS);
+ return;
+ }
+ if (attribute.equals(LAST_ACCESS_TIME_NAME)) {
+ setTimes(null, toTimeValue(value), null, TimeUnit.MILLISECONDS);
+ return;
+ }
+ if (attribute.equals(CREATION_TIME_NAME)) {
+ setTimes(null, null, toTimeValue(value), TimeUnit.MILLISECONDS);
+ return;
+ }
+ throw new UnsupportedOperationException("'" + attribute +
+ "' is unknown or read-only attribute");
+ }
+
+ /**
+ *
+ */
+ static class AttributesBuilder {
+ private Set<String> set = new HashSet<String>();
+ private Map<String,Object> map = new HashMap<String,Object>();
+ private boolean copyAll;
+
+ private AttributesBuilder(String first, String[] rest) {
+ if (first.equals("*")) {
+ copyAll = true;
+ } else {
+ set.add(first);
+ // copy names into the given Set bailing out if "*" is found
+ for (String attribute: rest) {
+ if (attribute.equals("*")) {
+ copyAll = true;
+ break;
+ }
+ set.add(attribute);
+ }
+ }
+ }
+
+ /**
+ * Creates builder to build up a map of the matching attributes
+ */
+ static AttributesBuilder create(String first, String[] rest) {
+ return new AttributesBuilder(first, rest);
+ }
+
+ /**
+ * Returns true if the attribute should be returned in the map
+ */
+ boolean match(String attribute) {
+ if (copyAll)
+ return true;
+ return set.contains(attribute);
+ }
+
+ void add(String attribute, Object value) {
+ map.put(attribute, value);
+ }
+
+ /**
+ * Returns the map. Discard all references to the AttributesBuilder
+ * after invoking this method.
+ */
+ Map<String,Object> unmodifiableMap() {
+ return Collections.unmodifiableMap(map);
+ }
+ }
+
+ /**
+ * Invoked by readAttributes or sub-classes to add all matching basic
+ * attributes to the builder
+ */
+ final void addBasicAttributesToBuilder(BasicFileAttributes attrs,
+ AttributesBuilder builder)
+ {
+ if (builder.match(SIZE_NAME))
+ builder.add(SIZE_NAME, attrs.size());
+ if (builder.match(CREATION_TIME_NAME))
+ builder.add(CREATION_TIME_NAME, attrs.creationTime());
+ if (builder.match(LAST_ACCESS_TIME_NAME))
+ builder.add(LAST_ACCESS_TIME_NAME, attrs.lastAccessTime());
+ if (builder.match(LAST_MODIFIED_TIME_NAME))
+ builder.add(LAST_MODIFIED_TIME_NAME, attrs.lastModifiedTime());
+ if (builder.match(RESOLUTION_NAME))
+ builder.add(RESOLUTION_NAME, attrs.resolution());
+ if (builder.match(FILE_KEY_NAME))
+ builder.add(FILE_KEY_NAME, attrs.fileKey());
+ if (builder.match(LINK_COUNT_NAME))
+ builder.add(LINK_COUNT_NAME, attrs.linkCount());
+ if (builder.match(IS_DIRECTORY_NAME))
+ builder.add(IS_DIRECTORY_NAME, attrs.isDirectory());
+ if (builder.match(IS_REGULAR_FILE_NAME))
+ builder.add(IS_REGULAR_FILE_NAME, attrs.isRegularFile());
+ if (builder.match(IS_SYMBOLIC_LINK_NAME))
+ builder.add(IS_SYMBOLIC_LINK_NAME, attrs.isSymbolicLink());
+ if (builder.match(IS_OTHER_NAME))
+ builder.add(IS_OTHER_NAME, attrs.isOther());
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ AttributesBuilder builder = AttributesBuilder.create(first, rest);
+ addBasicAttributesToBuilder(readAttributes(), builder);
+ return builder.unmodifiableMap();
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractFileStoreSpaceAttributeView.java b/src/share/classes/sun/nio/fs/AbstractFileStoreSpaceAttributeView.java
new file mode 100644
index 000000000..73c8c2a0d
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractFileStoreSpaceAttributeView.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Base implementation of FileStoreSpaceAttributeView
+ */
+
+abstract class AbstractFileStoreSpaceAttributeView
+ implements FileStoreSpaceAttributeView
+{
+ private static final String TOTAL_SPACE_NAME = "totalSpace";
+ private static final String USABLE_SPACE_NAME = "usableSpace";
+ private static final String UNALLOCATED_SPACE_NAME = "unallocatedSpace";
+
+ @Override
+ public final String name() {
+ return "space";
+ }
+
+ @Override
+ public final Object getAttribute(String attribute) throws IOException {
+ FileStoreSpaceAttributes attrs = readAttributes();
+ if (attribute.equals(TOTAL_SPACE_NAME))
+ return attrs.totalSpace();
+ if (attribute.equals(USABLE_SPACE_NAME))
+ return attrs.usableSpace();
+ if (attribute.equals(UNALLOCATED_SPACE_NAME))
+ return attrs.unallocatedSpace();
+ return null;
+ }
+
+ @Override
+ public final void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute == null || value == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ boolean total = false;
+ boolean usable = false;
+ boolean unallocated = false;
+
+ if (first.equals(TOTAL_SPACE_NAME)) total = true;
+ else if (first.equals(USABLE_SPACE_NAME)) usable = true;
+ else if (first.equals(UNALLOCATED_SPACE_NAME)) unallocated = true;
+ else if (first.equals("*")) {
+ total = true;
+ usable = true;
+ unallocated = true;
+ }
+
+ if (!total || !usable || !unallocated) {
+ for (String attribute: rest) {
+ if (attribute.equals("*")) {
+ total = true;
+ usable = true;
+ unallocated = true;
+ break;
+ }
+ if (attribute.equals(TOTAL_SPACE_NAME)) {
+ total = true;
+ continue;
+ }
+ if (attribute.equals(USABLE_SPACE_NAME)) {
+ usable = true;
+ continue;
+ }
+ if (attribute.equals(UNALLOCATED_SPACE_NAME)) {
+ unallocated = true;
+ continue;
+ }
+ }
+ }
+
+ FileStoreSpaceAttributes attrs = readAttributes();
+ Map<String,Object> result = new HashMap<String,Object>(2);
+ if (total)
+ result.put(TOTAL_SPACE_NAME, attrs.totalSpace());
+ if (usable)
+ result.put(USABLE_SPACE_NAME, attrs.usableSpace());
+ if (unallocated)
+ result.put(UNALLOCATED_SPACE_NAME, attrs.unallocatedSpace());
+ return Collections.unmodifiableMap(result);
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractFileTypeDetector.java b/src/share/classes/sun/nio/fs/AbstractFileTypeDetector.java
new file mode 100644
index 000000000..d8a391b80
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractFileTypeDetector.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.FileRef;
+import java.nio.file.spi.FileTypeDetector;
+import java.io.IOException;
+import sun.nio.fs.MimeType;
+
+/**
+ * Base implementation of FileTypeDetector
+ */
+
+public abstract class AbstractFileTypeDetector
+ extends FileTypeDetector
+{
+ protected AbstractFileTypeDetector() {
+ super();
+ }
+
+ /**
+ * Invokes the implProbeContentType method to guess the file's content type,
+ * and this validates that the content type's syntax is valid.
+ */
+ @Override
+ public final String probeContentType(FileRef file) throws IOException {
+ if (file == null)
+ throw new NullPointerException("'file' is null");
+ String result = implProbeContentType(file);
+ if (result != null) {
+ // check the content type
+ try {
+ MimeType.parse(result);
+ } catch (IllegalArgumentException ignore) {
+ result = null;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Probes the given file to guess its content type.
+ */
+ protected abstract String implProbeContentType(FileRef file)
+ throws IOException;
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractPoller.java b/src/share/classes/sun/nio/fs/AbstractPoller.java
new file mode 100644
index 000000000..3ab8af8e1
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractPoller.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Base implementation of background poller thread used in watch service
+ * implementations. A poller thread waits on events from the file system and
+ * also services "requests" from clients to register for new events or cancel
+ * existing registrations.
+ */
+
+abstract class AbstractPoller implements Runnable {
+
+ // list of requests pending to the poller thread
+ private final LinkedList<Request> requestList;
+
+ // set to true when shutdown
+ private boolean shutdown;
+
+ protected AbstractPoller() {
+ this.requestList = new LinkedList<Request>();
+ this.shutdown = false;
+ }
+
+ /**
+ * Starts the poller thread
+ */
+ public void start() {
+ final Runnable thisRunnable = this;
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ Thread thr = new Thread(thisRunnable);
+ thr.setDaemon(true);
+ thr.start();
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Wakeup poller thread so that it can service pending requests
+ */
+ abstract void wakeup() throws IOException;
+
+ /**
+ * Executed by poller thread to register directory for changes
+ */
+ abstract Object implRegister(Path path,
+ Set<? extends WatchEvent.Kind<?>> events,
+ WatchEvent.Modifier... modifiers);
+
+ /**
+ * Executed by poller thread to cancel key
+ */
+ abstract void implCancelKey(WatchKey key);
+
+ /**
+ * Executed by poller thread to shutdown and cancel all keys
+ */
+ abstract void implCloseAll();
+
+ /**
+ * Requests, and waits on, poller thread to register given file.
+ */
+ final WatchKey register(FileRef dir,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ // validate arguments before request to poller
+ if (dir == null)
+ throw new NullPointerException();
+ if (events.length == 0)
+ throw new IllegalArgumentException("No events to register");
+ Set<WatchEvent.Kind<?>> eventSet = new HashSet<WatchEvent.Kind<?>>(events.length);
+ for (WatchEvent.Kind<?> event: events) {
+ // standard events
+ if (event == StandardWatchEventKind.ENTRY_CREATE ||
+ event == StandardWatchEventKind.ENTRY_MODIFY ||
+ event == StandardWatchEventKind.ENTRY_DELETE)
+ {
+ eventSet.add(event);
+ continue;
+ }
+
+ // OVERFLOW is ignored
+ if (event == StandardWatchEventKind.OVERFLOW) {
+ if (events.length == 1)
+ throw new IllegalArgumentException("No events to register");
+ continue;
+ }
+
+ // null/unsupported
+ if (event == null)
+ throw new NullPointerException("An element in event set is 'null'");
+ throw new UnsupportedOperationException(event.name());
+ }
+ return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers);
+ }
+
+ /**
+ * Cancels, and waits on, poller thread to cancel given key.
+ */
+ final void cancel(WatchKey key) {
+ try {
+ invoke(RequestType.CANCEL, key);
+ } catch (IOException x) {
+ // should not happen
+ throw new AssertionError(x.getMessage());
+ }
+ }
+
+ /**
+ * Shutdown poller thread
+ */
+ final void close() throws IOException {
+ invoke(RequestType.CLOSE);
+ }
+
+ /**
+ * Types of request that the poller thread must handle
+ */
+ private static enum RequestType {
+ REGISTER,
+ CANCEL,
+ CLOSE;
+ }
+
+ /**
+ * Encapsulates a request (command) to the poller thread.
+ */
+ private static class Request {
+ private final RequestType type;
+ private final Object[] params;
+
+ private boolean completed = false;
+ private Object result = null;
+
+ Request(RequestType type, Object... params) {
+ this.type = type;
+ this.params = params;
+ }
+
+ RequestType type() {
+ return type;
+ }
+
+ Object[] parameters() {
+ return params;
+ }
+
+ void release(Object result) {
+ synchronized (this) {
+ this.completed = true;
+ this.result = result;
+ notifyAll();
+ }
+ }
+
+ /**
+ * Await completion of the request. The return value is the result of
+ * the request.
+ */
+ Object awaitResult() {
+ synchronized (this) {
+ while (!completed) {
+ try {
+ wait();
+ } catch (InterruptedException x) {
+ // ignore
+ }
+ }
+ return result;
+ }
+ }
+ }
+
+ /**
+ * Enqueues request to poller thread and waits for result
+ */
+ private Object invoke(RequestType type, Object... params) throws IOException {
+ // submit request
+ Request req = new Request(type, params);
+ synchronized (requestList) {
+ if (shutdown) {
+ throw new ClosedWatchServiceException();
+ }
+ requestList.add(req);
+ }
+
+ // wakeup thread
+ wakeup();
+
+ // wait for result
+ Object result = req.awaitResult();
+
+ if (result instanceof RuntimeException)
+ throw (RuntimeException)result;
+ if (result instanceof IOException )
+ throw (IOException)result;
+ return result;
+ }
+
+ /**
+ * Invoked by poller thread to process all pending requests
+ *
+ * @return true if poller thread should shutdown
+ */
+ @SuppressWarnings("unchecked")
+ boolean processRequests() {
+ synchronized (requestList) {
+ Request req;
+ while ((req = requestList.poll()) != null) {
+ // if in process of shutdown then reject request
+ if (shutdown) {
+ req.release(new ClosedWatchServiceException());
+ }
+
+ switch (req.type()) {
+ /**
+ * Register directory
+ */
+ case REGISTER: {
+ Object[] params = req.parameters();
+ Path path = (Path)params[0];
+ Set<? extends WatchEvent.Kind<?>> events =
+ (Set<? extends WatchEvent.Kind<?>>)params[1];
+ WatchEvent.Modifier[] modifiers =
+ (WatchEvent.Modifier[])params[2];
+ req.release(implRegister(path, events, modifiers));
+ break;
+ }
+ /**
+ * Cancel existing key
+ */
+ case CANCEL : {
+ Object[] params = req.parameters();
+ WatchKey key = (WatchKey)params[0];
+ implCancelKey(key);
+ req.release(null);
+ break;
+ }
+ /**
+ * Close watch service
+ */
+ case CLOSE: {
+ implCloseAll();
+ req.release(null);
+ shutdown = true;
+ break;
+ }
+
+ default:
+ req.release(new IOException("request not recognized"));
+ }
+ }
+ }
+ return shutdown;
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java b/src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java
new file mode 100644
index 000000000..3ac9b507f
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.ByteBuffer;
+import java.nio.file.attribute.UserDefinedFileAttributeView;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Base implementation of NamedAttributeView
+ */
+
+abstract class AbstractUserDefinedFileAttributeView
+ implements UserDefinedFileAttributeView
+{
+ protected AbstractUserDefinedFileAttributeView() { }
+
+ protected void checkAccess(String file,
+ boolean checkRead,
+ boolean checkWrite)
+ {
+ assert checkRead || checkWrite;
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (checkRead)
+ sm.checkRead(file);
+ if (checkWrite)
+ sm.checkWrite(file);
+ sm.checkPermission(new RuntimePermission("accessUserDefinedAttributes"));
+ }
+ }
+
+ @Override
+ public final String name() {
+ return "xattr";
+ }
+
+ @Override
+ public final Object getAttribute(String attribute) throws IOException {
+ int size;
+ try {
+ size = size(attribute);
+ } catch (IOException e) {
+ // not found or some other I/O error
+ if (list().contains(attribute))
+ throw e;
+ return null;
+ }
+ byte[] buf = new byte[size];
+ int n = read(attribute, ByteBuffer.wrap(buf));
+ return (n == size) ? buf : Arrays.copyOf(buf, n);
+ }
+
+ @Override
+ public final void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ ByteBuffer bb;
+ if (value instanceof byte[]) {
+ bb = ByteBuffer.wrap((byte[])value);
+ } else {
+ bb = (ByteBuffer)value;
+ }
+ write(attribute, bb);
+ }
+
+ @Override
+ public final Map<String,?> readAttributes(String first, String... rest)
+ throws IOException
+ {
+ // names of attributes to return
+ List<String> names = new ArrayList<String>();
+
+ boolean readAll = false;
+ if (first.equals("*")) {
+ readAll = true;
+ } else {
+ names.add(first);
+ }
+ for (String name: rest) {
+ if (name.equals("*")) {
+ readAll = true;
+ } else {
+ names.add(name);
+ }
+ }
+ if (readAll)
+ names = list();
+
+ // read each value and return in map
+ Map<String,Object> result = new HashMap<String,Object>();
+ for (String name: names) {
+ Object value = getAttribute(name);
+ if (value != null)
+ result.put(name, value);
+ }
+
+ return result;
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractWatchKey.java b/src/share/classes/sun/nio/fs/AbstractWatchKey.java
new file mode 100644
index 000000000..8e33f81ed
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractWatchKey.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.util.*;
+
+/**
+ * Base implementation class for watch keys.
+ */
+
+abstract class AbstractWatchKey extends WatchKey {
+
+ /**
+ * Maximum size of event list (in the future this may be tunable)
+ */
+ static final int MAX_EVENT_LIST_SIZE = 512;
+
+ /**
+ * Special event to signal overflow
+ */
+ static final Event<Void> OVERFLOW_EVENT =
+ new Event<Void>(StandardWatchEventKind.OVERFLOW, null);
+
+ /**
+ * Possible key states
+ */
+ private static enum State { READY, SIGNALLED };
+
+ // reference to watcher
+ private final AbstractWatchService watcher;
+
+ // key state
+ private State state;
+
+ // pending events
+ private List<WatchEvent<?>> events;
+
+ protected AbstractWatchKey(AbstractWatchService watcher) {
+ this.watcher = watcher;
+ this.state = State.READY;
+ this.events = new ArrayList<WatchEvent<?>>();
+ }
+
+ final AbstractWatchService watcher() {
+ return watcher;
+ }
+
+ /**
+ * Enqueues this key to the watch service
+ */
+ final void signal() {
+ synchronized (this) {
+ if (state == State.READY) {
+ state = State.SIGNALLED;
+ watcher.enqueueKey(this);
+ }
+ }
+ }
+
+ /**
+ * Adds the event to this key and signals it.
+ */
+ @SuppressWarnings("unchecked")
+ final void signalEvent(WatchEvent.Kind<?> kind, Object context) {
+ synchronized (this) {
+ int size = events.size();
+ if (size > 1) {
+ // don't let list get too big
+ if (size >= MAX_EVENT_LIST_SIZE) {
+ kind = StandardWatchEventKind.OVERFLOW;
+ context = null;
+ }
+
+ // repeated event
+ WatchEvent<?> prev = events.get(size-1);
+ if (kind == prev.kind()) {
+ boolean isRepeat;
+ if (context == null) {
+ isRepeat = (prev.context() == null);
+ } else {
+ isRepeat = context.equals(prev.context());
+ }
+ if (isRepeat) {
+ ((Event<?>)prev).increment();
+ return;
+ }
+ }
+ }
+
+ // non-repeated event
+ events.add(new Event<Object>((WatchEvent.Kind<Object>)kind, context));
+ signal();
+ }
+ }
+
+ @Override
+ public final List<WatchEvent<?>> pollEvents() {
+ synchronized (this) {
+ List<WatchEvent<?>> result = events;
+ events = new ArrayList<WatchEvent<?>>();
+ return result;
+ }
+ }
+
+ @Override
+ public final boolean reset() {
+ synchronized (this) {
+ if (state == State.SIGNALLED && isValid()) {
+ if (events.isEmpty()) {
+ state = State.READY;
+ } else {
+ // pending events so re-queue key
+ watcher.enqueueKey(this);
+ }
+ }
+ return isValid();
+ }
+ }
+
+ /**
+ * WatchEvent implementation
+ */
+ private static class Event<T> extends WatchEvent<T> {
+ private final WatchEvent.Kind<T> kind;
+ private final T context;
+
+ // synchronize on watch key to access/increment count
+ private int count;
+
+ Event(WatchEvent.Kind<T> type, T context) {
+ this.kind = type;
+ this.context = context;
+ this.count = 1;
+ }
+
+ @Override
+ public WatchEvent.Kind<T> kind() {
+ return kind;
+ }
+
+ @Override
+ public T context() {
+ return context;
+ }
+
+ @Override
+ public int count() {
+ return count;
+ }
+
+ // for repeated events
+ void increment() {
+ count++;
+ }
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/AbstractWatchService.java b/src/share/classes/sun/nio/fs/AbstractWatchService.java
new file mode 100644
index 000000000..c29d78b59
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/AbstractWatchService.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.util.concurrent.*;
+import java.io.IOException;
+
+/**
+ * Base implementation class for watch services.
+ */
+
+abstract class AbstractWatchService extends WatchService {
+
+ // signaled keys waiting to be dequeued
+ private final LinkedBlockingDeque<WatchKey> pendingKeys =
+ new LinkedBlockingDeque<WatchKey>();
+
+ // special key to indicate that watch service is closed
+ private final WatchKey CLOSE_KEY =
+ new AbstractWatchKey(null) {
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void cancel() {
+ }
+ };
+
+ // used when closing watch service
+ private volatile boolean closed;
+ private Object closeLock = new Object();
+
+ protected AbstractWatchService() {
+ }
+
+ /**
+ * Register the given object with this watch service
+ */
+ abstract WatchKey register(Path path,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifers)
+ throws IOException;
+
+ // used by AbstractWatchKey to enqueue key
+ final void enqueueKey(WatchKey key) {
+ pendingKeys.offer(key);
+ }
+
+ /**
+ * Throws ClosedWatchServiceException if watch service is closed
+ */
+ private void checkOpen() {
+ if (closed)
+ throw new ClosedWatchServiceException();
+ }
+
+ /**
+ * Checks the key isn't the special CLOSE_KEY used to unblock threads when
+ * the watch service is closed.
+ */
+ private void checkKey(WatchKey key) {
+ if (key == CLOSE_KEY) {
+ // re-queue in case there are other threads blocked in take/poll
+ enqueueKey(key);
+ }
+ checkOpen();
+ }
+
+ @Override
+ public final WatchKey poll() {
+ checkOpen();
+ WatchKey key = pendingKeys.poll();
+ checkKey(key);
+ return key;
+ }
+
+ @Override
+ public final WatchKey poll(long timeout, TimeUnit unit)
+ throws InterruptedException
+ {
+ checkOpen();
+ WatchKey key = pendingKeys.poll(timeout, unit);
+ checkKey(key);
+ return key;
+ }
+
+ @Override
+ public final WatchKey take()
+ throws InterruptedException
+ {
+ checkOpen();
+ WatchKey key = pendingKeys.take();
+ checkKey(key);
+ return key;
+ }
+
+ /**
+ * Tells whether or not this watch service is open.
+ */
+ final boolean isOpen() {
+ return !closed;
+ }
+
+ /**
+ * Retrieves the object upon which the close method synchronizes.
+ */
+ final Object closeLock() {
+ return closeLock;
+ }
+
+ /**
+ * Closes this watch service. This method is invoked by the close
+ * method to perform the actual work of closing the watch service.
+ */
+ abstract void implClose() throws IOException;
+
+ @Override
+ public final void close()
+ throws IOException
+ {
+ synchronized (closeLock) {
+ // nothing to do if already closed
+ if (closed)
+ return;
+ closed = true;
+
+ implClose();
+
+ // clear pending keys and queue special key to ensure that any
+ // threads blocked in take/poll wakeup
+ pendingKeys.clear();
+ pendingKeys.offer(CLOSE_KEY);
+ }
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/BasicFileAttributesHolder.java b/src/share/classes/sun/nio/fs/BasicFileAttributesHolder.java
new file mode 100644
index 000000000..28f8691ec
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/BasicFileAttributesHolder.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * Implemented by objects that may hold or cache the attributes of a file.
+ */
+
+public interface BasicFileAttributesHolder {
+ /**
+ * Returns cached attributes (may be null). If file is a symbolic link then
+ * the attributes are the link attributes and not the final target of the
+ * file.
+ */
+ BasicFileAttributes get();
+
+ /**
+ * Invalidates cached attributes
+ */
+ void invalidate();
+}
diff --git a/src/share/classes/sun/nio/fs/Cancellable.java b/src/share/classes/sun/nio/fs/Cancellable.java
new file mode 100644
index 000000000..3b40ddd10
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/Cancellable.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import sun.misc.Unsafe;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Base implementation of a task (typically native) that polls a memory location
+ * during execution so that it may be aborted/cancelled before completion. The
+ * task is executed by invoking the {@link runInterruptibly} method defined
+ * here and cancelled by invoking Thread.interrupt.
+ */
+
+abstract class Cancellable implements Runnable {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private final long pollingAddress;
+ private final Object lock = new Object();
+
+ // the following require lock when examining or changing
+ private boolean completed;
+ private Throwable exception;
+
+ protected Cancellable() {
+ pollingAddress = unsafe.allocateMemory(4);
+ unsafe.putIntVolatile(null, pollingAddress, 0);
+ }
+
+ /**
+ * Returns the memory address of a 4-byte int that should be polled to
+ * detect cancellation.
+ */
+ protected long addressToPollForCancel() {
+ return pollingAddress;
+ }
+
+ /**
+ * The value to write to the polled memory location to indicate that the
+ * task has been cancelled. If this method is not overridden then it
+ * defaults to MAX_VALUE.
+ */
+ protected int cancelValue() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * "cancels" the task by writing bits into memory location that it polled
+ * by the task.
+ */
+ final void cancel() {
+ synchronized (lock) {
+ if (!completed) {
+ unsafe.putIntVolatile(null, pollingAddress, cancelValue());
+ }
+ }
+ }
+
+ /**
+ * Returns the exception thrown by the task or null if the task completed
+ * successfully.
+ */
+ private Throwable exception() {
+ synchronized (lock) {
+ return exception;
+ }
+ }
+
+ @Override
+ public final void run() {
+ try {
+ implRun();
+ } catch (Throwable t) {
+ synchronized (lock) {
+ exception = t;
+ }
+ } finally {
+ synchronized (lock) {
+ completed = true;
+ unsafe.freeMemory(pollingAddress);
+ }
+ }
+ }
+
+ /**
+ * The task body. This should periodically poll the memory location
+ * to check for cancellation.
+ */
+ abstract void implRun() throws Throwable;
+
+ /**
+ * Invokes the given task in its own thread. If this (meaning the current)
+ * thread is interrupted then an attempt is make to cancel the background
+ * thread by writing into the memory location that it polls cooperatively.
+ */
+ static void runInterruptibly(Cancellable task) throws ExecutionException {
+ Thread t = new Thread(task);
+ t.start();
+ boolean cancelledByInterrupt = false;
+ while (t.isAlive()) {
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ cancelledByInterrupt = true;
+ task.cancel();
+ }
+ }
+ if (cancelledByInterrupt)
+ Thread.currentThread().interrupt();
+ Throwable exc = task.exception();
+ if (exc != null)
+ throw new ExecutionException(exc);
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java b/src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java
new file mode 100644
index 000000000..f40c2a797
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * An implementation of FileOwnerAttributeView that delegates to a given
+ * PosixFileAttributeView or AclFileAttributeView object.
+ */
+
+final class FileOwnerAttributeViewImpl implements FileOwnerAttributeView {
+ private static final String OWNER_NAME = "owner";
+
+ private final FileAttributeView view;
+ private final boolean isPosixView;
+
+ FileOwnerAttributeViewImpl(PosixFileAttributeView view) {
+ this.view = view;
+ this.isPosixView = true;
+ }
+
+ FileOwnerAttributeViewImpl(AclFileAttributeView view) {
+ this.view = view;
+ this.isPosixView = false;
+ }
+
+ @Override
+ public String name() {
+ return "owner";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(OWNER_NAME))
+ return getOwner();
+ return null;
+ }
+
+ @Override
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(OWNER_NAME)) {
+ setOwner((UserPrincipal)value);
+ return;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest) throws IOException {
+ Map<String,Object> result = new HashMap<String,Object>();
+ if (first.equals("*") || first.equals(OWNER_NAME)) {
+ result.put(OWNER_NAME, getOwner());
+ } else {
+ for (String attribute: rest) {
+ if (attribute.equals("*") || attribute.equals(OWNER_NAME)) {
+ result.put(OWNER_NAME, getOwner());
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public UserPrincipal getOwner() throws IOException {
+ if (isPosixView) {
+ return ((PosixFileAttributeView)view).readAttributes().owner();
+ } else {
+ return ((AclFileAttributeView)view).getOwner();
+ }
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner)
+ throws IOException
+ {
+ if (isPosixView) {
+ ((PosixFileAttributeView)view).setOwner(owner);
+ } else {
+ ((AclFileAttributeView)view).setOwner(owner);
+ }
+ }
+ }
diff --git a/src/share/classes/sun/nio/fs/Globs.java b/src/share/classes/sun/nio/fs/Globs.java
new file mode 100644
index 000000000..33857ea0c
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/Globs.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.util.regex.PatternSyntaxException;
+
+public class Globs {
+ private Globs() { }
+
+ private static final String regexMetaChars = ".^$+{[]|()";
+ private static final String globMetaChars = "\\*?[{";
+
+ private static boolean isRegexMeta(char c) {
+ return regexMetaChars.indexOf(c) != -1;
+ }
+
+ private static boolean isGlobMeta(char c) {
+ return globMetaChars.indexOf(c) != -1;
+ }
+ private static char EOL = 0; //TBD
+
+ private static char next(String glob, int i) {
+ if (i < glob.length()) {
+ return glob.charAt(i);
+ }
+ return EOL;
+ }
+
+ /**
+ * Creates a regex pattern from the given glob expression.
+ *
+ * @throws PatternSyntaxException
+ */
+ private static String toRegexPattern(String globPattern, boolean isDos) {
+ boolean inGroup = false;
+ StringBuilder regex = new StringBuilder("^");
+
+ int i = 0;
+ while (i < globPattern.length()) {
+ char c = globPattern.charAt(i++);
+ switch (c) {
+ case '\\':
+ // escape special characters
+ if (i == globPattern.length()) {
+ throw new PatternSyntaxException("No character to escape",
+ globPattern, i - 1);
+ }
+ char next = globPattern.charAt(i++);
+ if (isGlobMeta(next) || isRegexMeta(next)) {
+ regex.append('\\');
+ }
+ regex.append(next);
+ break;
+ case '/':
+ if (isDos) {
+ regex.append("\\\\");
+ } else {
+ regex.append(c);
+ }
+ break;
+ case '[':
+ // don't match name separator in class
+ if (isDos) {
+ regex.append("[[^\\\\]&&[");
+ } else {
+ regex.append("[[^/]&&[");
+ }
+ if (next(globPattern, i) == '^') {
+ // escape the regex negation char if it appears
+ regex.append("\\^");
+ i++;
+ } else {
+ // negation
+ if (next(globPattern, i) == '!') {
+ regex.append('^');
+ i++;
+ }
+ // hyphen allowed at start
+ if (next(globPattern, i) == '-') {
+ regex.append('-');
+ i++;
+ }
+ }
+ boolean hasRangeStart = false;
+ char last = 0;
+ while (i < globPattern.length()) {
+ c = globPattern.charAt(i++);
+ if (c == ']') {
+ break;
+ }
+ if (c == '/' || (isDos && c == '\\')) {
+ throw new PatternSyntaxException("Explicit 'name separator' in class",
+ globPattern, i - 1);
+ }
+ // TBD: how to specify ']' in a class?
+ if (c == '\\' || c == '[' ||
+ c == '&' && next(globPattern, i) == '&') {
+ // escape '\', '[' or "&&" for regex class
+ regex.append('\\');
+ }
+ regex.append(c);
+
+ if (c == '-') {
+ if (!hasRangeStart) {
+ throw new PatternSyntaxException("Invalid range",
+ globPattern, i - 1);
+ }
+ if ((c = next(globPattern, i++)) == EOL || c == ']') {
+ break;
+ }
+ if (c < last) {
+ throw new PatternSyntaxException("Invalid range",
+ globPattern, i - 3);
+ }
+ regex.append(c);
+ hasRangeStart = false;
+ } else {
+ hasRangeStart = true;
+ last = c;
+ }
+ }
+ if (c != ']') {
+ throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
+ }
+ regex.append("]]");
+ break;
+ case '{':
+ if (inGroup) {
+ throw new PatternSyntaxException("Cannot nest groups",
+ globPattern, i - 1);
+ }
+ regex.append("(?:(?:");
+ inGroup = true;
+ break;
+ case '}':
+ if (inGroup) {
+ regex.append("))");
+ inGroup = false;
+ } else {
+ regex.append('}');
+ }
+ break;
+ case ',':
+ if (inGroup) {
+ regex.append(")|(?:");
+ } else {
+ regex.append(',');
+ }
+ break;
+ case '*':
+ if (next(globPattern, i) == '*') {
+ // crosses directory boundaries
+ regex.append(".*");
+ i++;
+ } else {
+ // within directory boundary
+ if (isDos) {
+ regex.append("[^\\\\]*");
+ } else {
+ regex.append("[^/]*");
+ }
+ }
+ break;
+ case '?':
+ if (isDos) {
+ regex.append("[^\\\\]");
+ } else {
+ regex.append("[^/]");
+ }
+ break;
+
+ default:
+ if (isRegexMeta(c)) {
+ regex.append('\\');
+ }
+ regex.append(c);
+ }
+ }
+
+ if (inGroup) {
+ throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
+ }
+
+ return regex.append('$').toString();
+ }
+
+ static String toUnixRegexPattern(String globPattern) {
+ return toRegexPattern(globPattern, false);
+ }
+
+ static String toWindowsRegexPattern(String globPattern) {
+ return toRegexPattern(globPattern, true);
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/MimeType.java b/src/share/classes/sun/nio/fs/MimeType.java
new file mode 100644
index 000000000..053ea44c2
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/MimeType.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Represents a MIME type for the purposes of validation and matching. For
+ * now this class is implemented using the javax.activation.MimeType class but
+ * this dependency can easily be eliminated when required.
+ */
+
+public class MimeType {
+ private final javax.activation.MimeType type;
+
+ private MimeType(javax.activation.MimeType type) {
+ this.type = type;
+ }
+
+ /**
+ * Parses the given string as a MIME type.
+ *
+ * @throws IllegalArgumentException
+ * If the string is not a valid MIME type
+ */
+ public static MimeType parse(String type) {
+ try {
+ return new MimeType(new javax.activation.MimeType(type));
+ } catch (javax.activation.MimeTypeParseException x) {
+ throw new IllegalArgumentException(x);
+ }
+ }
+
+ /**
+ * Returns {@code true} if this MIME type has parameters.
+ */
+ public boolean hasParameters() {
+ return !type.getParameters().isEmpty();
+ }
+
+ /**
+ * Matches this MIME type against a given MIME type. This method returns
+ * true if the given string is a MIME type and it matches this type.
+ */
+ public boolean match(String other) {
+ try {
+ return type.match(other);
+ } catch (javax.activation.MimeTypeParseException x) {
+ return false;
+ }
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/NativeBuffer.java b/src/share/classes/sun/nio/fs/NativeBuffer.java
new file mode 100644
index 000000000..975c1c20b
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/NativeBuffer.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import sun.misc.Unsafe;
+import sun.misc.Cleaner;
+
+/**
+ * A light-weight buffer in native memory.
+ */
+
+class NativeBuffer {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private final long address;
+ private final int size;
+ private final Cleaner cleaner;
+
+ // optional "owner" to avoid copying
+ // (only safe for use by thread-local caches)
+ private Object owner;
+
+ private static class Deallocator implements Runnable {
+ private final long address;
+ Deallocator(long address) {
+ this.address = address;
+ }
+ public void run() {
+ unsafe.freeMemory(address);
+ }
+ }
+
+ NativeBuffer(int size) {
+ this.address = unsafe.allocateMemory(size);
+ this.size = size;
+ this.cleaner = Cleaner.create(this, new Deallocator(address));
+ }
+
+ void release() {
+ NativeBuffers.releaseNativeBuffer(this);
+ }
+
+ long address() {
+ return address;
+ }
+
+ int size() {
+ return size;
+ }
+
+ Cleaner cleaner() {
+ return cleaner;
+ }
+
+ // not synchronized; only safe for use by thread-local caches
+ void setOwner(Object owner) {
+ this.owner = owner;
+ }
+
+ // not synchronized; only safe for use by thread-local caches
+ Object owner() {
+ return owner;
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/NativeBuffers.java b/src/share/classes/sun/nio/fs/NativeBuffers.java
new file mode 100644
index 000000000..fa247a6ac
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/NativeBuffers.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import sun.misc.Unsafe;
+
+/**
+ * Factory for native buffers.
+ */
+
+class NativeBuffers {
+ private NativeBuffers() { }
+
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private static final int TEMP_BUF_POOL_SIZE = 3;
+ private static ThreadLocal<NativeBuffer[]> threadLocal =
+ new ThreadLocal<NativeBuffer[]>();
+
+ /**
+ * Allocates a native buffer, of at least the given size, from the heap.
+ */
+ static NativeBuffer allocNativeBuffer(int size) {
+ // Make a new one of at least 2k
+ if (size < 2048) size = 2048;
+ return new NativeBuffer(size);
+ }
+
+ /**
+ * Returns a native buffer, of at least the given size, from the thread
+ * local cache.
+ */
+ static NativeBuffer getNativeBufferFromCache(int size) {
+ // return from cache if possible
+ NativeBuffer[] buffers = threadLocal.get();
+ if (buffers != null) {
+ for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
+ NativeBuffer buffer = buffers[i];
+ if (buffer != null && buffer.size() >= size) {
+ buffers[i] = null;
+ return buffer;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a native buffer, of at least the given size. The native buffer
+ * is taken from the thread local cache if possible; otherwise it is
+ * allocated from the heap.
+ */
+ static NativeBuffer getNativeBuffer(int size) {
+ NativeBuffer buffer = getNativeBufferFromCache(size);
+ if (buffer != null) {
+ buffer.setOwner(null);
+ return buffer;
+ } else {
+ return allocNativeBuffer(size);
+ }
+ }
+
+ /**
+ * Releases the given buffer. If there is space in the thread local cache
+ * then the buffer goes into the cache; otherwise the memory is deallocated.
+ */
+ static void releaseNativeBuffer(NativeBuffer buffer) {
+ // create cache if it doesn't exist
+ NativeBuffer[] buffers = threadLocal.get();
+ if (buffers == null) {
+ buffers = new NativeBuffer[TEMP_BUF_POOL_SIZE];
+ buffers[0] = buffer;
+ threadLocal.set(buffers);
+ return;
+ }
+ // Put it in an empty slot if such exists
+ for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
+ if (buffers[i] == null) {
+ buffers[i] = buffer;
+ return;
+ }
+ }
+ // Otherwise replace a smaller one in the cache if such exists
+ for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
+ NativeBuffer existing = buffers[i];
+ if (existing.size() < buffer.size()) {
+ existing.cleaner().clean();
+ buffers[i] = buffer;
+ return;
+ }
+ }
+
+ // free it
+ buffer.cleaner().clean();
+ }
+
+ /**
+ * Copies a byte array and zero terminator into a given native buffer.
+ */
+ static void copyCStringToNativeBuffer(byte[] cstr, NativeBuffer buffer) {
+ long offset = Unsafe.ARRAY_BYTE_BASE_OFFSET;
+ long len = cstr.length;
+ assert buffer.size() >= (len + 1);
+ unsafe.copyMemory(cstr, offset, null, buffer.address(), len);
+ unsafe.putByte(buffer.address() + len, (byte)0);
+ }
+
+ /**
+ * Copies a byte array and zero terminator into a native buffer, returning
+ * the buffer.
+ */
+ static NativeBuffer asNativeBuffer(byte[] cstr) {
+ NativeBuffer buffer = getNativeBuffer(cstr.length+1);
+ copyCStringToNativeBuffer(cstr, buffer);
+ return buffer;
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/PollingWatchService.java b/src/share/classes/sun/nio/fs/PollingWatchService.java
new file mode 100644
index 000000000..73a89c271
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/PollingWatchService.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.*;
+import com.sun.nio.file.SensitivityWatchEventModifier;
+
+/**
+ * Simple WatchService implementation that uses periodic tasks to poll
+ * registered directories for changes. This implementation is for use on
+ * operating systems that do not have native file change notification support.
+ */
+
+class PollingWatchService
+ extends AbstractWatchService
+{
+ // map of registrations
+ private final Map<Object,PollingWatchKey> map =
+ new HashMap<Object,PollingWatchKey>();
+
+ // used to execute the periodic tasks that poll for changes
+ private final ScheduledExecutorService scheduledExecutor;
+
+ PollingWatchService() {
+ // TBD: Make the number of threads configurable
+ scheduledExecutor = Executors
+ .newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ return t;
+ }});
+ }
+
+ /**
+ * Register the given file with this watch service
+ */
+ @Override
+ WatchKey register(final Path path,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ // check events - CCE will be thrown if there are invalid elements
+ if (events.length == 0)
+ throw new IllegalArgumentException("No events to register");
+ final Set<WatchEvent.Kind<?>> eventSet =
+ new HashSet<WatchEvent.Kind<?>>(events.length);
+ for (WatchEvent.Kind<?> event: events) {
+ // standard events
+ if (event == StandardWatchEventKind.ENTRY_CREATE ||
+ event == StandardWatchEventKind.ENTRY_MODIFY ||
+ event == StandardWatchEventKind.ENTRY_DELETE)
+ {
+ eventSet.add(event);
+ continue;
+ }
+
+ // OVERFLOW is ignored
+ if (event == StandardWatchEventKind.OVERFLOW) {
+ if (events.length == 1)
+ throw new IllegalArgumentException("No events to register");
+ continue;
+ }
+
+ // null/unsupported
+ if (event == null)
+ throw new NullPointerException("An element in event set is 'null'");
+ throw new UnsupportedOperationException(event.name());
+ }
+
+ // A modifier may be used to specify the sensitivity level
+ SensitivityWatchEventModifier sensivity = SensitivityWatchEventModifier.MEDIUM;
+ if (modifiers.length > 0) {
+ for (WatchEvent.Modifier modifier: modifiers) {
+ if (modifier == null)
+ throw new NullPointerException();
+ if (modifier instanceof SensitivityWatchEventModifier) {
+ sensivity = (SensitivityWatchEventModifier)modifier;
+ continue;
+ }
+ throw new UnsupportedOperationException("Modifier not supported");
+ }
+ }
+
+ // check if watch service is closed
+ if (!isOpen())
+ throw new ClosedWatchServiceException();
+
+ // registration is done in privileged block as it requires the
+ // attributes of the entries in the directory.
+ try {
+ final SensitivityWatchEventModifier s = sensivity;
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<PollingWatchKey>() {
+ @Override
+ public PollingWatchKey run() throws IOException {
+ return doPrivilegedRegister(path, eventSet, s);
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ Throwable cause = pae.getCause();
+ if (cause != null && cause instanceof IOException)
+ throw (IOException)cause;
+ throw new AssertionError(pae);
+ }
+ }
+
+ // registers directory returning a new key if not already registered or
+ // existing key if already registered
+ private PollingWatchKey doPrivilegedRegister(Path path,
+ Set<? extends WatchEvent.Kind<?>> events,
+ SensitivityWatchEventModifier sensivity)
+ throws IOException
+ {
+ // check file is a directory and get its file key if possible
+ BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path);
+ if (!attrs.isDirectory()) {
+ throw new NotDirectoryException(path.toString());
+ }
+ Object fileKey = attrs.fileKey();
+ if (fileKey == null)
+ throw new AssertionError("File keys must be supported");
+
+ // grab close lock to ensure that watch service cannot be closed
+ synchronized (closeLock()) {
+ if (!isOpen())
+ throw new ClosedWatchServiceException();
+
+ PollingWatchKey watchKey;
+ synchronized (map) {
+ watchKey = map.get(fileKey);
+ if (watchKey == null) {
+ // new registration
+ watchKey = new PollingWatchKey(this, path, fileKey);
+ map.put(fileKey, watchKey);
+ } else {
+ // update to existing registration
+ watchKey.disable();
+ }
+ }
+ watchKey.enable(events, sensivity.sensitivityValueInSeconds());
+ return watchKey;
+ }
+
+ }
+
+ @Override
+ void implClose() throws IOException {
+ synchronized (map) {
+ for (Map.Entry<Object,PollingWatchKey> entry: map.entrySet()) {
+ PollingWatchKey watchKey = entry.getValue();
+ watchKey.disable();
+ watchKey.invalidate();
+ }
+ map.clear();
+ }
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ scheduledExecutor.shutdown();
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Entry in directory cache to record file last-modified-time and tick-count
+ */
+ private static class CacheEntry {
+ private long lastModified;
+ private int lastTickCount;
+
+ CacheEntry(long lastModified, int lastTickCount) {
+ this.lastModified = lastModified;
+ this.lastTickCount = lastTickCount;
+ }
+
+ int lastTickCount() {
+ return lastTickCount;
+ }
+
+ long lastModified() {
+ return lastModified;
+ }
+
+ void update(long lastModified, int tickCount) {
+ this.lastModified = lastModified;
+ this.lastTickCount = tickCount;
+ }
+ }
+
+ /**
+ * WatchKey implementation that encapsulates a map of the entries of the
+ * entries in the directory. Polling the key causes it to re-scan the
+ * directory and queue keys when entries are added, modified, or deleted.
+ */
+ private class PollingWatchKey extends AbstractWatchKey {
+ private final Path dir;
+ private final Object fileKey;
+
+ // current event set
+ private Set<? extends WatchEvent.Kind<?>> events;
+
+ // the result of the periodic task that causes this key to be polled
+ private ScheduledFuture<?> poller;
+
+ // indicates if the key is valid
+ private volatile boolean valid;
+
+ // used to detect files that have been deleted
+ private int tickCount;
+
+ // map of entries in directory
+ private Map<Path,CacheEntry> entries;
+
+ PollingWatchKey(PollingWatchService watcher,
+ Path dir,
+ Object fileKey)
+ throws IOException
+ {
+ super(watcher);
+ this.dir = dir;
+ this.fileKey = fileKey;
+ this.valid = true;
+ this.tickCount = 0;
+ this.entries = new HashMap<Path,CacheEntry>();
+
+ // get the initial entries in the directory
+ DirectoryStream<Path> stream = dir.newDirectoryStream();
+ try {
+ for (Path entry: stream) {
+ // don't follow links
+ long lastModified = Attributes
+ .readBasicFileAttributes(entry, LinkOption.NOFOLLOW_LINKS)
+ .lastModifiedTime();
+ entries.put(entry.getName(),
+ new CacheEntry(lastModified, tickCount));
+ }
+ } catch (ConcurrentModificationException cme) {
+ // thrown if directory iteration fails
+ Throwable cause = cme.getCause();
+ if (cause != null && cause instanceof IOException)
+ throw (IOException)cause;
+ throw new AssertionError(cme);
+ } finally {
+ stream.close();
+ }
+ }
+
+ FileRef directory() {
+ return dir;
+ }
+
+ Object fileKey() {
+ return fileKey;
+ }
+
+ @Override
+ public boolean isValid() {
+ return valid;
+ }
+
+ void invalidate() {
+ valid = false;
+ }
+
+ // enables periodic polling
+ void enable(Set<? extends WatchEvent.Kind<?>> events, long period) {
+ synchronized (this) {
+ // update the events
+ this.events = events;
+
+ // create the periodic task
+ Runnable thunk = new Runnable() { public void run() { poll(); }};
+ this.poller = scheduledExecutor
+ .scheduleAtFixedRate(thunk, period, period, TimeUnit.SECONDS);
+ }
+ }
+
+ // disables periodic polling
+ void disable() {
+ synchronized (this) {
+ if (poller != null)
+ poller.cancel(false);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ valid = false;
+ synchronized (map) {
+ map.remove(fileKey());
+ }
+ disable();
+ }
+
+ /**
+ * Polls the directory to detect for new files, modified files, or
+ * deleted files.
+ */
+ synchronized void poll() {
+ if (!valid) {
+ return;
+ }
+
+ // update tick
+ tickCount++;
+
+ // open directory
+ DirectoryStream<Path> stream = null;
+ try {
+ stream = dir.newDirectoryStream();
+ } catch (IOException x) {
+ // directory is no longer accessible so cancel key
+ cancel();
+ signal();
+ return;
+ }
+
+ // iterate over all entries in directory
+ try {
+ for (Path entry: stream) {
+ long lastModified = 0L;
+ try {
+ lastModified = Attributes
+ .readBasicFileAttributes(entry, LinkOption.NOFOLLOW_LINKS)
+ .lastModifiedTime();
+ } catch (IOException x) {
+ // unable to get attributes of entry. If file has just
+ // been deleted then we'll report it as deleted on the
+ // next poll
+ continue;
+ }
+
+ // lookup cache
+ CacheEntry e = entries.get(entry.getName());
+ if (e == null) {
+ // new file found
+ entries.put(entry.getName(),
+ new CacheEntry(lastModified, tickCount));
+
+ // queue ENTRY_CREATE if event enabled
+ if (events.contains(StandardWatchEventKind.ENTRY_CREATE)) {
+ signalEvent(StandardWatchEventKind.ENTRY_CREATE, entry.getName());
+ continue;
+ } else {
+ // if ENTRY_CREATE is not enabled and ENTRY_MODIFY is
+ // enabled then queue event to avoid missing out on
+ // modifications to the file immediately after it is
+ // created.
+ if (events.contains(StandardWatchEventKind.ENTRY_MODIFY)) {
+ signalEvent(StandardWatchEventKind.ENTRY_MODIFY, entry.getName());
+ }
+ }
+ continue;
+ }
+
+ // check if file has changed
+ if (e.lastModified != lastModified) {
+ if (events.contains(StandardWatchEventKind.ENTRY_MODIFY)) {
+ signalEvent(StandardWatchEventKind.ENTRY_MODIFY, entry.getName());
+ }
+ }
+ // entry in cache so update poll time
+ e.update(lastModified, tickCount);
+
+ }
+ } catch (ConcurrentModificationException x) {
+ // FIXME - should handle this
+ } finally {
+
+ // close directory stream
+ try {
+ stream.close();
+ } catch (IOException x) {
+ // ignore
+ }
+ }
+
+ // iterate over cache to detect entries that have been deleted
+ Iterator<Map.Entry<Path,CacheEntry>> i = entries.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<Path,CacheEntry> mapEntry = i.next();
+ CacheEntry entry = mapEntry.getValue();
+ if (entry.lastTickCount() != tickCount) {
+ Path name = mapEntry.getKey();
+ // remove from map and queue delete event (if enabled)
+ i.remove();
+ if (events.contains(StandardWatchEventKind.ENTRY_DELETE)) {
+ signalEvent(StandardWatchEventKind.ENTRY_DELETE, name);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/share/classes/sun/nio/fs/Reflect.java b/src/share/classes/sun/nio/fs/Reflect.java
new file mode 100644
index 000000000..d425ef249
--- /dev/null
+++ b/src/share/classes/sun/nio/fs/Reflect.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Utility class for reflection.
+ */
+
+class Reflect {
+ private Reflect() {}
+
+ private static void setAccessible(final AccessibleObject ao) {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ ao.setAccessible(true);
+ return null;
+ }});
+ }
+
+ /**
+ * Lookup the field of a given class.
+ */
+ static Field lookupField(String className, String fieldName) {
+ try {
+ Class<?> cl = Class.forName(className);
+ Field f = cl.getDeclaredField(fieldName);
+ setAccessible(f);
+ return f;
+ } catch (ClassNotFoundException x) {
+ throw new AssertionError(x);
+ } catch (NoSuchFieldException x) {
+ throw new AssertionError(x);
+ }
+ }
+}
diff --git a/src/share/classes/sun/security/krb5/Config.java b/src/share/classes/sun/security/krb5/Config.java
index 2a16b983f..07c78e295 100644
--- a/src/share/classes/sun/security/krb5/Config.java
+++ b/src/share/classes/sun/security/krb5/Config.java
@@ -1,5 +1,5 @@
/*
- * Portions Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Portions Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -39,7 +39,6 @@ import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Enumeration;
-import java.util.List;
import java.util.StringTokenizer;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -74,7 +73,7 @@ public class Config {
private String defaultRealm; // default kdc realm.
// used for native interface
- private static native String getWindowsDirectory();
+ private static native String getWindowsDirectory(boolean isSystem);
/**
@@ -156,11 +155,7 @@ public class Config {
configFile = loadConfigFile();
stanzaTable = parseStanzaTable(configFile);
} catch (IOException ioe) {
- KrbException ke = new KrbException("Could not load " +
- "configuration file " +
- ioe.getMessage());
- ke.initCause(ioe);
- throw(ke);
+ // No krb5.conf, no problem. We'll use DNS etc.
}
}
}
@@ -661,38 +656,37 @@ public class Config {
}
/**
- * Gets the default configuration file name. The file will be searched
- * in a list of possible loations in the following order:
- * 1. the location and file name defined by system property
- * "java.security.krb5.conf",
- * 2. at Java home lib\security directory with "krb5.conf" name,
- * 3. "krb5.ini" at Java home,
- * 4. at windows directory with the name of "krb5.ini" for Windows,
- * /etc/krb5/krb5.conf for Solaris, /etc/krb5.conf for Linux.
+ * Gets the default configuration file name. This method will never
+ * return null.
+ *
+ * If the system property "java.security.krb5.conf" is defined, we'll
+ * use its value, no matter if the file exists or not. Otherwise,
+ * the file will be searched in a list of possible loations in the
+ * following order:
+ *
+ * 1. at Java home lib\security directory with "krb5.conf" name,
+ * 2. at windows directory with the name of "krb5.ini" for Windows,
+ * /etc/krb5/krb5.conf for Solaris, /etc/krb5.conf otherwise.
+ *
+ * Note: When the Terminal Service is started in Windows (from 2003),
+ * there are two kinds of Windows directories: A system one (say,
+ * C:\Windows), and a user-private one (say, C:\Users\Me\Windows).
+ * We will first look for krb5.ini in the user-private one. If not
+ * found, try the system one instead.
*/
private String getFileName() {
String name =
java.security.AccessController.doPrivileged(
new sun.security.action.
GetPropertyAction("java.security.krb5.conf"));
- if (name != null) {
- boolean temp =
- java.security.AccessController.doPrivileged(
- new FileExistsAction(name));
- if (temp)
- return name;
- } else {
+ if (name == null) {
name = java.security.AccessController.doPrivileged(
new sun.security.action.
GetPropertyAction("java.home")) + File.separator +
"lib" + File.separator + "security" +
File.separator + "krb5.conf";
- boolean temp =
- java.security.AccessController.doPrivileged(
- new FileExistsAction(name));
- if (temp) {
- return name;
- } else {
+ if (!fileExists(name)) {
+ name = null;
String osname =
java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("os.name"));
@@ -703,19 +697,35 @@ public class Config {
// ignore exceptions
}
if (Credentials.alreadyLoaded) {
- if ((name = getWindowsDirectory()) == null) {
- name = "c:\\winnt\\krb5.ini";
- } else if (name.endsWith("\\")) {
- name += "krb5.ini";
- } else {
- name += "\\krb5.ini";
+ String path = getWindowsDirectory(false);
+ if (path != null) {
+ if (path.endsWith("\\")) {
+ path = path + "krb5.ini";
+ } else {
+ path = path + "\\krb5.ini";
+ }
+ if (fileExists(path)) {
+ name = path;
+ }
}
- } else {
+ if (name == null) {
+ path = getWindowsDirectory(true);
+ if (path != null) {
+ if (path.endsWith("\\")) {
+ path = path + "krb5.ini";
+ } else {
+ path = path + "\\krb5.ini";
+ }
+ name = path;
+ }
+ }
+ }
+ if (name == null) {
name = "c:\\winnt\\krb5.ini";
}
} else if (osname.startsWith("SunOS")) {
name = "/etc/krb5/krb5.conf";
- } else if (osname.startsWith("Linux")) {
+ } else {
name = "/etc/krb5.conf";
}
}
@@ -1042,7 +1052,12 @@ public class Config {
public boolean useDNS(String name) {
String value = getDefault(name, "libdefaults");
if (value == null) {
- return getDefaultBooleanValue("dns_fallback", "libdefaults");
+ value = getDefault("dns_fallback", "libdefaults");
+ if ("false".equalsIgnoreCase(value)) {
+ return false;
+ } else {
+ return true;
+ }
} else {
return value.equalsIgnoreCase("true");
}
@@ -1064,12 +1079,39 @@ public class Config {
/**
* Gets default realm.
+ * @throws KrbException where no realm can be located
+ * @return the default realm, always non null
*/
public String getDefaultRealm() throws KrbException {
+ Exception cause = null;
String realm = getDefault("default_realm", "libdefaults");
if ((realm == null) && useDNS_Realm()) {
// use DNS to locate Kerberos realm
- realm = getRealmFromDNS();
+ try {
+ realm = getRealmFromDNS();
+ } catch (KrbException ke) {
+ cause = ke;
+ }
+ }
+ if (realm == null) {
+ realm = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ String osname = System.getProperty("os.name");
+ if (osname.startsWith("Windows")) {
+ return System.getenv("USERDNSDOMAIN");
+ }
+ return null;
+ }
+ });
+ }
+ if (realm == null) {
+ KrbException ke = new KrbException("Cannot locate default realm");
+ if (cause != null) {
+ ke.initCause(cause);
+ }
+ throw ke;
}
return realm;
}
@@ -1077,17 +1119,48 @@ public class Config {
/**
* Returns a list of KDC's with each KDC separated by a space
*
- * @param realm the realm for which the master KDC is desired
- * @return the list of KDCs
+ * @param realm the realm for which the KDC list is desired
+ * @throws KrbException if there's no way to find KDC for the realm
+ * @return the list of KDCs separated by a space, always non null
*/
public String getKDCList(String realm) throws KrbException {
if (realm == null) {
realm = getDefaultRealm();
}
+ Exception cause = null;
String kdcs = getDefault("kdc", realm);
if ((kdcs == null) && useDNS_KDC()) {
// use DNS to locate KDC
- kdcs = getKDCFromDNS(realm);
+ try {
+ kdcs = getKDCFromDNS(realm);
+ } catch (KrbException ke) {
+ cause = ke;
+ }
+ }
+ if (kdcs == null) {
+ kdcs = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ String osname = System.getProperty("os.name");
+ if (osname.startsWith("Windows")) {
+ String logonServer = System.getenv("LOGONSERVER");
+ if (logonServer != null
+ && logonServer.startsWith("\\\\")) {
+ logonServer = logonServer.substring(2);
+ }
+ return logonServer;
+ }
+ return null;
+ }
+ });
+ }
+ if (kdcs == null) {
+ KrbException ke = new KrbException("Cannot locate KDC");
+ if (cause != null) {
+ ke.initCause(cause);
+ }
+ throw ke;
}
return kdcs;
}
@@ -1102,7 +1175,7 @@ public class Config {
String realm = null;
String hostName = null;
try {
- hostName = InetAddress.getLocalHost().getHostName();
+ hostName = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException e) {
KrbException ke = new KrbException(Krb5.KRB_ERR_GENERIC,
"Unable to locate Kerberos realm: " + e.getMessage());
@@ -1171,6 +1244,11 @@ public class Config {
return kdcs;
}
+ private boolean fileExists(String name) {
+ return java.security.AccessController.doPrivileged(
+ new FileExistsAction(name));
+ }
+
static class FileExistsAction
implements java.security.PrivilegedAction<Boolean> {
diff --git a/src/share/classes/sun/security/krb5/KrbServiceLocator.java b/src/share/classes/sun/security/krb5/KrbServiceLocator.java
index 0efe37d1a..fefcdbde0 100644
--- a/src/share/classes/sun/security/krb5/KrbServiceLocator.java
+++ b/src/share/classes/sun/security/krb5/KrbServiceLocator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2006-2009 Sun Microsystems, Inc. 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
@@ -133,7 +133,7 @@ class KrbServiceLocator {
*/
static String[] getKerberosService(String realmName, String protocol) {
- String dnsUrl = "dns:///_kerberos." + protocol + realmName;
+ String dnsUrl = "dns:///_kerberos." + protocol + "." + realmName;
String[] hostports = null;
try {
diff --git a/src/share/classes/sun/security/krb5/Realm.java b/src/share/classes/sun/security/krb5/Realm.java
index 1dbffe264..a34745d87 100644
--- a/src/share/classes/sun/security/krb5/Realm.java
+++ b/src/share/classes/sun/security/krb5/Realm.java
@@ -39,7 +39,6 @@ import sun.security.krb5.RealmException;
import sun.security.krb5.internal.Krb5;
import sun.security.util.*;
import java.io.IOException;
-import java.io.OutputStream;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Stack;
@@ -364,7 +363,6 @@ public class Realm implements Cloneable {
}
String tempTarget = null, tempRealm = null;
- StringTokenizer strTok = null;
Stack<String> iStack = new Stack<String> ();
/*
@@ -382,7 +380,7 @@ public class Realm implements Cloneable {
tempTarget = sRealm;
}
- do {
+ out: do {
if (DEBUG) {
count++;
System.out.println(">>> Realm parseCapaths: loop " +
@@ -400,15 +398,21 @@ public class Realm implements Cloneable {
/*
* We have one or more space-separated intermediary realms.
- * Stack them.
+ * Stack them. A null is always added between intermedies of
+ * different targets. When this null is popped, it means none
+ * of the intermedies for this target is useful (because of
+ * infinite loop), the target is then removed from the partial
+ * tempList, and the next possible intermediary is tried.
*/
- strTok = new StringTokenizer(intermediaries, " ");
- while (strTok.hasMoreTokens())
+ iStack.push(null);
+ String[] ints = intermediaries.split("\\s+");
+ for (int i = ints.length-1; i>=0; i--)
{
- tempRealm = strTok.nextToken();
- if (!tempRealm.equals(PrincipalName.
- REALM_COMPONENT_SEPARATOR_STR) &&
- !iStack.contains(tempRealm)) {
+ tempRealm = ints[i];
+ if (tempRealm.equals(PrincipalName.REALM_COMPONENT_SEPARATOR_STR)) {
+ break out;
+ }
+ if (!tempList.contains(tempRealm)) {
iStack.push(tempRealm);
if (DEBUG) {
System.out.println(">>> Realm parseCapaths: loop " +
@@ -418,16 +422,18 @@ public class Realm implements Cloneable {
}
} else if (DEBUG) {
System.out.println(">>> Realm parseCapaths: loop " +
-
count +
": ignoring realm: [" +
tempRealm + "]");
}
}
- } else if (DEBUG) {
- System.out.println(">>> Realm parseCapaths: loop " +
- count +
- ": no intermediaries");
+ } else {
+ if (DEBUG) {
+ System.out.println(">>> Realm parseCapaths: loop " +
+ count +
+ ": no intermediaries");
+ }
+ break;
}
/*
@@ -435,7 +441,12 @@ public class Realm implements Cloneable {
*/
try {
- tempTarget = iStack.pop();
+ while ((tempTarget = iStack.pop()) == null) {
+ tempList.removeElementAt(tempList.size()-1);
+ if (DEBUG) {
+ System.out.println(">>> Realm parseCapaths: backtrack, remove tail");
+ }
+ }
} catch (EmptyStackException exc) {
tempTarget = null;
}
diff --git a/src/share/classes/sun/security/provider/X509Factory.java b/src/share/classes/sun/security/provider/X509Factory.java
index 8fc755cc8..ce0d120ae 100644
--- a/src/share/classes/sun/security/provider/X509Factory.java
+++ b/src/share/classes/sun/security/provider/X509Factory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2009 Sun Microsystems, Inc. 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
@@ -638,10 +638,15 @@ is)
// First read all of the data that is found between
// the "-----BEGIN" and "-----END" boundaries into a buffer.
String temp;
- if ((temp=readLine(br))==null || !temp.startsWith("-----BEGIN")) {
- throw new IOException("Unsupported encoding");
- } else {
+ while (true) {
+ temp=readLine(br);
+ if (temp == null) {
+ throw new IOException("Unsupported encoding");
+ }
len += temp.length();
+ if (temp.startsWith("-----BEGIN")) {
+ break;
+ }
}
StringBuffer strBuf = new StringBuffer();
while ((temp=readLine(br))!=null && !temp.startsWith("-----END")) {
@@ -683,22 +688,11 @@ is)
* Determines if input is binary or Base64 encoded.
*/
private boolean isBase64(InputStream is) throws IOException {
- if (is.available() >= 10) {
- is.mark(10);
+ if (is.available() >= 1) {
+ is.mark(1);
int c1 = is.read();
- int c2 = is.read();
- int c3 = is.read();
- int c4 = is.read();
- int c5 = is.read();
- int c6 = is.read();
- int c7 = is.read();
- int c8 = is.read();
- int c9 = is.read();
- int c10 = is.read();
is.reset();
- if (c1 == '-' && c2 == '-' && c3 == '-' && c4 == '-'
- && c5 == '-' && c6 == 'B' && c7 == 'E' && c8 == 'G'
- && c9 == 'I' && c10 == 'N') {
+ if (c1 != DerValue.tag_Sequence) {
return true;
} else {
return false;
diff --git a/src/share/classes/sun/security/ssl/AppInputStream.java b/src/share/classes/sun/security/ssl/AppInputStream.java
index d638b8f46..2ca889593 100644
--- a/src/share/classes/sun/security/ssl/AppInputStream.java
+++ b/src/share/classes/sun/security/ssl/AppInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -81,6 +81,14 @@ class AppInputStream extends InputStream {
*/
public synchronized int read(byte b[], int off, int len)
throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
if (c.checkEOF()) {
return -1;
}
diff --git a/src/share/classes/sun/security/ssl/AppOutputStream.java b/src/share/classes/sun/security/ssl/AppOutputStream.java
index d039cacb8..d8c78a8eb 100644
--- a/src/share/classes/sun/security/ssl/AppOutputStream.java
+++ b/src/share/classes/sun/security/ssl/AppOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -58,18 +58,25 @@ class AppOutputStream extends OutputStream {
*/
synchronized public void write(byte b[], int off, int len)
throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+
// check if the Socket is invalid (error or closed)
c.checkWrite();
- //
+
// Always flush at the end of each application level record.
// This lets application synchronize read and write streams
// however they like; if we buffered here, they couldn't.
- //
- // NOTE: *must* call c.writeRecord() even for len == 0
try {
do {
int howmuch = Math.min(len, r.availableDataBytes());
+ // NOTE: *must* call c.writeRecord() even for howmuch == 0
if (howmuch > 0) {
r.write(b, off, howmuch);
off += howmuch;
diff --git a/src/share/classes/sun/security/ssl/ByteBufferInputStream.java b/src/share/classes/sun/security/ssl/ByteBufferInputStream.java
index 9c484d87e..d69e2bfdc 100644
--- a/src/share/classes/sun/security/ssl/ByteBufferInputStream.java
+++ b/src/share/classes/sun/security/ssl/ByteBufferInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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
@@ -89,8 +89,7 @@ class ByteBufferInputStream extends InputStream {
if (b == null) {
throw new NullPointerException();
- } else if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) > b.length) || ((off + len) < 0)) {
+ } else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
diff --git a/src/share/classes/sun/security/ssl/SSLSessionContextImpl.java b/src/share/classes/sun/security/ssl/SSLSessionContextImpl.java
index c98f1cc2c..3aeef2d8e 100644
--- a/src/share/classes/sun/security/ssl/SSLSessionContextImpl.java
+++ b/src/share/classes/sun/security/ssl/SSLSessionContextImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2009 Sun Microsystems, Inc. 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
@@ -41,88 +41,112 @@ import javax.net.ssl.SSLSessionBindingEvent;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
-import sun.misc.Cache;
+import sun.security.util.Cache;
-final class SSLSessionContextImpl implements SSLSessionContext
-{
- private Cache sessionCache = new Cache();
- private Cache sessionHostPortCache = new Cache();
- private int cacheLimit;
- private long timeoutMillis;
+final class SSLSessionContextImpl implements SSLSessionContext {
+ private Cache sessionCache; // session cache, session id as key
+ private Cache sessionHostPortCache; // session cache, "host:port" as key
+ private int cacheLimit; // the max cache size
+ private int timeout; // timeout in seconds
+
private static final Debug debug = Debug.getInstance("ssl");
- // file private
- SSLSessionContextImpl()
- {
- cacheLimit = getCacheLimit();
- timeoutMillis = 86400000; // default, 24 hours
+ // package private
+ SSLSessionContextImpl() {
+ cacheLimit = getDefaultCacheLimit(); // default cache size
+ timeout = 86400; // default, 24 hours
+
+ // use soft reference
+ sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
+ sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
}
/**
- * Returns the SSL session object associated with the
- * specific session ID passed.
+ * Returns the <code>SSLSession</code> bound to the specified session id.
*/
- public SSLSession getSession(byte[] id)
- {
- SSLSession sess = (SSLSession) sessionCache.get(
- new SessionId(id));
- return checkTimeValidity(sess);
+ public SSLSession getSession(byte[] sessionId) {
+ if (sessionId == null) {
+ throw new NullPointerException("session id cannot be null");
+ }
+
+ SSLSessionImpl sess =
+ (SSLSessionImpl)sessionCache.get(new SessionId(sessionId));
+ if (!isTimedout(sess)) {
+ return sess;
+ }
+
+ return null;
}
/**
* Returns an enumeration of the active SSL sessions.
*/
public Enumeration<byte[]> getIds() {
- Vector<byte[]> v = new Vector<byte[]>(sessionCache.size());
- SessionId sessId;
+ SessionCacheVisitor scVisitor = new SessionCacheVisitor();
+ sessionCache.accept(scVisitor);
- for (Enumeration e = sessionCache.keys(); e.hasMoreElements(); ) {
- sessId = (SessionId) e.nextElement();
- if (!isTimedout((SSLSession)sessionCache.get(sessId)))
- v.addElement(sessId.getId());
- }
- return v.elements();
+ return scVisitor.getSessionIds();
}
+ /**
+ * Sets the timeout limit for cached <code>SSLSession</code> objects
+ *
+ * Note that after reset the timeout, the cached session before
+ * should be timed within the shorter one of the old timeout and the
+ * new timeout.
+ */
public void setSessionTimeout(int seconds)
throws IllegalArgumentException {
- if (seconds < 0)
+ if (seconds < 0) {
throw new IllegalArgumentException();
- timeoutMillis = seconds * 1000L;
+ }
+
+ if (timeout != seconds) {
+ sessionCache.setTimeout(seconds);
+ sessionHostPortCache.setTimeout(seconds);
+ timeout = seconds;
+ }
}
+ /**
+ * Gets the timeout limit for cached <code>SSLSession</code> objects
+ */
public int getSessionTimeout() {
- return (int) (timeoutMillis / 1000);
+ return timeout;
}
+ /**
+ * Sets the size of the cache used for storing
+ * <code>SSLSession</code> objects.
+ */
public void setSessionCacheSize(int size)
throws IllegalArgumentException {
if (size < 0)
throw new IllegalArgumentException();
- cacheLimit = size;
- /**
- * If cache size limit is reduced, when the cache is full to its
- * previous limit, trim the cache before its contents
- * are used.
- */
- if ((cacheLimit != 0) && (sessionCache.size() > cacheLimit))
- adjustCacheSizeTo(cacheLimit);
+ if (cacheLimit != size) {
+ sessionCache.setCapacity(size);
+ sessionHostPortCache.setCapacity(size);
+ cacheLimit = size;
+ }
}
+ /**
+ * Gets the size of the cache used for storing
+ * <code>SSLSession</code> objects.
+ */
public int getSessionCacheSize() {
return cacheLimit;
}
+
+ // package-private method, used ONLY by ServerHandshaker
SSLSessionImpl get(byte[] id) {
- return (SSLSessionImpl) getSession(id);
+ return (SSLSessionImpl)getSession(id);
}
- /**
- * Returns the SSL session object associated with the
- * specific host name and port number passed.
- */
+ // package-private method, used ONLY by ClientHandshaker
SSLSessionImpl get(String hostname, int port) {
/*
* If no session caching info is available, we won't
@@ -131,96 +155,51 @@ final class SSLSessionContextImpl implements SSLSessionContext
if (hostname == null && port == -1) {
return null;
}
- SSLSession sess = (SSLSessionImpl) sessionHostPortCache
- .get(getKey(hostname, port));
- return (SSLSessionImpl) checkTimeValidity(sess);
+
+ SSLSessionImpl sess =
+ (SSLSessionImpl)sessionHostPortCache.get(getKey(hostname, port));
+ if (!isTimedout(sess)) {
+ return sess;
+ }
+
+ return null;
}
private String getKey(String hostname, int port) {
- return (hostname + ":" + String.valueOf(port))
- .toLowerCase();
+ return (hostname + ":" + String.valueOf(port)).toLowerCase();
}
+ // cache a SSLSession
+ //
+ // In SunJSSE implementation, a session is created while getting a
+ // client hello or a server hello message, and cached while the
+ // handshaking finished.
+ // Here we time the session from the time it cached instead of the
+ // time it created, which is a little longer than the expected. So
+ // please do check isTimedout() while getting entry from the cache.
void put(SSLSessionImpl s) {
- // make space for the new session to be added
- if ((cacheLimit != 0) && (sessionCache.size() >= cacheLimit))
- adjustCacheSizeTo(cacheLimit - 1);
-
- /*
- * Can always add the session id.
- */
sessionCache.put(s.getSessionId(), s);
- /*
- * If no hostname/port info is available, don't add this one.
- */
+ // If no hostname/port info is available, don't add this one.
if ((s.getPeerHost() != null) && (s.getPeerPort() != -1)) {
sessionHostPortCache.put(
getKey(s.getPeerHost(), s.getPeerPort()), s);
}
+
s.setContext(this);
}
- private void adjustCacheSizeTo(int targetSize) {
-
- int cacheSize = sessionCache.size();
-
- if (targetSize < 0)
- return;
-
- while (cacheSize > targetSize) {
- SSLSessionImpl lru = null;
- SSLSessionImpl s = null;
- Enumeration e;
-
- if (debug != null && Debug.isOn("sessioncache")) {
- System.out.println("exceeded cache limit of " + cacheLimit);
- }
-
- /*
- * Count the number of elements in the cache. The size() method
- * does not reflect the cache entries that are no longer available,
- * i.e entries that are garbage collected (the cache entries are
- * held using soft references and are garbage collected when not
- * in use).
- */
- int count;
- for (count = 0, e = sessionCache.elements();
- e.hasMoreElements(); count++) {
- try {
- s = (SSLSessionImpl)e.nextElement();
- } catch (NoSuchElementException nsee) {
- break;
- }
- if (isTimedout(s)) {
- lru = s;
- break;
- } else if ((lru == null) || (s.getLastAccessedTime()
- < lru.getLastAccessedTime())) {
- lru = s;
- }
- }
- if ((lru != null) && (count > targetSize)) {
- if (debug != null && Debug.isOn("sessioncache")) {
- System.out.println("uncaching " + lru);
- }
- lru.invalidate();
- count--; // element removed from the cache
- }
- cacheSize = count;
+ // package-private method, remove a cached SSLSession
+ void remove(SessionId key) {
+ SSLSessionImpl s = (SSLSessionImpl)sessionCache.get(key);
+ if (s != null) {
+ sessionCache.remove(key);
+ sessionHostPortCache.remove(
+ getKey(s.getPeerHost(), s.getPeerPort()));
}
}
- // file private
- void remove(SessionId key)
- {
- SSLSessionImpl s = (SSLSessionImpl) sessionCache.get(key);
- sessionCache.remove(key);
- sessionHostPortCache.remove(getKey(s.getPeerHost(),
- s.getPeerPort()));
- }
-
- private int getCacheLimit() {
+ private int getDefaultCacheLimit() {
int cacheLimit = 0;
try {
String s = java.security.AccessController.doPrivileged(
@@ -237,21 +216,40 @@ final class SSLSessionContextImpl implements SSLSessionContext
return (cacheLimit > 0) ? cacheLimit : 0;
}
- SSLSession checkTimeValidity(SSLSession sess) {
- if (isTimedout(sess)) {
- sess.invalidate();
- return null;
- } else
- return sess;
- }
-
boolean isTimedout(SSLSession sess) {
- if (timeoutMillis == 0)
+ if (timeout == 0) {
return false;
- if ((sess != null) &&
- ((sess.getCreationTime() + timeoutMillis)
- <= (System.currentTimeMillis())))
+ }
+
+ if ((sess != null) && ((sess.getCreationTime() + timeout * 1000L)
+ <= (System.currentTimeMillis()))) {
+ sess.invalidate();
return true;
+ }
+
return false;
}
+
+ final class SessionCacheVisitor
+ implements sun.security.util.Cache.CacheVisitor {
+ Vector<byte[]> ids = null;
+
+ // public void visit(java.util.Map<Object, Object> map) {}
+ public void visit(java.util.Map<Object, Object> map) {
+ ids = new Vector<byte[]>(map.size());
+
+ for (Object key : map.keySet()) {
+ SSLSessionImpl value = (SSLSessionImpl)map.get(key);
+ if (!isTimedout(value)) {
+ ids.addElement(((SessionId)key).getId());
+ }
+ }
+ }
+
+ public Enumeration<byte[]> getSessionIds() {
+ return ids != null ? ids.elements() :
+ new Vector<byte[]>().elements();
+ }
+ }
+
}
diff --git a/src/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/share/classes/sun/security/ssl/SSLSocketImpl.java
index 7974f1b13..820954f96 100644
--- a/src/share/classes/sun/security/ssl/SSLSocketImpl.java
+++ b/src/share/classes/sun/security/ssl/SSLSocketImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -368,7 +368,9 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
super();
this.host = host;
init(context, false);
- SocketAddress socketAddress = new InetSocketAddress(host, port);
+ SocketAddress socketAddress =
+ host != null ? new InetSocketAddress(host, port) :
+ new InetSocketAddress(InetAddress.getByName(null), port);
connect(socketAddress, 0);
}
@@ -409,7 +411,9 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
this.host = host;
init(context, false);
bind(new InetSocketAddress(localAddr, localPort));
- SocketAddress socketAddress = new InetSocketAddress(host, port);
+ SocketAddress socketAddress =
+ host != null ? new InetSocketAddress(host, port) :
+ new InetSocketAddress(InetAddress.getByName(null), port);
connect(socketAddress, 0);
}
@@ -1829,7 +1833,8 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
}
synchronized String getHost() {
- if (host == null) {
+ // Note that the host may be null or empty for localhost.
+ if (host == null || host.length() == 0) {
host = getInetAddress().getHostName();
}
return host;
diff --git a/src/share/classes/sun/security/tools/KeyTool.java b/src/share/classes/sun/security/tools/KeyTool.java
index 220dbd033..e99c40d9d 100644
--- a/src/share/classes/sun/security/tools/KeyTool.java
+++ b/src/share/classes/sun/security/tools/KeyTool.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -69,6 +69,10 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+import sun.misc.BASE64Decoder;
+import sun.security.pkcs.PKCS10Attribute;
+import sun.security.pkcs.PKCS9Attribute;
+import sun.security.util.DerValue;
import sun.security.x509.*;
import static java.security.KeyStore.*;
@@ -85,7 +89,6 @@ import static java.security.KeyStore.*;
*
* @since 1.2
*/
-
public final class KeyTool {
private boolean debug = false;
@@ -100,6 +103,8 @@ public final class KeyTool {
private String dname = null;
private String dest = null;
private String filename = null;
+ private String infilename = null;
+ private String outfilename = null;
private String srcksfname = null;
// User-specified providers are added before any command is called.
@@ -117,7 +122,6 @@ public final class KeyTool {
private char[] storePassNew = null;
private char[] keyPass = null;
private char[] keyPassNew = null;
- private char[] oldPass = null;
private char[] newPass = null;
private char[] destKeyPass = null;
private char[] srckeyPass = null;
@@ -140,6 +144,8 @@ public final class KeyTool {
private Set<char[]> passwords = new HashSet<char[]> ();
private String startDate = null;
+ private List <String> v3ext = new ArrayList <String> ();
+
private static final int CERTREQ = 1;
private static final int CHANGEALIAS = 2;
private static final int DELETE = 3;
@@ -156,6 +162,8 @@ public final class KeyTool {
private static final int PRINTCERT = 13;
private static final int SELFCERT = 14;
private static final int STOREPASSWD = 15;
+ private static final int GENCERT = 16;
+ private static final int PRINTCERTREQ = 17;
private static final Class[] PARAM_STRING = { String.class };
@@ -184,7 +192,9 @@ public final class KeyTool {
private void run(String[] args, PrintStream out) throws Exception {
try {
parseArgs(args);
- doCommands(out);
+ if (command != -1) {
+ doCommands(out);
+ }
} catch (Exception e) {
System.out.println(rb.getString("keytool error: ") + e);
if (verbose) {
@@ -214,7 +224,10 @@ public final class KeyTool {
*/
void parseArgs(String[] args) {
- if (args.length == 0) usage();
+ if (args.length == 0) {
+ usage();
+ return;
+ }
int i=0;
@@ -260,6 +273,10 @@ public final class KeyTool {
command = IMPORTKEYSTORE;
} else if (collator.compare(flags, "-genseckey") == 0) {
command = GENSECKEY;
+ } else if (collator.compare(flags, "-gencert") == 0) {
+ command = GENCERT;
+ } else if (collator.compare(flags, "-printcertreq") == 0) {
+ command = PRINTCERTREQ;
}
/*
@@ -337,9 +354,18 @@ public final class KeyTool {
} else if (collator.compare(flags, "-validity") == 0) {
if (++i == args.length) errorNeedArgument(flags);
validity = Long.parseLong(args[i]);
+ } else if (collator.compare(flags, "-ext") == 0) {
+ if (++i == args.length) errorNeedArgument(flags);
+ v3ext.add(args[i]);
} else if (collator.compare(flags, "-file") == 0) {
if (++i == args.length) errorNeedArgument(flags);
filename = args[i];
+ } else if (collator.compare(flags, "-infile") == 0) {
+ if (++i == args.length) errorNeedArgument(flags);
+ infilename = args[i];
+ } else if (collator.compare(flags, "-outfile") == 0) {
+ if (++i == args.length) errorNeedArgument(flags);
+ outfilename = args[i];
} else if (collator.compare(flags, "-sslserver") == 0) {
if (++i == args.length) errorNeedArgument(flags);
sslserver = args[i];
@@ -364,7 +390,7 @@ public final class KeyTool {
}
}
providers.add(
- new Pair<String, String>(providerClass, providerArg));
+ Pair.of(providerClass, providerArg));
}
/*
@@ -404,6 +430,10 @@ public final class KeyTool {
}
}
+ boolean isKeyStoreRelated(int cmd) {
+ return cmd != PRINTCERT && cmd != PRINTCERTREQ;
+ }
+
/**
* Execute the commands.
*/
@@ -568,7 +598,7 @@ public final class KeyTool {
// the default, which is located in $HOME/.keystore.
// If the command is "genkey", "identitydb", "import", or "printcert",
// it is OK not to have a keystore.
- if (command != PRINTCERT) {
+ if (isKeyStoreRelated(command)) {
if (ksfname == null) {
ksfname = System.getProperty("user.home") + File.separator
+ ".keystore";
@@ -721,7 +751,7 @@ public final class KeyTool {
}
} else if (!protectedPath
&& !KeyStoreUtil.isWindowsKeyStore(storetype)
- && !(command == PRINTCERT)) {
+ && isKeyStoreRelated(command)) {
// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
System.err.print(rb.getString("Enter keystore password: "));
System.err.flush();
@@ -763,7 +793,7 @@ public final class KeyTool {
// Create a certificate factory
if (command == PRINTCERT || command == IMPORTCERT
- || command == IDENTITYDB) {
+ || command == IDENTITYDB) {
cf = CertificateFactory.getInstance("X509");
}
@@ -930,6 +960,41 @@ public final class KeyTool {
storePassNew = getNewPasswd("keystore password", storePass);
}
kssave = true;
+ } else if (command == GENCERT) {
+ if (alias == null) {
+ alias = keyAlias;
+ }
+ InputStream inStream = System.in;
+ if (infilename != null) {
+ inStream = new FileInputStream(infilename);
+ }
+ PrintStream ps = null;
+ if (outfilename != null) {
+ ps = new PrintStream(new FileOutputStream(outfilename));
+ out = ps;
+ }
+ try {
+ doGenCert(alias, sigAlgName, inStream, out);
+ } finally {
+ if (inStream != System.in) {
+ inStream.close();
+ }
+ if (ps != null) {
+ ps.close();
+ }
+ }
+ } else if (command == PRINTCERTREQ) {
+ InputStream inStream = System.in;
+ if (filename != null) {
+ inStream = new FileInputStream(filename);
+ }
+ try {
+ doPrintCertReq(inStream, out);
+ } finally {
+ if (inStream != System.in) {
+ inStream.close();
+ }
+ }
}
// If we need to save the keystore, do so.
@@ -962,6 +1027,91 @@ public final class KeyTool {
}
/**
+ * Generate a certificate: Read PKCS10 request from in, and print
+ * certificate to out. Use alias as CA, sigAlgName as the signature
+ * type.
+ */
+ private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)
+ throws Exception {
+
+
+ Certificate signerCert = keyStore.getCertificate(alias);
+ byte[] encoded = signerCert.getEncoded();
+ X509CertImpl signerCertImpl = new X509CertImpl(encoded);
+ X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
+ X509CertImpl.NAME + "." + X509CertImpl.INFO);
+ X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
+ CertificateSubjectName.DN_NAME);
+
+ Date firstDate = getStartDate(startDate);
+ Date lastDate = new Date();
+ lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);
+ CertificateValidity interval = new CertificateValidity(firstDate,
+ lastDate);
+
+ PrivateKey privateKey = (PrivateKey)recoverKey(alias, storePass, keyPass).fst;
+ if (sigAlgName == null) {
+ sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());
+ }
+ Signature signature = Signature.getInstance(sigAlgName);
+ signature.initSign(privateKey);
+
+ X500Signer signer = new X500Signer(signature, owner);
+
+ X509CertInfo info = new X509CertInfo();
+ info.set(X509CertInfo.VALIDITY, interval);
+ info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber
+ ((int)(firstDate.getTime()/1000)));
+ info.set(X509CertInfo.VERSION,
+ new CertificateVersion(CertificateVersion.V3));
+ info.set(X509CertInfo.ALGORITHM_ID,
+ new CertificateAlgorithmId(signer.getAlgorithmId()));
+ info.set(X509CertInfo.ISSUER,
+ new CertificateIssuerName(signer.getSigner()));
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ boolean canRead = false;
+ StringBuffer sb = new StringBuffer();
+ while (true) {
+ String s = reader.readLine();
+ if (s == null) break;
+ // OpenSSL does not use NEW
+ //if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {
+ if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) {
+ canRead = true;
+ //} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {
+ } else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) {
+ break;
+ } else if (canRead) {
+ sb.append(s);
+ }
+ }
+ byte[] rawReq = new BASE64Decoder().decodeBuffer(new String(sb));
+ PKCS10 req = new PKCS10(rawReq);
+
+ info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
+ info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(req.getSubjectName()));
+ CertificateExtensions reqex = null;
+ Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator();
+ while (attrs.hasNext()) {
+ PKCS10Attribute attr = attrs.next();
+ if (attr.getAttributeId().equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
+ reqex = (CertificateExtensions)attr.getAttributeValue();
+ }
+ }
+ CertificateExtensions ext = createV3Extensions(
+ reqex,
+ null,
+ v3ext,
+ req.getSubjectPublicKeyInfo(),
+ signerCert.getPublicKey());
+ info.set(X509CertInfo.EXTENSIONS, ext);
+ X509CertImpl cert = new X509CertImpl(info);
+ cert.sign(privateKey, sigAlgName);
+ dumpCert(cert, out);
+ }
+
+ /**
* Creates a PKCS#10 cert signing request, corresponding to the
* keys (and name) associated with a given alias.
*/
@@ -972,10 +1122,10 @@ public final class KeyTool {
alias = keyAlias;
}
- Object[] objs = recoverKey(alias, storePass, keyPass);
- PrivateKey privKey = (PrivateKey)objs[0];
+ Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
+ PrivateKey privKey = (PrivateKey)objs.fst;
if (keyPass == null) {
- keyPass = (char[])objs[1];
+ keyPass = objs.snd;
}
Certificate cert = keyStore.getCertificate(alias);
@@ -986,21 +1136,14 @@ public final class KeyTool {
throw new Exception(form.format(source));
}
PKCS10 request = new PKCS10(cert.getPublicKey());
+ CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null);
+ // Attribute name is not significant
+ request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS,
+ new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext));
// Construct an X500Signer object, so that we can sign the request
if (sigAlgName == null) {
- // If no signature algorithm was specified at the command line,
- // we choose one that is compatible with the selected private key
- String keyAlgName = privKey.getAlgorithm();
- if ("DSA".equalsIgnoreCase(keyAlgName)
- || "DSS".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1WithDSA";
- } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1WithRSA";
- } else {
- throw new Exception(rb.getString
- ("Cannot derive signature algorithm"));
- }
+ sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());
}
Signature signature = Signature.getInstance(sigAlgName);
@@ -1153,6 +1296,23 @@ public final class KeyTool {
}
/**
+ * If no signature algorithm was specified at the command line,
+ * we choose one that is compatible with the selected private key
+ */
+ private static String getCompatibleSigAlgName(String keyAlgName)
+ throws Exception {
+ if ("DSA".equalsIgnoreCase(keyAlgName)) {
+ return "SHA1WithDSA";
+ } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
+ return "SHA1WithRSA";
+ } else if ("EC".equalsIgnoreCase(keyAlgName)) {
+ return "SHA1withECDSA";
+ } else {
+ throw new Exception(rb.getString
+ ("Cannot derive signature algorithm"));
+ }
+ }
+ /**
* Creates a new key pair and self-signed certificate.
*/
private void doGenKeyPair(String alias, String dname, String keyAlgName,
@@ -1179,16 +1339,7 @@ public final class KeyTool {
}
if (sigAlgName == null) {
- if ("DSA".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1WithDSA";
- } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1WithRSA";
- } else if ("EC".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1withECDSA";
- } else {
- throw new Exception(rb.getString
- ("Cannot derive signature algorithm"));
- }
+ sigAlgName = getCompatibleSigAlgName(keyAlgName);
}
CertAndKeyGen keypair =
new CertAndKeyGen(keyAlgName, sigAlgName, providerName);
@@ -1225,6 +1376,9 @@ public final class KeyTool {
keyPass = promptForKeyPass(alias, null, storePass);
}
keyStore.setKeyEntry(alias, privKey, keyPass, chain);
+
+ // resign so that -ext are applied.
+ doSelfCert(alias, null, sigAlgName);
}
/**
@@ -1247,9 +1401,9 @@ public final class KeyTool {
throw new Exception(form.format(source));
}
- Object[] objs = recoverEntry(keyStore, orig, storePass, keyPass);
- Entry entry = (Entry)objs[0];
- keyPass = (char[])objs[1];
+ Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass);
+ Entry entry = objs.fst;
+ keyPass = objs.snd;
PasswordProtection pp = null;
@@ -1275,10 +1429,10 @@ public final class KeyTool {
if (alias == null) {
alias = keyAlias;
}
- Object[] objs = recoverKey(alias, storePass, keyPass);
- Key privKey = (Key)objs[0];
+ Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
+ Key privKey = objs.fst;
if (keyPass == null) {
- keyPass = (char[])objs[1];
+ keyPass = objs.snd;
}
if (keyPassNew == null) {
@@ -1629,8 +1783,8 @@ public final class KeyTool {
}
}
- Object[] objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);
- Entry entry = (Entry)objs[0];
+ Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);
+ Entry entry = objs.fst;
PasswordProtection pp = null;
@@ -1640,8 +1794,8 @@ public final class KeyTool {
// so always try to protect with destKeyPass.
if (destKeyPass != null) {
pp = new PasswordProtection(destKeyPass);
- } else if (objs[1] != null) {
- pp = new PasswordProtection((char[])objs[1]);
+ } else if (objs.snd != null) {
+ pp = new PasswordProtection(objs.snd);
}
try {
@@ -1726,9 +1880,50 @@ public final class KeyTool {
}
}
+ private void doPrintCertReq(InputStream in, PrintStream out)
+ throws Exception {
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuffer sb = new StringBuffer();
+ boolean started = false;
+ while (true) {
+ String s = reader.readLine();
+ if (s == null) break;
+ if (!started) {
+ if (s.startsWith("-----")) {
+ started = true;
+ }
+ } else {
+ if (s.startsWith("-----")) {
+ break;
+ }
+ sb.append(s);
+ }
+ }
+ PKCS10 req = new PKCS10(new BASE64Decoder().decodeBuffer(new String(sb)));
+
+ PublicKey pkey = req.getSubjectPublicKeyInfo();
+ out.printf(rb.getString("PKCS #10 Certificate Request (Version 1.0)\n" +
+ "Subject: %s\nPublic Key: %s format %s key\n"),
+ req.getSubjectName(), pkey.getFormat(), pkey.getAlgorithm());
+ for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
+ ObjectIdentifier oid = attr.getAttributeId();
+ if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
+ CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue();
+ printExtensions(rb.getString("Extension Request:"), exts, out);
+ } else {
+ out.println(attr.getAttributeId());
+ out.println(attr.getAttributeValue());
+ }
+ }
+ if (debug) {
+ out.println(req); // Just to see more, say, public key length...
+ }
+ }
+
/**
* Reads a certificate (or certificate chain) and prints its contents in
- * a human readbable format.
+ * a human readable format.
*/
private void printCertFromStream(InputStream in, PrintStream out)
throws Exception
@@ -1840,7 +2035,18 @@ public final class KeyTool {
inStream = new FileInputStream(filename);
}
try {
- printCertFromStream(inStream, out);
+ // Read the full stream before feeding to X509Factory,
+ // otherwise, keytool -gencert | keytool -printcert
+ // might not work properly, since -gencert is slow
+ // and there's no data in the pipe at the beginning.
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ byte[] b = new byte[4096];
+ while (true) {
+ int len = inStream.read(b);
+ if (len < 0) break;
+ bout.write(b, 0, len);
+ }
+ printCertFromStream(new ByteArrayInputStream(bout.toByteArray()), out);
} finally {
if (inStream != System.in) {
inStream.close();
@@ -1859,27 +2065,14 @@ public final class KeyTool {
alias = keyAlias;
}
- Object[] objs = recoverKey(alias, storePass, keyPass);
- PrivateKey privKey = (PrivateKey)objs[0];
+ Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
+ PrivateKey privKey = (PrivateKey)objs.fst;
if (keyPass == null)
- keyPass = (char[])objs[1];
+ keyPass = objs.snd;
// Determine the signature algorithm
if (sigAlgName == null) {
- // If no signature algorithm was specified at the command line,
- // we choose one that is compatible with the selected private key
- String keyAlgName = privKey.getAlgorithm();
- if ("DSA".equalsIgnoreCase(keyAlgName)
- || "DSS".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1WithDSA";
- } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1WithRSA";
- } else if ("EC".equalsIgnoreCase(keyAlgName)) {
- sigAlgName = "SHA1withECDSA";
- } else {
- throw new Exception
- (rb.getString("Cannot derive signature algorithm"));
- }
+ sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());
}
// Get the old certificate
@@ -1943,11 +2136,16 @@ public final class KeyTool {
certInfo.set(CertificateAlgorithmId.NAME + "." +
CertificateAlgorithmId.ALGORITHM, sigAlgid);
- // first upgrade to version 3
-
certInfo.set(X509CertInfo.VERSION,
new CertificateVersion(CertificateVersion.V3));
+ CertificateExtensions ext = createV3Extensions(
+ null,
+ (CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),
+ v3ext,
+ oldCert.getPublicKey(),
+ null);
+ certInfo.set(X509CertInfo.EXTENSIONS, ext);
// Sign the new certificate
newCert = new X509CertImpl(certInfo);
newCert.sign(privKey, sigAlgName);
@@ -1985,10 +2183,10 @@ public final class KeyTool {
alias = keyAlias;
}
- Object[] objs = recoverKey(alias, storePass, keyPass);
- PrivateKey privKey = (PrivateKey)objs[0];
+ Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
+ PrivateKey privKey = (PrivateKey)objs.fst;
if (keyPass == null) {
- keyPass = (char[])objs[1];
+ keyPass = objs.snd;
}
Certificate userCert = keyStore.getCertificate(alias);
@@ -2290,36 +2488,40 @@ public final class KeyTool {
};
out.println(form.format(source));
- int extnum = 0;
if (cert instanceof X509CertImpl) {
X509CertImpl impl = (X509CertImpl)cert;
- if (cert.getCriticalExtensionOIDs() != null) {
- for (String extOID : cert.getCriticalExtensionOIDs()) {
- if (extnum == 0) {
- out.println();
- out.println(rb.getString("Extensions: "));
- out.println();
- }
- out.println("#"+(++extnum)+": "+
- impl.getExtension(new ObjectIdentifier(extOID)));
- }
+ X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME
+ + "." +
+ X509CertImpl.INFO);
+ CertificateExtensions exts = (CertificateExtensions)
+ certInfo.get(X509CertInfo.EXTENSIONS);
+ printExtensions(rb.getString("Extensions: "), exts, out);
+ }
+ }
+
+ private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)
+ throws Exception {
+ int extnum = 0;
+ Iterator<Extension> i1 = exts.getAllExtensions().iterator();
+ Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();
+ while (i1.hasNext() || i2.hasNext()) {
+ Extension ext = i1.hasNext()?i1.next():i2.next();
+ if (extnum == 0) {
+ out.println();
+ out.println(title);
+ out.println();
}
- if (cert.getNonCriticalExtensionOIDs() != null) {
- for (String extOID : cert.getNonCriticalExtensionOIDs()) {
- if (extnum == 0) {
- out.println();
- out.println(rb.getString("Extensions: "));
- out.println();
- }
- Extension ext = impl.getExtension(new ObjectIdentifier(extOID));
- if (ext != null) {
- out.println("#"+(++extnum)+": "+ ext);
- } else {
- out.println("#"+(++extnum)+": "+
- impl.getUnparseableExtension(new ObjectIdentifier(extOID)));
- }
+ out.print("#"+(++extnum)+": "+ ext);
+ if (ext.getClass() == Extension.class) {
+ byte[] v = ext.getExtensionValue();
+ if (v.length == 0) {
+ out.println(rb.getString("(Empty value)"));
+ } else {
+ new sun.misc.HexDumpEncoder().encode(ext.getExtensionValue(), out);
+ out.println();
}
}
+ out.println();
}
}
@@ -2470,7 +2672,7 @@ public final class KeyTool {
* recovered private key, and the 2nd element is the password used to
* recover it.
*/
- private Object[] recoverKey(String alias, char[] storePass,
+ private Pair<Key,char[]> recoverKey(String alias, char[] storePass,
char[] keyPass)
throws Exception
{
@@ -2510,7 +2712,7 @@ public final class KeyTool {
key = keyStore.getKey(alias, keyPass);
}
- return new Object[] {key, keyPass};
+ return Pair.of(key, keyPass);
}
/**
@@ -2520,7 +2722,7 @@ public final class KeyTool {
* recovered entry, and the 2nd element is the password used to
* recover it (null if no password).
*/
- private Object[] recoverEntry(KeyStore ks,
+ private Pair<Entry,char[]> recoverEntry(KeyStore ks,
String alias,
char[] pstore,
char[] pkey) throws Exception {
@@ -2585,7 +2787,7 @@ public final class KeyTool {
}
}
- return new Object[] {entry, pkey};
+ return Pair.of(entry, pkey);
}
/**
* Gets the requested finger print of the certificate.
@@ -3027,6 +3229,443 @@ public final class KeyTool {
}
/**
+ * Match a command (may be abbreviated) with a command set.
+ * @param s the command provided
+ * @param list the legal command set
+ * @return the position of a single match, or -1 if none matched
+ * @throws Exception if s is ambiguous
+ */
+ private static int oneOf(String s, String... list) throws Exception {
+ int[] match = new int[list.length];
+ int nmatch = 0;
+ for (int i = 0; i<list.length; i++) {
+ String one = list[i];
+ if (one.toLowerCase().startsWith(s.toLowerCase())) {
+ match[nmatch++] = i;
+ } else {
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ for (char c: one.toCharArray()) {
+ if (first) {
+ sb.append(c);
+ first = false;
+ } else {
+ if (!Character.isLowerCase(c)) {
+ sb.append(c);
+ }
+ }
+ }
+ if (sb.toString().equalsIgnoreCase(s)) {
+ match[nmatch++] = i;
+ }
+ }
+ }
+ if (nmatch == 0) return -1;
+ if (nmatch == 1) return match[0];
+ StringBuffer sb = new StringBuffer();
+ MessageFormat form = new MessageFormat(rb.getString
+ ("command {0} is ambiguous:"));
+ Object[] source = {s};
+ sb.append(form.format(source) +"\n ");
+ for (int i=0; i<nmatch; i++) {
+ sb.append(" " + list[match[i]]);
+ }
+ throw new Exception(sb.toString());
+ }
+
+ /**
+ * Create a GeneralName object from known types
+ * @param t one of 5 known types
+ * @param v value
+ * @return which one
+ */
+ private GeneralName createGeneralName(String t, String v)
+ throws Exception {
+ GeneralNameInterface gn;
+ int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");
+ if (p < 0) {
+ throw new Exception(rb.getString(
+ "Unrecognized GeneralName type: ") + t);
+ }
+ switch (p) {
+ case 0: gn = new RFC822Name(v); break;
+ case 1: gn = new URIName(v); break;
+ case 2: gn = new DNSName(v); break;
+ case 3: gn = new IPAddressName(v); break;
+ default: gn = new OIDName(v); break; //4
+ }
+ return new GeneralName(gn);
+ }
+
+ private static final String[] extSupported = {
+ "BasicConstraints",
+ "KeyUsage",
+ "ExtendedKeyUsage",
+ "SubjectAlternativeName",
+ "IssuerAlternativeName",
+ "SubjectInfoAccess",
+ "AuthorityInfoAccess",
+ };
+
+ private ObjectIdentifier findOidForExtName(String type)
+ throws Exception {
+ switch (oneOf(type, extSupported)) {
+ case 0: return PKIXExtensions.BasicConstraints_Id;
+ case 1: return PKIXExtensions.KeyUsage_Id;
+ case 2: return PKIXExtensions.ExtendedKeyUsage_Id;
+ case 3: return PKIXExtensions.SubjectAlternativeName_Id;
+ case 4: return PKIXExtensions.IssuerAlternativeName_Id;
+ case 5: return PKIXExtensions.SubjectInfoAccess_Id;
+ case 6: return PKIXExtensions.AuthInfoAccess_Id;
+ default: return new ObjectIdentifier(type);
+ }
+ }
+
+ /**
+ * Create X509v3 extensions from a string representation. Note that the
+ * SubjectKeyIdentifierExtension will always be created non-critical besides
+ * the extension requested in the <code>extstr</code> argument.
+ *
+ * @param reqex the requested extensions, can be null, used for -gencert
+ * @param ext the original extensions, can be null, used for -selfcert
+ * @param extstrs -ext values, Read keytool doc
+ * @param pkey the public key for the certificate
+ * @param akey the public key for the authority (issuer)
+ * @return the created CertificateExtensions
+ */
+ private CertificateExtensions createV3Extensions(
+ CertificateExtensions reqex,
+ CertificateExtensions ext,
+ List <String> extstrs,
+ PublicKey pkey,
+ PublicKey akey) throws Exception {
+
+ if (ext != null && reqex != null) {
+ // This should not happen
+ throw new Exception("One of request and original should be null.");
+ }
+ if (ext == null) ext = new CertificateExtensions();
+ try {
+ // name{:critical}{=value}
+ // Honoring requested extensions
+ if (reqex != null) {
+ for(String extstr: extstrs) {
+ if (extstr.toLowerCase().startsWith("honored=")) {
+ List<String> list = Arrays.asList(
+ extstr.toLowerCase().substring(8).split(","));
+ // First check existence of "all"
+ if (list.contains("all")) {
+ ext = reqex; // we know ext was null
+ }
+ // one by one for others
+ for (String item: list) {
+ if (item.equals("all")) continue;
+
+ // add or remove
+ boolean add = true;
+ // -1, unchanged, 0 crtical, 1 non-critical
+ int action = -1;
+ String type = null;
+ if (item.startsWith("-")) {
+ add = false;
+ type = item.substring(1);
+ } else {
+ int colonpos = item.indexOf(':');
+ if (colonpos >= 0) {
+ type = item.substring(0, colonpos);
+ action = oneOf(item.substring(colonpos+1),
+ "critical", "non-critical");
+ if (action == -1) {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + item);
+ }
+ }
+ }
+ String n = reqex.getNameByOid(findOidForExtName(type));
+ if (add) {
+ Extension e = (Extension)reqex.get(n);
+ if (!e.isCritical() && action == 0
+ || e.isCritical() && action == 1) {
+ e = Extension.newExtension(
+ e.getExtensionId(),
+ !e.isCritical(),
+ e.getExtensionValue());
+ ext.set(n, e);
+ }
+ } else {
+ ext.delete(n);
+ }
+ }
+ break;
+ }
+ }
+ }
+ for(String extstr: extstrs) {
+ String name, value;
+ boolean isCritical = false;
+
+ int eqpos = extstr.indexOf('=');
+ if (eqpos >= 0) {
+ name = extstr.substring(0, eqpos);
+ value = extstr.substring(eqpos+1);
+ } else {
+ name = extstr;
+ value = null;
+ }
+
+ int colonpos = name.indexOf(':');
+ if (colonpos >= 0) {
+ if (name.substring(colonpos+1).equalsIgnoreCase("critical")) {
+ isCritical = true;
+ }
+ name = name.substring(0, colonpos);
+ }
+
+ if (name.equalsIgnoreCase("honored")) {
+ continue;
+ }
+ int exttype = oneOf(name, extSupported);
+ switch (exttype) {
+ case 0: // BC
+ int pathLen = -1;
+ boolean isCA = false;
+ if (value == null) {
+ isCA = true;
+ } else {
+ try { // the abbr format
+ pathLen = Integer.parseInt(value);
+ isCA = true;
+ } catch (NumberFormatException ufe) {
+ // ca:true,pathlen:1
+ for (String part: value.split(",")) {
+ String[] nv = part.split(":");
+ if (nv.length != 2) {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ } else {
+ if (nv[0].equalsIgnoreCase("ca")) {
+ isCA = Boolean.parseBoolean(nv[1]);
+ } else if (nv[0].equalsIgnoreCase("pathlen")) {
+ pathLen = Integer.parseInt(nv[1]);
+ } else {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ }
+ }
+ }
+ }
+ }
+ ext.set(BasicConstraintsExtension.NAME,
+ new BasicConstraintsExtension(isCritical, isCA,
+ pathLen));
+ break;
+ case 1: // KU
+ if(value != null) {
+ boolean[] ok = new boolean[9];
+ for (String s: value.split(",")) {
+ int p = oneOf(s,
+ "digitalSignature", // (0),
+ "nonRepudiation", // (1)
+ "keyEncipherment", // (2),
+ "dataEncipherment", // (3),
+ "keyAgreement", // (4),
+ "keyCertSign", // (5),
+ "cRLSign", // (6),
+ "encipherOnly", // (7),
+ "decipherOnly", // (8)
+ "contentCommitment" // also (1)
+ );
+ if (p < 0) {
+ throw new Exception(rb.getString("Unknown keyUsage type: ") + s);
+ }
+ if (p == 9) p = 1;
+ ok[p] = true;
+ }
+ KeyUsageExtension kue = new KeyUsageExtension(ok);
+ // The above KeyUsageExtension constructor does not
+ // allow isCritical value, so...
+ ext.set(KeyUsageExtension.NAME, Extension.newExtension(
+ kue.getExtensionId(),
+ isCritical,
+ kue.getExtensionValue()));
+ } else {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ }
+ break;
+ case 2: // EKU
+ if(value != null) {
+ Vector <ObjectIdentifier> v =
+ new Vector <ObjectIdentifier>();
+ for (String s: value.split(",")) {
+ int p = oneOf(s,
+ "anyExtendedKeyUsage",
+ "serverAuth", //1
+ "clientAuth", //2
+ "codeSigning", //3
+ "emailProtection", //4
+ "", //5
+ "", //6
+ "", //7
+ "timeStamping", //8
+ "OCSPSigning" //9
+ );
+ if (p < 0) {
+ try {
+ v.add(new ObjectIdentifier(s));
+ } catch (Exception e) {
+ throw new Exception(rb.getString(
+ "Unknown extendedkeyUsage type: ") + s);
+ }
+ } else if (p == 0) {
+ v.add(new ObjectIdentifier("2.5.29.37.0"));
+ } else {
+ v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p));
+ }
+ }
+ ext.set(ExtendedKeyUsageExtension.NAME,
+ new ExtendedKeyUsageExtension(isCritical, v));
+ } else {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ }
+ break;
+ case 3: // SAN
+ case 4: // IAN
+ if(value != null) {
+ String[] ps = value.split(",");
+ GeneralNames gnames = new GeneralNames();
+ for(String item: ps) {
+ colonpos = item.indexOf(':');
+ if (colonpos < 0) {
+ throw new Exception("Illegal item " + item + " in " + extstr);
+ }
+ String t = item.substring(0, colonpos);
+ String v = item.substring(colonpos+1);
+ gnames.add(createGeneralName(t, v));
+ }
+ if (exttype == 3) {
+ ext.set(SubjectAlternativeNameExtension.NAME,
+ new SubjectAlternativeNameExtension(
+ isCritical, gnames));
+ } else {
+ ext.set(IssuerAlternativeNameExtension.NAME,
+ new IssuerAlternativeNameExtension(
+ isCritical, gnames));
+ }
+ } else {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ }
+ break;
+ case 5: // SIA, always non-critical
+ case 6: // AIA, always non-critical
+ if (isCritical) {
+ throw new Exception(rb.getString(
+ "This extension cannot be marked as critical. ") + extstr);
+ }
+ if(value != null) {
+ List<AccessDescription> accessDescriptions =
+ new ArrayList<AccessDescription>();
+ String[] ps = value.split(",");
+ for(String item: ps) {
+ colonpos = item.indexOf(':');
+ int colonpos2 = item.indexOf(':', colonpos+1);
+ if (colonpos < 0 || colonpos2 < 0) {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ }
+ String m = item.substring(0, colonpos);
+ String t = item.substring(colonpos+1, colonpos2);
+ String v = item.substring(colonpos2+1);
+ int p = oneOf(m,
+ "",
+ "ocsp", //1
+ "caIssuers", //2
+ "timeStamping", //3
+ "",
+ "caRepository" //5
+ );
+ ObjectIdentifier oid;
+ if (p < 0) {
+ try {
+ oid = new ObjectIdentifier(m);
+ } catch (Exception e) {
+ throw new Exception(rb.getString(
+ "Unknown AccessDescription type: ") + m);
+ }
+ } else {
+ oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);
+ }
+ accessDescriptions.add(new AccessDescription(
+ oid, createGeneralName(t, v)));
+ }
+ if (exttype == 5) {
+ ext.set(SubjectInfoAccessExtension.NAME,
+ new SubjectInfoAccessExtension(accessDescriptions));
+ } else {
+ ext.set(AuthorityInfoAccessExtension.NAME,
+ new AuthorityInfoAccessExtension(accessDescriptions));
+ }
+ } else {
+ throw new Exception(rb.getString
+ ("Illegal value: ") + extstr);
+ }
+ break;
+ case -1:
+ ObjectIdentifier oid = new ObjectIdentifier(name);
+ byte[] data = null;
+ if (value != null) {
+ data = new byte[value.length() / 2 + 1];
+ int pos = 0;
+ for (char c: value.toCharArray()) {
+ int hex;
+ if (c >= '0' && c <= '9') {
+ hex = c - '0' ;
+ } else if (c >= 'A' && c <= 'F') {
+ hex = c - 'A' + 10;
+ } else if (c >= 'a' && c <= 'f') {
+ hex = c - 'a' + 10;
+ } else {
+ continue;
+ }
+ if (pos % 2 == 0) {
+ data[pos/2] = (byte)(hex << 4);
+ } else {
+ data[pos/2] += hex;
+ }
+ pos++;
+ }
+ if (pos % 2 != 0) {
+ throw new Exception(rb.getString(
+ "Odd number of hex digits found: ") + extstr);
+ }
+ data = Arrays.copyOf(data, pos/2);
+ } else {
+ data = new byte[0];
+ }
+ ext.set(oid.toString(), new Extension(oid, isCritical,
+ new DerValue(DerValue.tag_OctetString, data)
+ .toByteArray()));
+ break;
+ }
+ }
+ // always non-critical
+ ext.set(SubjectKeyIdentifierExtension.NAME,
+ new SubjectKeyIdentifierExtension(
+ new KeyIdentifier(pkey).getIdentifier()));
+ if (akey != null && !pkey.equals(akey)) {
+ ext.set(AuthorityKeyIdentifierExtension.NAME,
+ new AuthorityKeyIdentifierExtension(
+ new KeyIdentifier(akey), null, null));
+ }
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ }
+ return ext;
+ }
+
+ /**
* Prints the usage of this tool.
*/
private void usage() {
@@ -3099,6 +3738,32 @@ public final class KeyTool {
System.err.println(rb.getString
("\t [-startdate <startdate>]"));
System.err.println(rb.getString
+ ("\t [-ext <key>[:critical][=<value>]]..."));
+ System.err.println(rb.getString
+ ("\t [-validity <valDays>] [-keypass <keypass>]"));
+ System.err.println(rb.getString
+ ("\t [-keystore <keystore>] [-storepass <storepass>]"));
+ System.err.println(rb.getString
+ ("\t [-storetype <storetype>] [-providername <name>]"));
+ System.err.println(rb.getString
+ ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
+ System.err.println(rb.getString
+ ("\t [-providerpath <pathlist>]"));
+ System.err.println();
+
+ System.err.println(rb.getString
+ ("-gencert [-v] [-rfc] [-protected]"));
+ System.err.println(rb.getString
+ ("\t [-infile <infile>] [-outfile <outfile>]"));
+ System.err.println(rb.getString
+ ("\t [-alias <alias>]"));
+ System.err.println(rb.getString
+ ("\t [-sigalg <sigalg>]"));
+ System.err.println(rb.getString
+ ("\t [-startdate <startdate>]"));
+ System.err.println(rb.getString
+ ("\t [-ext <key>[:critical][=<value>]]..."));
+ System.err.println(rb.getString
("\t [-validity <valDays>] [-keypass <keypass>]"));
System.err.println(rb.getString
("\t [-keystore <keystore>] [-storepass <storepass>]"));
@@ -3202,6 +3867,10 @@ public final class KeyTool {
System.err.println();
System.err.println(rb.getString
+ ("-printcertreq [-v] [-file <cert_file>]"));
+ System.err.println();
+
+ System.err.println(rb.getString
("-storepasswd [-v] [-new <new_storepass>]"));
System.err.println(rb.getString
("\t [-keystore <keystore>] [-storepass <storepass>]"));
@@ -3211,12 +3880,6 @@ public final class KeyTool {
("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
System.err.println(rb.getString
("\t [-providerpath <pathlist>]"));
-
- if (debug) {
- throw new RuntimeException("NO ERROR, SORRY");
- } else {
- System.exit(1);
- }
}
private void tinyHelp() {
@@ -3270,4 +3933,8 @@ class Pair<A, B> {
else if (snd == null) return fst.hashCode() + 2;
else return fst.hashCode() * 17 + snd.hashCode();
}
+
+ public static <A,B> Pair<A,B> of(A a, B b) {
+ return new Pair<A,B>(a,b);
+ }
}
diff --git a/src/share/classes/sun/security/util/Cache.java b/src/share/classes/sun/security/util/Cache.java
index e6eddb6f0..35d648a5a 100644
--- a/src/share/classes/sun/security/util/Cache.java
+++ b/src/share/classes/sun/security/util/Cache.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2009 Sun Microsystems, Inc. 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
@@ -101,6 +101,21 @@ public abstract class Cache {
public abstract void remove(Object key);
/**
+ * Set the maximum size.
+ */
+ public abstract void setCapacity(int size);
+
+ /**
+ * Set the timeout(in seconds).
+ */
+ public abstract void setTimeout(int timeout);
+
+ /**
+ * accept a visitor
+ */
+ public abstract void accept(CacheVisitor visitor);
+
+ /**
* Return a new memory cache with the specified maximum size, unlimited
* lifetime for entries, with the values held by SoftReferences.
*/
@@ -178,6 +193,10 @@ public abstract class Cache {
}
}
+ public interface CacheVisitor {
+ public void visit(Map<Object, Object> map);
+ }
+
}
class NullCache extends Cache {
@@ -208,6 +227,18 @@ class NullCache extends Cache {
// empty
}
+ public void setCapacity(int size) {
+ // empty
+ }
+
+ public void setTimeout(int timeout) {
+ // empty
+ }
+
+ public void accept(CacheVisitor visitor) {
+ // empty
+ }
+
}
class MemoryCache extends Cache {
@@ -218,8 +249,8 @@ class MemoryCache extends Cache {
private final static boolean DEBUG = false;
private final Map<Object, CacheEntry> cacheMap;
- private final int maxSize;
- private final int lifetime;
+ private int maxSize;
+ private long lifetime;
private final ReferenceQueue queue;
public MemoryCache(boolean soft, int maxSize) {
@@ -328,7 +359,7 @@ class MemoryCache extends Cache {
oldEntry.invalidate();
return;
}
- if (cacheMap.size() > maxSize) {
+ if (maxSize > 0 && cacheMap.size() > maxSize) {
expungeExpiredEntries();
if (cacheMap.size() > maxSize) { // still too large?
Iterator<CacheEntry> t = cacheMap.values().iterator();
@@ -368,6 +399,55 @@ class MemoryCache extends Cache {
}
}
+ public synchronized void setCapacity(int size) {
+ expungeExpiredEntries();
+ if (size > 0 && cacheMap.size() > size) {
+ Iterator<CacheEntry> t = cacheMap.values().iterator();
+ for (int i = cacheMap.size() - size; i > 0; i--) {
+ CacheEntry lruEntry = t.next();
+ if (DEBUG) {
+ System.out.println("** capacity reset removal "
+ + lruEntry.getKey() + " | " + lruEntry.getValue());
+ }
+ t.remove();
+ lruEntry.invalidate();
+ }
+ }
+
+ maxSize = size > 0 ? size : 0;
+
+ if (DEBUG) {
+ System.out.println("** capacity reset to " + size);
+ }
+ }
+
+ public synchronized void setTimeout(int timeout) {
+ emptyQueue();
+ lifetime = timeout > 0 ? timeout * 1000L : 0L;
+
+ if (DEBUG) {
+ System.out.println("** lifetime reset to " + timeout);
+ }
+ }
+
+ // it is a heavyweight method.
+ public synchronized void accept(CacheVisitor visitor) {
+ expungeExpiredEntries();
+ Map<Object, Object> cached = getCachedEntries();
+
+ visitor.visit(cached);
+ }
+
+ private Map<Object, Object> getCachedEntries() {
+ Map<Object,Object> kvmap = new HashMap<Object,Object>(cacheMap.size());
+
+ for (CacheEntry entry : cacheMap.values()) {
+ kvmap.put(entry.getKey(), entry.getValue());
+ }
+
+ return kvmap;
+ }
+
protected CacheEntry newEntry(Object key, Object value,
long expirationTime, ReferenceQueue queue) {
if (queue != null) {
diff --git a/src/share/classes/sun/security/util/DerValue.java b/src/share/classes/sun/security/util/DerValue.java
index 74e59f609..114788beb 100644
--- a/src/share/classes/sun/security/util/DerValue.java
+++ b/src/share/classes/sun/security/util/DerValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -65,7 +65,7 @@ public class DerValue {
protected DerInputBuffer buffer;
/**
- * The DER-encoded data of the value.
+ * The DER-encoded data of the value, never null
*/
public final DerInputStream data;
@@ -378,8 +378,6 @@ public class DerValue {
("Indefinite length encoding not supported");
length = DerInputStream.getLength(in);
}
- if (length == 0)
- return null;
if (fullyBuffered && in.available() != length)
throw new IOException("extra data given to DerValue constructor");
@@ -477,6 +475,11 @@ public class DerValue {
"DerValue.getOctetString, not an Octet String: " + tag);
}
bytes = new byte[length];
+ // Note: do not tempt to call buffer.read(bytes) at all. There's a
+ // known bug that it returns -1 instead of 0.
+ if (length == 0) {
+ return bytes;
+ }
if (buffer.read(bytes) != length)
throw new IOException("short read on DerValue buffer");
if (isConstructed()) {
diff --git a/src/share/classes/sun/security/util/Resources.java b/src/share/classes/sun/security/util/Resources.java
index e89d6cdfc..c8c5e386d 100644
--- a/src/share/classes/sun/security/util/Resources.java
+++ b/src/share/classes/sun/security/util/Resources.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -49,6 +49,7 @@ public class Resources extends java.util.ListResourceBundle {
// keytool
{"keytool error: ", "keytool error: "},
{"Illegal option: ", "Illegal option: "},
+ {"Illegal value: ", "Illegal value: "},
{"Try keytool -help","Try keytool -help"},
{"Command option <flag> needs an argument.", "Command option {0} needs an argument."},
{"Warning: Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified <command> value.",
@@ -281,6 +282,20 @@ public class Resources extends java.util.ListResourceBundle {
{"keytool usage:\n", "keytool usage:\n"},
{"Extensions: ", "Extensions: "},
+ {"(Empty value)", "(Empty value)"},
+ {"Extension Request:", "Extension Request:"},
+ {"PKCS #10 Certificate Request (Version 1.0)\n" +
+ "Subject: %s\nPublic Key: %s format %s key\n",
+ "PKCS #10 Certificate Request (Version 1.0)\n" +
+ "Subject: %s\nPublic Key: %s format %s key\n"},
+ {"Unknown keyUsage type: ", "Unknown keyUsage type: "},
+ {"Unknown extendedkeyUsage type: ", "Unknown extendedkeyUsage type: "},
+ {"Unknown AccessDescription type: ", "Unknown AccessDescription type: "},
+ {"Unrecognized GeneralName type: ", "Unrecognized GeneralName type: "},
+ {"This extension cannot be marked as critical. ",
+ "This extension cannot be marked as critical. "},
+ {"Odd number of hex digits found: ", "Odd number of hex digits found: "},
+ {"command {0} is ambiguous:", "command {0} is ambiguous:"},
{"-certreq [-v] [-protected]",
"-certreq [-v] [-protected]"},
@@ -322,6 +337,14 @@ public class Resources extends java.util.ListResourceBundle {
{"\t [-validity <valDays>] [-keypass <keypass>]",
"\t [-validity <valDays>] [-keypass <keypass>]"},
/** rest is same as -certreq starting from -keystore **/
+ {"-gencert [-v] [-rfc] [-protected]",
+ "-gencert [-v] [-rfc] [-protected]"},
+ {"\t [-infile <infile>] [-outfile <outfile>]",
+ "\t [-infile <infile>] [-outfile <outfile>]"},
+ {"\t [-sigalg <sigalg>]",
+ "\t [-sigalg <sigalg>]"},
+ {"\t [-ext <key>[:critical][=<value>]]...",
+ "\t [-ext <key>[:critical][=<value>]]..."},
{"-genseckey [-v] [-protected]",
"-genseckey [-v] [-protected]"},
@@ -388,6 +411,8 @@ public class Resources extends java.util.ListResourceBundle {
{"-printcert [-v] [-rfc] [-file <cert_file> | -sslserver <host[:port]>]",
"-printcert [-v] [-rfc] [-file <cert_file> | -sslserver <host[:port]>]"},
+ {"-printcertreq [-v] [-file <cert_file>]",
+ "-printcertreq [-v] [-file <cert_file>]"},
{"No certificate from the SSL server",
"No certificate from the SSL server"},
diff --git a/src/share/classes/sun/security/util/SecurityConstants.java b/src/share/classes/sun/security/util/SecurityConstants.java
index 95cd1f570..43e2cd3d7 100644
--- a/src/share/classes/sun/security/util/SecurityConstants.java
+++ b/src/share/classes/sun/security/util/SecurityConstants.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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
@@ -52,6 +52,7 @@ public final class SecurityConstants {
public static final String FILE_EXECUTE_ACTION = "execute";
public static final String FILE_READ_ACTION = "read";
public static final String FILE_WRITE_ACTION = "write";
+ public static final String FILE_READLINK_ACTION = "readlink";
public static final String SOCKET_RESOLVE_ACTION = "resolve";
public static final String SOCKET_CONNECT_ACTION = "connect";
diff --git a/src/share/classes/sun/security/x509/AccessDescription.java b/src/share/classes/sun/security/x509/AccessDescription.java
index a9ccbb868..1544ea953 100644
--- a/src/share/classes/sun/security/x509/AccessDescription.java
+++ b/src/share/classes/sun/security/x509/AccessDescription.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2009 Sun Microsystems, Inc. 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
@@ -48,6 +48,17 @@ public final class AccessDescription {
public static final ObjectIdentifier Ad_CAISSUERS_Id =
ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 2});
+ public static final ObjectIdentifier Ad_TIMESTAMPING_Id =
+ ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 3});
+
+ public static final ObjectIdentifier Ad_CAREPOSITORY_Id =
+ ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 5});
+
+ public AccessDescription(ObjectIdentifier accessMethod, GeneralName accessLocation) {
+ this.accessMethod = accessMethod;
+ this.accessLocation = accessLocation;
+ }
+
public AccessDescription(DerValue derValue) throws IOException {
DerInputStream derIn = derValue.getData();
accessMethod = derIn.getOID();
@@ -90,7 +101,19 @@ public final class AccessDescription {
}
public String toString() {
- return ("accessMethod: " + accessMethod.toString() +
- "\n accessLocation: " + accessLocation.toString());
+ String method = null;
+ if (accessMethod.equals(Ad_CAISSUERS_Id)) {
+ method = "caIssuers";
+ } else if (accessMethod.equals(Ad_CAREPOSITORY_Id)) {
+ method = "caRepository";
+ } else if (accessMethod.equals(Ad_TIMESTAMPING_Id)) {
+ method = "timeStamping";
+ } else if (accessMethod.equals(Ad_OCSP_Id)) {
+ method = "ocsp";
+ } else {
+ method = accessMethod.toString();
+ }
+ return ("accessMethod: " + method +
+ "\n accessLocation: " + accessLocation.toString() + "\n");
}
}
diff --git a/src/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java b/src/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java
index 5b0b287fa..da14007cd 100644
--- a/src/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java
+++ b/src/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java
@@ -43,8 +43,9 @@ import sun.security.util.DerValue;
* certificate that identifies the specific OCSP Responder to use when
* performing on-line validation of that certificate.
* <p>
- * This extension is defined in
- * <a href="http://www.ietf.org/rfc/rfc3280.txt">Internet X.509 PKI Certificate and Certificate Revocation List (CRL) Profile</a>. The profile permits
+ * This extension is defined in <a href="http://www.ietf.org/rfc/rfc3280.txt">
+ * Internet X.509 PKI Certificate and Certificate Revocation List
+ * (CRL) Profile</a>. The profile permits
* the extension to be included in end-entity or CA certificates,
* and it must be marked as non-critical. Its ASN.1 definition is as follows:
* <pre>
diff --git a/src/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java b/src/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java
index f63dca90a..b14c84362 100644
--- a/src/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java
+++ b/src/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -198,7 +198,7 @@ implements CertAttrSet<String> {
public String toString() {
String s = super.toString() + "AuthorityKeyIdentifier [\n";
if (id != null) {
- s += id.toString() + "\n";
+ s += id.toString(); // id already has a newline
}
if (names != null) {
s += names.toString() + "\n";
diff --git a/src/share/classes/sun/security/x509/BasicConstraintsExtension.java b/src/share/classes/sun/security/x509/BasicConstraintsExtension.java
index 26344ee27..c38bb002b 100644
--- a/src/share/classes/sun/security/x509/BasicConstraintsExtension.java
+++ b/src/share/classes/sun/security/x509/BasicConstraintsExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -70,18 +70,15 @@ implements CertAttrSet<String> {
// Encode this extension value
private void encodeThis() throws IOException {
- if (ca == false && pathLen < 0) {
- this.extensionValue = null;
- return;
- }
DerOutputStream out = new DerOutputStream();
DerOutputStream tmp = new DerOutputStream();
if (ca) {
tmp.putBoolean(ca);
- }
- if (pathLen >= 0) {
- tmp.putInteger(pathLen);
+ // Only encode pathLen when ca == true
+ if (pathLen >= 0) {
+ tmp.putInteger(pathLen);
+ }
}
out.write(DerValue.tag_Sequence, tmp);
this.extensionValue = out.toByteArray();
@@ -134,7 +131,7 @@ implements CertAttrSet<String> {
throw new IOException("Invalid encoding of BasicConstraints");
}
- if (val.data == null) {
+ if (val.data == null || val.data.available() == 0) {
// non-CA cert ("cA" field is FALSE by default), return -1
return;
}
diff --git a/src/share/classes/sun/security/x509/CertAndKeyGen.java b/src/share/classes/sun/security/x509/CertAndKeyGen.java
index 0e0800910..38ca60092 100644
--- a/src/share/classes/sun/security/x509/CertAndKeyGen.java
+++ b/src/share/classes/sun/security/x509/CertAndKeyGen.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. 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
@@ -276,12 +276,6 @@ public final class CertAndKeyGen {
info.set(X509CertInfo.ISSUER,
new CertificateIssuerName(issuer.getSigner()));
- CertificateExtensions ext = new CertificateExtensions();
- ext.set(SubjectKeyIdentifierExtension.NAME,
- new SubjectKeyIdentifierExtension(
- new KeyIdentifier(publicKey).getIdentifier()));
- info.set(X509CertInfo.EXTENSIONS, ext);
-
cert = new X509CertImpl(info);
cert.sign(privateKey, this.sigAlg);
diff --git a/src/share/classes/sun/security/x509/CertificateExtensions.java b/src/share/classes/sun/security/x509/CertificateExtensions.java
index 7049d01e9..cd32e748c 100644
--- a/src/share/classes/sun/security/x509/CertificateExtensions.java
+++ b/src/share/classes/sun/security/x509/CertificateExtensions.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -231,6 +231,15 @@ public class CertificateExtensions implements CertAttrSet<Extension> {
map.remove(name);
}
+ public String getNameByOid(ObjectIdentifier oid) throws IOException {
+ for (String name: map.keySet()) {
+ if (map.get(name).getExtensionId().equals(oid)) {
+ return name;
+ }
+ }
+ return null;
+ }
+
/**
* Return an enumeration of names of attributes existing within this
* attribute.
diff --git a/src/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java b/src/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java
index aad153b64..73f3ecd5c 100644
--- a/src/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java
+++ b/src/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -90,6 +90,22 @@ extends Extension implements CertAttrSet<String> {
}
/**
+ * Create a IssuerAlternativeNameExtension with the passed criticality
+ * and GeneralNames.
+ *
+ * @param critical true if the extension is to be treated as critical.
+ * @param names the GeneralNames for the issuer.
+ * @exception IOException on error.
+ */
+ public IssuerAlternativeNameExtension(Boolean critical, GeneralNames names)
+ throws IOException {
+ this.names = names;
+ this.extensionId = PKIXExtensions.IssuerAlternativeName_Id;
+ this.critical = critical.booleanValue();
+ encodeThis();
+ }
+
+ /**
* Create a default IssuerAlternativeNameExtension.
*/
public IssuerAlternativeNameExtension() {
diff --git a/src/share/classes/sun/security/x509/OIDMap.java b/src/share/classes/sun/security/x509/OIDMap.java
index ddbb1d6f6..bb7cffef3 100644
--- a/src/share/classes/sun/security/x509/OIDMap.java
+++ b/src/share/classes/sun/security/x509/OIDMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc. 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
@@ -90,6 +90,8 @@ public class OIDMap {
private static final String CERT_ISSUER = ROOT + "." +
CertificateIssuerExtension.NAME;
+ private static final String SUBJECT_INFO_ACCESS = ROOT + "." +
+ SubjectInfoAccessExtension.NAME;
private static final String AUTH_INFO_ACCESS = ROOT + "." +
AuthorityInfoAccessExtension.NAME;
private static final String ISSUING_DIST_POINT = ROOT + "." +
@@ -148,6 +150,8 @@ public class OIDMap {
"sun.security.x509.CRLDistributionPointsExtension");
addInternal(CERT_ISSUER, PKIXExtensions.CertificateIssuer_Id,
"sun.security.x509.CertificateIssuerExtension");
+ addInternal(SUBJECT_INFO_ACCESS, PKIXExtensions.SubjectInfoAccess_Id,
+ "sun.security.x509.SubjectInfoAccessExtension");
addInternal(AUTH_INFO_ACCESS, PKIXExtensions.AuthInfoAccess_Id,
"sun.security.x509.AuthorityInfoAccessExtension");
addInternal(ISSUING_DIST_POINT,
diff --git a/src/share/classes/sun/security/x509/SubjectInfoAccessExtension.java b/src/share/classes/sun/security/x509/SubjectInfoAccessExtension.java
new file mode 100644
index 000000000..449f45eb5
--- /dev/null
+++ b/src/share/classes/sun/security/x509/SubjectInfoAccessExtension.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.x509;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.*;
+
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+
+/**
+ * The Subject Information Access Extension (OID = 1.3.6.1.5.5.7.1.11).
+ * <p>
+ * The subject information access extension indicates how to access
+ * information and services for the subject of the certificate in which
+ * the extension appears. When the subject is a CA, information and
+ * services may include certificate validation services and CA policy
+ * data. When the subject is an end entity, the information describes
+ * the type of services offered and how to access them. In this case,
+ * the contents of this extension are defined in the protocol
+ * specifications for the supported services. This extension may be
+ * included in end entity or CA certificates. Conforming CAs MUST mark
+ * this extension as non-critical.
+ * <p>
+ * This extension is defined in <a href="http://www.ietf.org/rfc/rfc3280.txt">
+ * Internet X.509 PKI Certificate and Certificate Revocation List
+ * (CRL) Profile</a>. The profile permits
+ * the extension to be included in end-entity or CA certificates,
+ * and it must be marked as non-critical. Its ASN.1 definition is as follows:
+ * <pre>
+ * id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 }
+ *
+ * SubjectInfoAccessSyntax ::=
+ * SEQUENCE SIZE (1..MAX) OF AccessDescription
+ *
+ * AccessDescription ::= SEQUENCE {
+ * accessMethod OBJECT IDENTIFIER,
+ * accessLocation GeneralName }
+ * </pre>
+ * <p>
+ * @see Extension
+ * @see CertAttrSet
+ */
+
+public class SubjectInfoAccessExtension extends Extension
+ implements CertAttrSet<String> {
+
+ /**
+ * Identifier for this attribute, to be used with the
+ * get, set, delete methods of Certificate, x509 type.
+ */
+ public static final String IDENT =
+ "x509.info.extensions.SubjectInfoAccess";
+
+ /**
+ * Attribute name.
+ */
+ public static final String NAME = "SubjectInfoAccess";
+ public static final String DESCRIPTIONS = "descriptions";
+
+ /**
+ * The List of AccessDescription objects.
+ */
+ private List<AccessDescription> accessDescriptions;
+
+ /**
+ * Create an SubjectInfoAccessExtension from a List of
+ * AccessDescription; the criticality is set to false.
+ *
+ * @param accessDescriptions the List of AccessDescription
+ * @throws IOException on error
+ */
+ public SubjectInfoAccessExtension(
+ List<AccessDescription> accessDescriptions) throws IOException {
+ this.extensionId = PKIXExtensions.SubjectInfoAccess_Id;
+ this.critical = false;
+ this.accessDescriptions = accessDescriptions;
+ encodeThis();
+ }
+
+ /**
+ * Create the extension from the passed DER encoded value of the same.
+ *
+ * @param critical true if the extension is to be treated as critical.
+ * @param value Array of DER encoded bytes of the actual value.
+ * @exception IOException on error.
+ */
+ public SubjectInfoAccessExtension(Boolean critical, Object value)
+ throws IOException {
+ this.extensionId = PKIXExtensions.SubjectInfoAccess_Id;
+ this.critical = critical.booleanValue();
+
+ if (!(value instanceof byte[])) {
+ throw new IOException("Illegal argument type");
+ }
+
+ extensionValue = (byte[])value;
+ DerValue val = new DerValue(extensionValue);
+ if (val.tag != DerValue.tag_Sequence) {
+ throw new IOException("Invalid encoding for " +
+ "SubjectInfoAccessExtension.");
+ }
+ accessDescriptions = new ArrayList<AccessDescription>();
+ while (val.data.available() != 0) {
+ DerValue seq = val.data.getDerValue();
+ AccessDescription accessDescription = new AccessDescription(seq);
+ accessDescriptions.add(accessDescription);
+ }
+ }
+
+ /**
+ * Return the list of AccessDescription objects.
+ */
+ public List<AccessDescription> getAccessDescriptions() {
+ return accessDescriptions;
+ }
+
+ /**
+ * Return the name of this attribute.
+ */
+ public String getName() {
+ return NAME;
+ }
+
+ /**
+ * Write the extension to the DerOutputStream.
+ *
+ * @param out the DerOutputStream to write the extension to.
+ * @exception IOException on encoding errors.
+ */
+ public void encode(OutputStream out) throws IOException {
+ DerOutputStream tmp = new DerOutputStream();
+ if (this.extensionValue == null) {
+ this.extensionId = PKIXExtensions.SubjectInfoAccess_Id;
+ this.critical = false;
+ encodeThis();
+ }
+ super.encode(tmp);
+ out.write(tmp.toByteArray());
+ }
+
+ /**
+ * Set the attribute value.
+ */
+ public void set(String name, Object obj) throws IOException {
+ if (name.equalsIgnoreCase(DESCRIPTIONS)) {
+ if (!(obj instanceof List)) {
+ throw new IOException("Attribute value should be of type List.");
+ }
+ accessDescriptions = (List<AccessDescription>)obj;
+ } else {
+ throw new IOException("Attribute name [" + name +
+ "] not recognized by " +
+ "CertAttrSet:SubjectInfoAccessExtension.");
+ }
+ encodeThis();
+ }
+
+ /**
+ * Get the attribute value.
+ */
+ public Object get(String name) throws IOException {
+ if (name.equalsIgnoreCase(DESCRIPTIONS)) {
+ return accessDescriptions;
+ } else {
+ throw new IOException("Attribute name [" + name +
+ "] not recognized by " +
+ "CertAttrSet:SubjectInfoAccessExtension.");
+ }
+ }
+
+ /**
+ * Delete the attribute value.
+ */
+ public void delete(String name) throws IOException {
+ if (name.equalsIgnoreCase(DESCRIPTIONS)) {
+ accessDescriptions = new ArrayList<AccessDescription>();
+ } else {
+ throw new IOException("Attribute name [" + name +
+ "] not recognized by " +
+ "CertAttrSet:SubjectInfoAccessExtension.");
+ }
+ encodeThis();
+ }
+
+ /**
+ * Return an enumeration of names of attributes existing within this
+ * attribute.
+ */
+ public Enumeration<String> getElements() {
+ AttributeNameEnumeration elements = new AttributeNameEnumeration();
+ elements.addElement(DESCRIPTIONS);
+ return elements.elements();
+ }
+
+ // Encode this extension value
+ private void encodeThis() throws IOException {
+ if (accessDescriptions.isEmpty()) {
+ this.extensionValue = null;
+ } else {
+ DerOutputStream ads = new DerOutputStream();
+ for (AccessDescription accessDescription : accessDescriptions) {
+ accessDescription.encode(ads);
+ }
+ DerOutputStream seq = new DerOutputStream();
+ seq.write(DerValue.tag_Sequence, ads);
+ this.extensionValue = seq.toByteArray();
+ }
+ }
+
+ /**
+ * Return the extension as user readable string.
+ */
+ public String toString() {
+ return super.toString() + "SubjectInfoAccess [\n "
+ + accessDescriptions + "\n]\n";
+ }
+
+}
diff --git a/src/share/classes/sun/swing/FilePane.java b/src/share/classes/sun/swing/FilePane.java
index 298fd42b1..de5ad7c48 100644
--- a/src/share/classes/sun/swing/FilePane.java
+++ b/src/share/classes/sun/swing/FilePane.java
@@ -689,7 +689,7 @@ public class FilePane extends JPanel implements PropertyChangeListener {
void updateColumnInfo() {
File dir = chooser.getCurrentDirectory();
- if (dir != null && fileChooserUIAccessor.usesShellFolder()) {
+ if (dir != null && usesShellFolder(chooser)) {
try {
dir = ShellFolder.getShellFolder(dir);
} catch (FileNotFoundException e) {
@@ -1947,7 +1947,7 @@ public class FilePane extends JPanel implements PropertyChangeListener {
if (f instanceof ShellFolder) {
return ((ShellFolder) f).isFileSystem();
} else {
- if (fileChooserUIAccessor.usesShellFolder()) {
+ if (usesShellFolder(getFileChooser())) {
try {
return ShellFolder.getShellFolder(f).isFileSystem();
} catch (FileNotFoundException ex) {
@@ -1961,6 +1961,16 @@ public class FilePane extends JPanel implements PropertyChangeListener {
}
}
+ /**
+ * Returns true if specified FileChooser should use ShellFolder
+ */
+ public static boolean usesShellFolder(JFileChooser chooser) {
+ Boolean prop = (Boolean) chooser.getClientProperty("FileChooser.useShellFolder");
+
+ return prop == null ? chooser.getFileSystemView().equals(FileSystemView.getFileSystemView())
+ : prop.booleanValue();
+ }
+
// This interface is used to access methods in the FileChooserUI
// that are not public.
public interface FileChooserUIAccessor {
@@ -1975,6 +1985,5 @@ public class FilePane extends JPanel implements PropertyChangeListener {
public Action getNewFolderAction();
public MouseListener createDoubleClickListener(JList list);
public ListSelectionListener createListSelectionListener();
- public boolean usesShellFolder();
}
}
diff --git a/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java b/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java
index 95fac3338..5ded9d729 100644
--- a/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java
+++ b/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java
@@ -71,8 +71,6 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI {
private JToggleButton listViewButton;
private JToggleButton detailsViewButton;
- private boolean useShellFolder;
-
private boolean readOnly;
private JPanel buttonPanel;
@@ -185,10 +183,6 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI {
public ListSelectionListener createListSelectionListener() {
return SynthFileChooserUIImpl.this.createListSelectionListener(getFileChooser());
}
-
- public boolean usesShellFolder() {
- return useShellFolder;
- }
}
protected void installDefaults(JFileChooser fc) {
@@ -201,8 +195,6 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI {
SynthContext context = getContext(fc, ENABLED);
- updateUseShellFolder();
-
fc.setLayout(new BorderLayout(0, 11));
// ********************************* //
@@ -432,20 +424,6 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI {
super.uninstallListeners(fc);
}
- private void updateUseShellFolder() {
- // Decide whether to use the ShellFolder class to populate shortcut
- // panel and combobox.
- JFileChooser fc = getFileChooser();
- Boolean prop =
- (Boolean)fc.getClientProperty("FileChooser.useShellFolder");
- if (prop != null) {
- useShellFolder = prop.booleanValue();
- } else {
- useShellFolder = fc.getFileSystemView().equals(FileSystemView.getFileSystemView());
- }
- }
-
-
private String fileNameString(File file) {
if (file == null) {
return null;
@@ -761,6 +739,8 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI {
return;
}
+ boolean useShellFolder = FilePane.usesShellFolder(chooser);
+
int oldSize = directories.size();
directories.clear();
if (oldSize > 0) {
diff --git a/src/share/classes/sun/text/resources/FormatData_th.java b/src/share/classes/sun/text/resources/FormatData_th.java
index 18e1da8ff..e1fe418f5 100644
--- a/src/share/classes/sun/text/resources/FormatData_th.java
+++ b/src/share/classes/sun/text/resources/FormatData_th.java
@@ -47,6 +47,19 @@ public class FormatData_th extends ListResourceBundle {
* Overrides ListResourceBundle
*/
protected final Object[][] getContents() {
+ String[] dateTimePatterns = new String[] {
+ "H' \u0e19\u0e32\u0e2c\u0e34\u0e01\u0e32 'm' \u0e19\u0e32\u0e17\u0e35 'ss' \u0e27\u0e34\u0e19\u0e32\u0e17\u0e35'", // full time pattern
+ "H' \u0e19\u0e32\u0e2c\u0e34\u0e01\u0e32 'm' \u0e19\u0e32\u0e17\u0e35'", // long time pattern
+ "H:mm:ss", // medium time pattern
+ "H:mm' \u0e19.'", // short time pattern (modified) -- add ' \u0e19.'
+ // (it means something like "o'clock" in english)
+ "EEEE'\u0e17\u0e35\u0e48 'd MMMM G yyyy", // full date pattern
+ "d MMMM yyyy", // long date pattern
+ "d MMM yyyy", // medium date pattern
+ "d/M/yyyy", // short date pattern
+ "{1}, {0}" // date-time pattern
+ };
+
return new Object[][] {
{ "MonthNames",
new String[] {
@@ -129,18 +142,10 @@ public class FormatData_th extends ListResourceBundle {
}
},
{ "sun.util.BuddhistCalendar.DateTimePatterns",
- new String[] {
- "H' \u0e19\u0e32\u0e2c\u0e34\u0e01\u0e32 'm' \u0e19\u0e32\u0e17\u0e35 'ss' \u0e27\u0e34\u0e19\u0e32\u0e17\u0e35'", // full time pattern
- "H' \u0e19\u0e32\u0e2c\u0e34\u0e01\u0e32 'm' \u0e19\u0e32\u0e17\u0e35'", // long time pattern
- "H:mm:ss", // medium time pattern
- "H:mm' \u0e19.'", // short time pattern (modified) -- add ' \u0e19.'
- // (it means something like "o'clock" in english)
- "EEEE'\u0e17\u0e35\u0e48 'd MMMM G yyyy", // full date pattern
- "d MMMM yyyy", // long date pattern
- "d MMM yyyy", // medium date pattern
- "d/M/yyyy", // short date pattern
- "{1}, {0}" // date-time pattern
- }
+ dateTimePatterns
+ },
+ { "DateTimePatterns",
+ dateTimePatterns
},
{ "DateTimePatternChars", "GanjkHmsSEDFwWxhKzZ" },
};
diff --git a/src/share/classes/sun/tools/jar/Main.java b/src/share/classes/sun/tools/jar/Main.java
index 693866e18..6d2a67810 100644
--- a/src/share/classes/sun/tools/jar/Main.java
+++ b/src/share/classes/sun/tools/jar/Main.java
@@ -74,7 +74,6 @@ class Main {
static final String MANIFEST = JarFile.MANIFEST_NAME;
static final String MANIFEST_DIR = "META-INF/";
static final String VERSION = "1.0";
- static final char SEPARATOR = File.separatorChar;
static final String INDEX = JarIndex.INDEX_NAME;
private static ResourceBundle rsrc;
@@ -227,19 +226,32 @@ class Main {
}
tmpFile.delete();
}
- } else if (xflag || tflag) {
- InputStream in;
+ } else if (tflag) {
+ replaceFSC(files);
if (fname != null) {
- in = new FileInputStream(fname);
+ list(fname, files);
} else {
- in = new FileInputStream(FileDescriptor.in);
+ InputStream in = new FileInputStream(FileDescriptor.in);
+ try{
+ list(new BufferedInputStream(in), files);
+ } finally {
+ in.close();
+ }
}
- if (xflag) {
- extract(new BufferedInputStream(in), files);
+ } else if (xflag) {
+ replaceFSC(files);
+ if (fname != null && files != null) {
+ extract(fname, files);
} else {
- list(new BufferedInputStream(in), files);
+ InputStream in = (fname == null)
+ ? new FileInputStream(FileDescriptor.in)
+ : new FileInputStream(fname);
+ try {
+ extract(new BufferedInputStream(in), files);
+ } finally {
+ in.close();
+ }
}
- in.close();
} else if (iflag) {
genIndex(rootjar, files);
}
@@ -760,6 +772,31 @@ class Main {
e.setCrc(crc32.getValue());
}
+ void replaceFSC(String files[]) {
+ if (files != null) {
+ for (String file : files) {
+ file = file.replace(File.separatorChar, '/');
+ }
+ }
+ }
+
+ Set<ZipEntry> newDirSet() {
+ return new HashSet<ZipEntry>() {
+ public boolean add(ZipEntry e) {
+ return ((e == null || useExtractionTime) ? false : super.add(e));
+ }};
+ }
+
+ void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException {
+ for (ZipEntry ze : zes) {
+ long lastModified = ze.getTime();
+ if (lastModified != -1) {
+ File f = new File(ze.getName().replace('/', File.separatorChar));
+ f.setLastModified(lastModified);
+ }
+ }
+ }
+
/*
* Extracts specified entries from JAR file.
*/
@@ -768,19 +805,13 @@ class Main {
ZipEntry e;
// Set of all directory entries specified in archive. Disallows
// null entries. Disallows all entries if using pre-6.0 behavior.
- Set<ZipEntry> dirs = new HashSet<ZipEntry>() {
- public boolean add(ZipEntry e) {
- return ((e == null || useExtractionTime) ? false : super.add(e));
- }};
-
+ Set<ZipEntry> dirs = newDirSet();
while ((e = zis.getNextEntry()) != null) {
if (files == null) {
dirs.add(extractFile(zis, e));
-
} else {
String name = e.getName();
- for (int i = 0; i < files.length; i++) {
- String file = files[i].replace(File.separatorChar, '/');
+ for (String file : files) {
if (name.startsWith(file)) {
dirs.add(extractFile(zis, e));
break;
@@ -793,13 +824,33 @@ class Main {
// timestamps as given in the archive. We do this after extraction,
// instead of during, because creating a file in a directory changes
// that directory's timestamp.
- for (ZipEntry dirEntry : dirs) {
- long lastModified = dirEntry.getTime();
- if (lastModified != -1) {
- File dir = new File(dirEntry.getName().replace('/', File.separatorChar));
- dir.setLastModified(lastModified);
+ updateLastModifiedTime(dirs);
+ }
+
+ /*
+ * Extracts specified entries from JAR file, via ZipFile.
+ */
+ void extract(String fname, String files[]) throws IOException {
+ ZipFile zf = new ZipFile(fname);
+ Set<ZipEntry> dirs = newDirSet();
+ Enumeration<? extends ZipEntry> zes = zf.entries();
+ while (zes.hasMoreElements()) {
+ ZipEntry e = zes.nextElement();
+ InputStream is;
+ if (files == null) {
+ dirs.add(extractFile(zf.getInputStream(e), e));
+ } else {
+ String name = e.getName();
+ for (String file : files) {
+ if (name.startsWith(file)) {
+ dirs.add(extractFile(zf.getInputStream(e), e));
+ break;
+ }
+ }
}
}
+ zf.close();
+ updateLastModifiedTime(dirs);
}
/*
@@ -807,7 +858,7 @@ class Main {
* the entry is for a directory which doesn't exist prior to this
* invocation, returns that entry, otherwise returns null.
*/
- ZipEntry extractFile(ZipInputStream zis, ZipEntry e) throws IOException {
+ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
ZipEntry rc = null;
String name = e.getName();
File f = new File(e.getName().replace('/', File.separatorChar));
@@ -838,13 +889,19 @@ class Main {
}
}
OutputStream os = new FileOutputStream(f);
- byte[] b = new byte[512];
+ byte[] b = new byte[8192];
int len;
- while ((len = zis.read(b, 0, b.length)) != -1) {
- os.write(b, 0, len);
+ try {
+ while ((len = is.read(b, 0, b.length)) != -1) {
+ os.write(b, 0, len);
+ }
+ } finally {
+ if (is instanceof ZipInputStream)
+ ((ZipInputStream)is).closeEntry();
+ else
+ is.close();
+ os.close();
}
- zis.closeEntry();
- os.close();
if (vflag) {
if (e.getMethod() == ZipEntry.DEFLATED) {
output(formatMsg("out.inflated", name));
@@ -869,7 +926,6 @@ class Main {
ZipInputStream zis = new ZipInputStream(in);
ZipEntry e;
while ((e = zis.getNextEntry()) != null) {
- String name = e.getName();
/*
* In the case of a compressed (deflated) entry, the entry size
* is stored immediately following the entry data and cannot be
@@ -877,18 +933,20 @@ class Main {
* the entry first before printing out its attributes.
*/
zis.closeEntry();
- if (files == null) {
- printEntry(e);
- } else {
- for (int i = 0; i < files.length; i++) {
- String file = files[i].replace(File.separatorChar, '/');
- if (name.startsWith(file)) {
- printEntry(e);
- break;
- }
- }
- }
+ printEntry(e, files);
+ }
+ }
+
+ /*
+ * Lists contents of JAR file, via ZipFile.
+ */
+ void list(String fname, String files[]) throws IOException {
+ ZipFile zf = new ZipFile(fname);
+ Enumeration<? extends ZipEntry> zes = zf.entries();
+ while (zes.hasMoreElements()) {
+ printEntry(zes.nextElement(), files);
}
+ zf.close();
}
/**
@@ -974,13 +1032,29 @@ class Main {
dumpIndex(rootjar, index);
}
+ /*
+ * Prints entry information, if requested.
+ */
+ void printEntry(ZipEntry e, String[] files) throws IOException {
+ if (files == null) {
+ printEntry(e);
+ } else {
+ String name = e.getName();
+ for (String file : files) {
+ if (name.startsWith(file)) {
+ printEntry(e);
+ return;
+ }
+ }
+ }
+ }
/*
* Prints entry information.
*/
void printEntry(ZipEntry e) throws IOException {
if (vflag) {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
String s = Long.toString(e.getSize());
for (int i = 6 - s.length(); i > 0; --i) {
sb.append(' ');
diff --git a/src/share/classes/sun/util/resources/LocaleNames.properties b/src/share/classes/sun/util/resources/LocaleNames.properties
index 15466144b..440f21534 100644
--- a/src/share/classes/sun/util/resources/LocaleNames.properties
+++ b/src/share/classes/sun/util/resources/LocaleNames.properties
@@ -257,6 +257,7 @@ BG=Bulgaria
BH=Bahrain
BI=Burundi
BJ=Benin
+BL=Saint Barth\u00e9lemy
BM=Bermuda
BN=Brunei
BO=Bolivia
@@ -370,6 +371,7 @@ MA=Morocco
MC=Monaco
MD=Moldova
ME=Montenegro
+MF=Saint Martin
MG=Madagascar
MH=Marshall Islands
MK=Macedonia
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames.java b/src/share/classes/sun/util/resources/TimeZoneNames.java
index aae4dd35e..7b820ecd7 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
"Malaysia Summer Time", "MYST"};
String NORONHA[] = new String[] {"Fernando de Noronha Time", "FNT",
"Fernando de Noronha Summer Time", "FNST"};
+ String NPT[] = new String[] {"Nepal Time", "NPT",
+ "Nepal Summer Time", "NPST"};
String NST[] = new String[] {"Newfoundland Standard Time", "NST",
"Newfoundland Daylight Time", "NDT"};
String NZST[] = new String[] {"New Zealand Standard Time", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
"Pakistan Summer Time", "PKST"};
String PST[] = new String[] {"Pacific Standard Time", "PST",
"Pacific Daylight Time", "PDT"};
+ String RST[] = new String[] {"Eastern Standard Time", "EST",
+ "Central Daylight Time", "CDT"};
String SAST[] = new String[] {"South Africa Standard Time", "SAST",
"South Africa Summer Time", "SAST"};
String SBT[] = new String[] {"Solomon Is. Time", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
"Petropavlovsk-Kamchatski Summer Time", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"Nepal Time", "NPT",
- "Nepal Summer Time", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"Krasnoyarsk Time", "KRAT",
"Krasnoyarsk Summer Time", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_de.java b/src/share/classes/sun/util/resources/TimeZoneNames_de.java
index cdcb45460..f8bd8ba84 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_de.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_de.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle {
"Malaysische Sommerzeit", "MYST"};
String NORONHA[] = new String[] {"Fernando de Noronha Zeit", "FNT",
"Fernando de Noronha Sommerzeit", "FNST"};
+ String NPT[] = new String[] {"Nepalesische Zeit", "NPT",
+ "Nepalesische Sommerzeit", "NPST"};
String NST[] = new String[] {"Neufundland Normalzeit", "NST",
"Neufundland Sommerzeit", "NDT"};
String NZST[] = new String[] {"Neuseeland Normalzeit", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle {
"Pakistanische Sommerzeit", "PKST"};
String PST[] = new String[] {"Pazifische Normalzeit", "PST",
"Pazifische Sommerzeit", "PDT"};
+ String RST[] = new String[] {"\u00d6stliche Normalzeit", "EST",
+ "Zentrale Sommerzeit", "CDT"};
String SAST[] = new String[] {"S\u00fcdafrikanische Normalzeit", "SAST",
"S\u00fcdafrikanische Sommerzeit", "SAST"};
String SBT[] = new String[] {"Salomoninseln Zeit", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle {
"Petropawlowsk-Kamtschatkische Sommerzeit", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"Nepalesische Zeit", "NPT",
- "Nepalesische Sommerzeit", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"Krasnojarsker Zeit", "KRAT",
"Krasnojarsker Sommerzeit", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_es.java b/src/share/classes/sun/util/resources/TimeZoneNames_es.java
index 557ea90c8..54651d692 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_es.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_es.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle {
"Hora de verano de Malasia", "MYST"};
String NORONHA[] = new String[] {"Hora de Fernando de Noronha", "FNT",
"Hora de verano de Fernando de Noronha", "FNST"};
+ String NPT[] = new String[] {"Hora de Nepal", "NPT",
+ "Hora de verano de Nepal", "NPST"};
String NST[] = new String[] {"Hora est\u00e1ndar de Terranova", "NST",
"Hora de verano de Terranova", "NDT"};
String NZST[] = new String[] {"Hora est\u00e1ndar de Nueva Zelanda", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle {
"Hora de verano de Pakist\u00e1n", "PKST"};
String PST[] = new String[] {"Hora est\u00e1ndar del Pac\u00edfico", "PST",
"Hora de verano del Pac\u00edfico", "PDT"};
+ String RST[] = new String[] {"Hora est\u00e1ndar Oriental", "EST",
+ "Hora de verano Central", "CDT"};
String SAST[] = new String[] {"Hora est\u00e1ndar de Sud\u00e1frica", "SAST",
"Hora de verano de Sud\u00e1frica", "SAST"};
String SBT[] = new String[] {"Hora de las Islas Solomon", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle {
"Hora de verano de Petropavlovsk-Kamchatski", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"Hora de Nepal", "NPT",
- "Hora de verano de Nepal", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"Hora de Krasnoyarsk", "KRAT",
"Hora de verano de Krasnoyarsk", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_fr.java b/src/share/classes/sun/util/resources/TimeZoneNames_fr.java
index ec537df46..0a91a7830 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_fr.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_fr.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle {
"Heure d'\u00e9t\u00e9 de Malaisie", "MYST"};
String NORONHA[] = new String[] {"Heure de Fernando de Noronha", "FNT",
"Heure d'\u00e9t\u00e9 de Fernando de Noronha", "FNST"};
+ String NPT[] = new String[] {"Heure du N\u00e9pal", "NPT",
+ "Heure d'\u00e9t\u00e9 du N\u00e9pal", "NPST"};
String NST[] = new String[] {"Heure normale de Terre-Neuve", "NST",
"Heure avanc\u00e9e de Terre-Neuve", "NDT"} ;
String NZST[] = new String[] {"Heure normale de Nouvelle-Z\u00e9lande", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle {
"Heure d'\u00e9t\u00e9 du Pakistan", "PKST"} ;
String PST[] = new String[] {"Heure normale du Pacifique", "PST",
"Heure avanc\u00e9e du Pacifique", "PDT"} ;
+ String RST[] = new String[] {"Heure normale de l'Est", "EST",
+ "Heure avanc\u00e9e du Centre", "CDT"} ;
String SAST[] = new String[] {"Heure normale d'Afrique du Sud", "SAST",
"Heure d'\u00e9t\u00e9 d'Afrique du Sud", "SAST"} ;
String SBT[] = new String[] {"Heure des \u00celes Salomon", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle {
"Heure d'\u00e9t\u00e9 de Petropavlovsk-Kamchatski", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"Heure du N\u00e9pal", "NPT",
- "Heure d'\u00e9t\u00e9 du N\u00e9pal", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"Heure de Krasno\u00efarsk", "KRAT",
"Heure d'\u00e9t\u00e9 de Krasno\u00efarsk", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_it.java b/src/share/classes/sun/util/resources/TimeZoneNames_it.java
index 4c7d79a8a..197dd5e87 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_it.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_it.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle {
"Ora estiva della Malaysia", "MYST"};
String NORONHA[] = new String[] {"Ora di Fernando de Noronha", "FNT",
"Ora estiva di Fernando de Noronha", "FNST"};
+ String NPT[] = new String[] {"Ora del Nepal", "NPT",
+ "Ora estiva del Nepal", "NPST"};
String NST[] = new String[] {"Ora solare di Terranova", "NST",
"Ora legale di Terranova", "NDT"};
String NZST[] = new String[] {"Ora solare della Nuova Zelanda", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle {
"Ora estiva del Pakistan", "PKST"};
String PST[] = new String[] {"Ora solare della costa occidentale USA", "PST",
"Ora legale della costa occidentale USA", "PDT"};
+ String RST[] = new String[] {"Ora solare USA orientale", "EST",
+ "Ora legale USA centrale", "CDT"};
String SAST[] = new String[] {"Ora solare del Sudafrica", "SAST",
"Ora estiva del Sudafrica", "SAST"};
String SBT[] = new String[] {"Ora delle Isole Salomone", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle {
"Ora estiva di Petropavlovsk-Kamchatski", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"Ora del Nepal", "NPT",
- "Ora estiva del Nepal", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"Ora di Krasnojarsk", "KRAT",
"Ora estiva di Krasnojarsk", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_ja.java b/src/share/classes/sun/util/resources/TimeZoneNames_ja.java
index 2eb8063cb..3cf518acf 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_ja.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_ja.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle {
"\u30de\u30ec\u30fc\u30b7\u30a2\u590f\u6642\u9593", "MYST"};
String NORONHA[] = new String[] {"\u30d5\u30a7\u30eb\u30ca\u30f3\u30c9\u30fb\u30c7\u30fb\u30ce\u30ed\u30fc\u30cb\u30e3\u6642\u9593", "FNT",
"\u30d5\u30a7\u30eb\u30ca\u30f3\u30c9\u30fb\u30c7\u30fb\u30ce\u30ed\u30fc\u30cb\u30e3\u590f\u6642\u9593", "FNST"};
+ String NPT[] = new String[] {"\u30cd\u30d1\u30fc\u30eb\u6642\u9593", "NPT",
+ "\u30cd\u30d1\u30fc\u30eb\u590f\u6642\u9593", "NPST"};
String NST[] = new String[] {"\u30cb\u30e5\u30fc\u30d5\u30a1\u30f3\u30c9\u30e9\u30f3\u30c9\u6a19\u6e96\u6642", "NST",
"\u30cb\u30e5\u30fc\u30d5\u30a1\u30f3\u30c9\u30e9\u30f3\u30c9\u590f\u6642\u9593", "NDT"};
String NZST[] = new String[] {"\u30cb\u30e5\u30fc\u30b8\u30fc\u30e9\u30f3\u30c9\u6a19\u6e96\u6642", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle {
"\u30d1\u30ad\u30b9\u30bf\u30f3\u590f\u6642\u9593", "PKST"};
String PST[] = new String[] {"\u592a\u5e73\u6d0b\u6a19\u6e96\u6642", "PST",
"\u592a\u5e73\u6d0b\u590f\u6642\u9593", "PDT"};
+ String RST[] = new String[] {"\u6771\u90e8\u6a19\u6e96\u6642", "EST",
+ "\u4e2d\u90e8\u590f\u6642\u9593", "CDT"};
String SAST[] = new String[] {"\u5357\u30a2\u30d5\u30ea\u30ab\u6a19\u6e96\u6642", "SAST",
"\u5357\u30a2\u30d5\u30ea\u30ab\u590f\u6642\u9593", "SAST"};
String SBT[] = new String[] {"\u30bd\u30ed\u30e2\u30f3\u8af8\u5cf6\u6642\u9593", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle {
"\u30da\u30c8\u30ed\u30d1\u30d6\u30ed\u30d5\u30b9\u30af\u30ab\u30e0\u30c1\u30e3\u30c4\u30ad\u30fc\u590f\u6642\u9593", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"\u30cd\u30d1\u30fc\u30eb\u6642\u9593", "NPT",
- "\u30cd\u30d1\u30fc\u30eb\u590f\u6642\u9593", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"\u30af\u30e9\u30b9\u30ce\u30e4\u30eb\u30b9\u30af\u6642\u9593", "KRAT",
"\u30af\u30e9\u30b9\u30ce\u30e4\u30eb\u30b9\u30af\u590f\u6642\u9593", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_ko.java b/src/share/classes/sun/util/resources/TimeZoneNames_ko.java
index 81a23dd01..d03e15044 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_ko.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_ko.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle {
"\ub9d0\ub808\uc774\uc2dc\uc544 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "MYST"};
String NORONHA[] = new String[] {"Fernando de Noronha \uc2dc\uac04", "FNT",
"Fernando de Noronha \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "FNST"};
+ String NPT[] = new String[] {"\ub124\ud314 \uc2dc\uac04", "NPT",
+ "\ub124\ud314 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "NPST"};
String NST[] = new String[] {"\ub274\ud380\ub4e4\ub79c\ub4dc \ud45c\uc900\uc2dc", "NST",
"\ub274\ud380\ub4e4\ub79c\ub4dc \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "NDT"};
String NZST[] = new String[] {"\ub274\uc9c8\ub79c\ub4dc \ud45c\uc900\uc2dc", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle {
"\ud30c\ud0a4\uc2a4\ud0c4 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PKST"};
String PST[] = new String[] {"\ud0dc\ud3c9\uc591 \ud45c\uc900\uc2dc", "PST",
"\ud0dc\ud3c9\uc591 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PDT"};
+ String RST[] = new String[] {"\ub3d9\ubd80 \ud45c\uc900\uc2dc", "EST",
+ "\uc911\ubd80 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "CDT"};
String SAST[] = new String[] {"\ub0a8\uc544\ud504\ub9ac\uce74 \ud45c\uc900\uc2dc", "SAST",
"\ub0a8\uc544\ud504\ub9ac\uce74 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "SAST"};
String SBT[] = new String[] {"\uc194\ub85c\ubaac \uad70\ub3c4 \uc2dc\uac04", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle {
"\ud398\ud2b8\ub85c\ud30c\ube14\ub85c\ud504\uc2a4\ud06c-\uce84\ucc28\uce20\ud0a4 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"\ub124\ud314 \uc2dc\uac04", "NPT",
- "\ub124\ud314 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"\ud06c\ub77c\uc2a4\ub178\uc57c\ub974\uc2a4\ud06c \uc2dc\uac04", "KRAT",
"\ud06c\ub77c\uc2a4\ub178\uc57c\ub974\uc2a4\ud06c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_sv.java b/src/share/classes/sun/util/resources/TimeZoneNames_sv.java
index e56a8dbff..2ede6f808 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_sv.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_sv.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle {
"Malaysia, sommartid", "MYST"};
String NORONHA[] = new String[] {"Fernando de Noronha, normaltid", "FNT",
"Fernando de Noronha, sommartid", "FNST"};
+ String NPT[] = new String[] {"Nepal, normaltid", "NPT",
+ "Nepal, sommartid", "NPST"};
String NST[] = new String[] {"Newfoundland, normaltid", "NST",
"Newfoundland, sommartid", "NDT"};
String NZST[] = new String[] {"Nya Zeeland, normaltid", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle {
"Pakistan, sommartid", "PKST"};
String PST[] = new String[] {"Stilla havet, normaltid", "PST",
"Stilla havet, sommartid", "PDT"};
+ String RST[] = new String[] {"Eastern, normaltid", "EST",
+ "Central sommartid", "CDT"};
String SAST[] = new String[] {"Sydafrika, normaltid", "SAST",
"Sydafrika, sommartid", "SAST"};
String SBT[] = new String[] {"Salomon\u00f6arna, normaltid", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle {
"Petropavlovsk-Kamtjatka, sommartid", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"Nepal, normaltid", "NPT",
- "Nepal, sommartid", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"Krasnojarsk, normaltid", "KRAT",
"Krasnojarsk, sommartid", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java b/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java
index 41fec630e..121492ce4 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle {
"\u9a6c\u6765\u897f\u4e9a\u590f\u4ee4\u65f6", "MYST"};
String NORONHA[] = new String[] {"\u8d39\u5c14\u5357\u591a\u5fb7\u8bfa\u7f57\u5c3c\u4e9a\u65f6\u95f4", "FNT",
"\u8d39\u5c14\u5357\u591a\u5fb7\u8bfa\u7f57\u5c3c\u4e9a\u590f\u4ee4\u65f6", "FNST"};
+ String NPT[] = new String[] {"\u5c3c\u6cca\u5c14\u65f6\u95f4", "NPT",
+ "\u5c3c\u6cca\u5c14\u590f\u4ee4\u65f6", "NPST"};
String NST[] = new String[] {"\u7ebd\u82ac\u5170\u6807\u51c6\u65f6\u95f4", "NST",
"\u7ebd\u82ac\u5170\u590f\u4ee4\u65f6", "NDT"};
String NZST[] = new String[] {"\u65b0\u897f\u5170\u6807\u51c6\u65f6\u95f4", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle {
"\u5df4\u57fa\u65af\u5766\u590f\u4ee4\u65f6", "PKST"};
String PST[] = new String[] {"\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4", "PST",
"\u592a\u5e73\u6d0b\u590f\u4ee4\u65f6", "PDT"};
+ String RST[] = new String[] {"\u4e1c\u90e8\u6807\u51c6\u65f6\u95f4", "EST",
+ "\u4e2d\u592e\u590f\u4ee4\u65f6", "CDT"};
String SAST[] = new String[] {"\u5357\u975e\u6807\u51c6\u65f6\u95f4", "SAST",
"\u5357\u975e\u590f\u4ee4\u65f6", "SAST"};
String SBT[] = new String[] {"\u6240\u7f57\u95e8\u7fa4\u5c9b\u65f6\u95f4", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle {
"\u5f7c\u5f97\u7f57\u5df4\u752b\u6d1b\u592b\u65af\u514b\u590f\u4ee4\u65f6", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"\u5c3c\u6cca\u5c14\u65f6\u95f4", "NPT",
- "\u5c3c\u6cca\u5c14\u590f\u4ee4\u65f6", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"\u514b\u62c9\u65af\u8bfa\u4e9a\u5c14\u65af\u514b\u65f6\u95f4", "KRAT",
"\u514b\u62c9\u65af\u8bfa\u4e9a\u5c14\u65af\u514b\u590f\u4ee4\u65f6", "KRAST"}},
diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java b/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java
index 1bd6bf739..afd610976 100644
--- a/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java
+++ b/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java
@@ -141,6 +141,8 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle {
"\u99ac\u4f86\u897f\u4e9e\u590f\u4ee4\u6642\u9593", "MYST"};
String NORONHA[] = new String[] {"\u8cbb\u723e\u5357\u591a-\u8fea\u8afe\u7f85\u5c3c\u4e9e\u6642\u9593", "FNT",
"\u8cbb\u723e\u5357\u591a-\u8fea\u8afe\u7f85\u5c3c\u4e9e\u590f\u4ee4\u6642\u9593", "FNST"};
+ String NPT[] = new String[] {"\u5c3c\u6cca\u723e\u6642\u9593", "NPT",
+ "\u5c3c\u6cca\u723e\u590f\u4ee4\u6642\u9593", "NPST"};
String NST[] = new String[] {"\u7d10\u82ac\u862d\u6a19\u6e96\u6642\u9593", "NST",
"\u7d10\u82ac\u862d\u65e5\u5149\u7bc0\u7d04\u6642\u9593", "NDT"};
String NZST[] = new String[] {"\u7d10\u897f\u862d\u6a19\u6e96\u6642\u9593", "NZST",
@@ -151,6 +153,8 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle {
"\u5df4\u57fa\u65af\u5766\u590f\u4ee4\u6642\u9593", "PKST"};
String PST[] = new String[] {"\u592a\u5e73\u6d0b\u6a19\u6e96\u6642\u9593", "PST",
"\u592a\u5e73\u6d0b\u65e5\u5149\u7bc0\u7d04\u6642\u9593", "PDT"};
+ String RST[] = new String[] {"\u6771\u65b9\u6a19\u6e96\u6642\u9593", "EST",
+ "\u4e2d\u592e\u65e5\u5149\u7bc0\u7d04\u6642\u9593", "CDT"};
String SAST[] = new String[] {"\u5357\u975e\u6a19\u6e96\u6642\u9593", "SAST",
"\u5357\u975e\u590f\u4ee4\u6642\u9593", "SAST"};
String SBT[] = new String[] {"\u6240\u7f85\u9580\u7fa4\u5cf6\u6642\u9593", "SBT",
@@ -290,6 +294,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle {
{"America/Argentina/La_Rioja", AGT},
{"America/Argentina/Mendoza", AGT},
{"America/Argentina/Rio_Gallegos", AGT},
+ {"America/Argentina/Salta", AGT},
{"America/Argentina/San_Juan", AGT},
{"America/Argentina/San_Luis", AGT},
{"America/Argentina/Tucuman", AGT},
@@ -407,7 +412,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle {
{"America/Rankin_Inlet", CST},
{"America/Recife", BRT},
{"America/Regina", CST},
- {"America/Resolute", EST},
+ {"America/Resolute", RST},
{"America/Rio_Branco", AMT},
{"America/Rosario", AGT},
{"America/Santarem", BRT},
@@ -505,8 +510,8 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle {
"Petropavlovsk-Kamchatski \u590f\u4ee4\u6642\u9593", "PETST"}},
{"Asia/Karachi", PKT},
{"Asia/Kashgar", CTT},
- {"Asia/Katmandu", new String[] {"\u5c3c\u6cca\u723e\u6642\u9593", "NPT",
- "\u5c3c\u6cca\u723e\u590f\u4ee4\u6642\u9593", "NPST"}},
+ {"Asia/Kathmandu", NPT},
+ {"Asia/Katmandu", NPT},
{"Asia/Kolkata", IST},
{"Asia/Krasnoyarsk", new String[] {"\u514b\u62c9\u65af\u8afe\u4e9e\u723e\u65af\u514b\u6642\u9593", "KRAT",
"\u514b\u62c9\u65af\u8afe\u4e9e\u723e\u65af\u514b\u590f\u4ee4\u6642\u9593", "KRAST"}},
diff --git a/src/share/lib/audio/soundbank.gm b/src/share/lib/audio/soundbank.gm
deleted file mode 100644
index 83c2f878d..000000000
--- a/src/share/lib/audio/soundbank.gm
+++ /dev/null
Binary files differ
diff --git a/src/share/native/java/util/zip/zip_util.c b/src/share/native/java/util/zip/zip_util.c
index 3b00b9500..9767dba45 100644
--- a/src/share/native/java/util/zip/zip_util.c
+++ b/src/share/native/java/util/zip/zip_util.c
@@ -135,11 +135,6 @@ ZFILE_Close(ZFILE zfd) {
#endif
}
-static jlong
-ZFILE_Lseek(ZFILE zfd, off_t offset, int whence) {
- return IO_Lseek(zfd, offset, whence);
-}
-
static int
ZFILE_read(ZFILE zfd, char *buf, jint nbytes) {
#ifdef WIN32
@@ -216,7 +211,7 @@ readFully(ZFILE zfd, void *buf, jlong len) {
static int
readFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset)
{
- if (ZFILE_Lseek(zfd, (off_t) offset, SEEK_SET) == -1) {
+ if (IO_Lseek(zfd, offset, SEEK_SET) == -1) {
return -1; /* lseek failure. */
}
@@ -476,7 +471,7 @@ readCEN(jzfile *zip, jint knownTotal)
unsigned char *cp;
#ifdef USE_MMAP
static jlong pagesize;
- off_t offset;
+ jlong offset;
#endif
unsigned char endbuf[ENDHDR];
jzcell *entries;
@@ -534,7 +529,7 @@ readCEN(jzfile *zip, jint knownTotal)
*/
zip->mlen = cenpos - offset + cenlen + ENDHDR;
zip->offset = offset;
- mappedAddr = mmap(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, offset);
+ mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
(unsigned char*)mappedAddr;
@@ -720,7 +715,7 @@ ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified)
return NULL;
}
- len = zip->len = ZFILE_Lseek(zfd, 0, SEEK_END);
+ len = zip->len = IO_Lseek(zfd, 0, SEEK_END);
if (len <= 0) {
if (len == 0) { /* zip file is empty */
if (pmsg) {
diff --git a/src/share/native/java/util/zip/zip_util.h b/src/share/native/java/util/zip/zip_util.h
index 5fdebaf13..353ebd238 100644
--- a/src/share/native/java/util/zip/zip_util.h
+++ b/src/share/native/java/util/zip/zip_util.h
@@ -174,7 +174,7 @@ typedef struct jzfile { /* Zip file */
#ifdef USE_MMAP
unsigned char *maddr; /* beginning address of the CEN & ENDHDR */
jlong mlen; /* length (in bytes) mmaped */
- off_t offset; /* offset of the mmapped region from the
+ jlong offset; /* offset of the mmapped region from the
start of the file. */
#else
cencache cencache; /* CEN header cache */
diff --git a/src/share/native/sun/nio/ch/genSocketOptionRegistry.c b/src/share/native/sun/nio/ch/genSocketOptionRegistry.c
index 70f95c6c5..bcd546b13 100644
--- a/src/share/native/sun/nio/ch/genSocketOptionRegistry.c
+++ b/src/share/native/sun/nio/ch/genSocketOptionRegistry.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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
@@ -71,9 +71,9 @@ int main(int argc, const char* argv[]) {
out("class SocketOptionRegistry { ");
out(" private SocketOptionRegistry() { } ");
out(" private static class RegistryKey { ");
- out(" private final SocketOption name; ");
+ out(" private final SocketOption<?> name; ");
out(" private final ProtocolFamily family; ");
- out(" RegistryKey(SocketOption name, ProtocolFamily family) { ");
+ out(" RegistryKey(SocketOption<?> name, ProtocolFamily family) { ");
out(" this.name = name; ");
out(" this.family = family; ");
out(" } ");
@@ -119,7 +119,7 @@ int main(int argc, const char* argv[]) {
out(" return map; ");
out(" } ");
out(" } ");
- out(" public static OptionKey findOption(SocketOption name, ProtocolFamily family) { ");
+ out(" public static OptionKey findOption(SocketOption<?> name, ProtocolFamily family) { ");
out(" RegistryKey key = new RegistryKey(name, family); ");
out(" return LazyInitialization.options.get(key); ");
out(" } ");
diff --git a/src/share/sample/nio/file/AclEdit.java b/src/share/sample/nio/file/AclEdit.java
new file mode 100644
index 000000000..55ed9c00b
--- /dev/null
+++ b/src/share/sample/nio/file/AclEdit.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * Sample utility for editing a file's ACL.
+ */
+
+public class AclEdit {
+
+ // parse string as list of ACE permissions separated by /
+ static Set<AclEntryPermission> parsePermissions(String permsString) {
+ Set<AclEntryPermission> perms = new HashSet<AclEntryPermission>();
+ String[] result = permsString.split("/");
+ for (String s : result) {
+ if (s.equals(""))
+ continue;
+ try {
+ perms.add(AclEntryPermission.valueOf(s.toUpperCase()));
+ } catch (IllegalArgumentException x) {
+ System.err.format("Invalid permission '%s'\n", s);
+ System.exit(-1);
+ }
+ }
+ return perms;
+ }
+
+ // parse string as list of ACE flags separated by /
+ static Set<AclEntryFlag> parseFlags(String flagsString) {
+ Set<AclEntryFlag> flags = new HashSet<AclEntryFlag>();
+ String[] result = flagsString.split("/");
+ for (String s : result) {
+ if (s.equals(""))
+ continue;
+ try {
+ flags.add(AclEntryFlag.valueOf(s.toUpperCase()));
+ } catch (IllegalArgumentException x) {
+ System.err.format("Invalid flag '%s'\n", s);
+ System.exit(-1);
+ }
+ }
+ return flags;
+ }
+
+ // parse ACE type
+ static AclEntryType parseType(String typeString) {
+ // FIXME: support audit and alarm types in the future
+ if (typeString.equalsIgnoreCase("allow"))
+ return AclEntryType.ALLOW;
+ if (typeString.equalsIgnoreCase("deny"))
+ return AclEntryType.DENY;
+ System.err.format("Invalid type '%s'\n", typeString);
+ System.exit(-1);
+ return null; // keep compiler happy
+ }
+
+ /**
+ * Parse string of the form:
+ * [user|group:]<username|groupname>:<perms>[:flags]:<allow|deny>
+ */
+ static AclEntry parseAceString(String s,
+ UserPrincipalLookupService lookupService)
+ {
+ String[] result = s.split(":");
+
+ // must have at least 3 components (username:perms:type)
+ if (result.length < 3)
+ usage();
+
+ int index = 0;
+ int remaining = result.length;
+
+ // optional first component can indicate user or group type
+ boolean isGroup = false;
+ if (result[index].equalsIgnoreCase("user") ||
+ result[index].equalsIgnoreCase("group"))
+ {
+ if (--remaining < 3)
+ usage();
+ isGroup = result[index++].equalsIgnoreCase("group");
+ }
+
+ // user and permissions required
+ String userString = result[index++]; remaining--;
+ String permsString = result[index++]; remaining--;
+
+ // flags are optional
+ String flagsString = "";
+ String typeString = null;
+ if (remaining == 1) {
+ typeString = result[index++];
+ } else {
+ if (remaining == 2) {
+ flagsString = result[index++];
+ typeString = result[index++];
+ } else {
+ usage();
+ }
+ }
+
+ // lookup UserPrincipal
+ UserPrincipal user = null;
+ try {
+ user = (isGroup) ?
+ lookupService.lookupPrincipalByGroupName(userString) :
+ lookupService.lookupPrincipalByName(userString);
+ } catch (UserPrincipalNotFoundException x) {
+ System.err.format("Invalid %s '%s'\n",
+ ((isGroup) ? "group" : "user"),
+ userString);
+ System.exit(-1);
+ } catch (IOException x) {
+ System.err.format("Lookup of '%s' failed: %s\n", userString, x);
+ System.exit(-1);
+ }
+
+ // map string representation of permissions, flags, and type
+ Set<AclEntryPermission> perms = parsePermissions(permsString);
+ Set<AclEntryFlag> flags = parseFlags(flagsString);
+ AclEntryType type = parseType(typeString);
+
+ // build the ACL entry
+ return AclEntry.newBuilder()
+ .setType(type)
+ .setPrincipal(user)
+ .setPermissions(perms).setFlags(flags).build();
+ }
+
+ static void usage() {
+ System.err.println("usage: java AclEdit [ACL-operation] file");
+ System.err.println("");
+ System.err.println("Example 1: Prepends access control entry to the begining of the myfile's ACL");
+ System.err.println(" java AclEdit A+alice:read_data/read_attributes:allow myfile");
+ System.err.println("");
+ System.err.println("Example 2: Remove the entry at index 6 of myfile's ACL");
+ System.err.println(" java AclEdit A6- myfile");
+ System.err.println("");
+ System.err.println("Example 3: Replace the entry at index 2 of myfile's ACL");
+ System.err.println(" java AclEdit A2=bob:write_data/append_data:deny myfile");
+ System.exit(-1);
+ }
+
+ static enum Action {
+ PRINT,
+ ADD,
+ REMOVE,
+ REPLACE;
+ }
+
+ /**
+ * Main class: parses arguments and prints or edits ACL
+ */
+ public static void main(String[] args) throws IOException {
+ Action action = null;
+ int index = -1;
+ String entryString = null;
+
+ // parse arguments
+ if (args.length < 1 || args[0].equals("-help") || args[0].equals("-?"))
+ usage();
+
+ if (args.length == 1) {
+ action = Action.PRINT;
+ } else {
+ String s = args[0];
+
+ // A[index]+entry
+ if (Pattern.matches("^A[0-9]*\\+.*", s)) {
+ String[] result = s.split("\\+", 2);
+ if (result.length == 2) {
+ if (result[0].length() < 2) {
+ index = 0;
+ } else {
+ index = Integer.parseInt(result[0].substring(1));
+ }
+ entryString = result[1];
+ action = Action.ADD;
+ }
+ }
+
+ // Aindex-
+ if (Pattern.matches("^A[0-9]+\\-", s)) {
+ String[] result = s.split("\\-", 2);
+ if (result.length == 2) {
+ index = Integer.parseInt(result[0].substring(1));
+ entryString = result[1];
+ action = Action.REMOVE;
+ }
+ }
+
+ // Aindex=entry
+ if (Pattern.matches("^A[0-9]+=.*", s)) {
+ String[] result = s.split("=", 2);
+ if (result.length == 2) {
+ index = Integer.parseInt(result[0].substring(1));
+ entryString = result[1];
+ action = Action.REPLACE;
+ }
+ }
+ }
+ if (action == null)
+ usage();
+
+ int fileArg = (action == Action.PRINT) ? 0 : 1;
+ Path file = Paths.get(args[fileArg]);
+
+ // read file's ACL
+ AclFileAttributeView view =
+ file.getFileAttributeView(AclFileAttributeView.class);
+ if (view == null) {
+ System.err.println("ACLs not supported on this platform");
+ System.exit(-1);
+ }
+ List<AclEntry> acl = view.getAcl();
+
+ switch (action) {
+ // print ACL
+ case PRINT : {
+ for (int i=0; i<acl.size(); i++) {
+ System.out.format("%5d: %s\n", i, acl.get(i));
+ }
+ break;
+ }
+
+ // add ACE to existing ACL
+ case ADD: {
+ AclEntry entry = parseAceString(entryString, file
+ .getFileSystem().getUserPrincipalLookupService());
+ if (index >= acl.size()) {
+ acl.add(entry);
+ } else {
+ acl.add(index, entry);
+ }
+ view.setAcl(acl);
+ break;
+ }
+
+ // remove ACE
+ case REMOVE: {
+ if (index >= acl.size()) {
+ System.err.format("Index '%d' is invalid", index);
+ System.exit(-1);
+ }
+ acl.remove(index);
+ view.setAcl(acl);
+ break;
+ }
+
+ // replace ACE
+ case REPLACE: {
+ if (index >= acl.size()) {
+ System.err.format("Index '%d' is invalid", index);
+ System.exit(-1);
+ }
+ AclEntry entry = parseAceString(entryString, file
+ .getFileSystem().getUserPrincipalLookupService());
+ acl.set(index, entry);
+ view.setAcl(acl);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/share/sample/nio/file/Chmod.java b/src/share/sample/nio/file/Chmod.java
new file mode 100644
index 000000000..eeb54ad6c
--- /dev/null
+++ b/src/share/sample/nio/file/Chmod.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import static java.nio.file.attribute.PosixFilePermission.*;
+import static java.nio.file.FileVisitResult.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Sample code that changes the permissions of files in a similar manner to the
+ * chmod(1) program.
+ */
+
+public class Chmod {
+
+ /**
+ * Compiles a list of one or more <em>symbolic mode expressions</em> that
+ * may be used to change a set of file permissions. This method is
+ * intended for use where file permissions are required to be changed in
+ * a manner similar to the UNIX <i>chmod</i> program.
+ *
+ * <p> The {@code exprs} parameter is a comma separated list of expressions
+ * where each takes the form:
+ * <blockquote>
+ * <i>who operator</i> [<i>permissions</i>]
+ * </blockquote>
+ * where <i>who</i> is one or more of the characters {@code 'u'}, {@code 'g'},
+ * {@code 'o'}, or {@code 'a'} meaning the owner (user), group, others, or
+ * all (owner, group, and others) respectively.
+ *
+ * <p> <i>operator</i> is the character {@code '+'}, {@code '-'}, or {@code
+ * '='} signifying how permissions are to be changed. {@code '+'} means the
+ * permissions are added, {@code '-'} means the permissions are removed, and
+ * {@code '='} means the permissions are assigned absolutely.
+ *
+ * <p> <i>permissions</i> is a sequence of zero or more of the following:
+ * {@code 'r'} for read permission, {@code 'w'} for write permission, and
+ * {@code 'x'} for execute permission. If <i>permissions</i> is omitted
+ * when assigned absolutely, then the permissions are cleared for
+ * the owner, group, or others as identified by <i>who</i>. When omitted
+ * when adding or removing then the expression is ignored.
+ *
+ * <p> The following examples demonstrate possible values for the {@code
+ * exprs} parameter:
+ *
+ * <table border="0">
+ * <tr>
+ * <td> {@code u=rw} </td>
+ * <td> Sets the owner permissions to be read and write. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code ug+w} </td>
+ * <td> Sets the owner write and group write permissions. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code u+w,o-rwx} </td>
+ * <td> Sets the owner write, and removes the others read, others write
+ * and others execute permissions. </td>
+ * </tr>
+ * <tr>
+ * <td> {@code o=} </td>
+ * <td> Sets the others permission to none (others read, others write and
+ * others execute permissions are removed if set) </td>
+ * </tr>
+ * </table>
+ *
+ * @param exprs
+ * List of one or more <em>symbolic mode expressions</em>
+ *
+ * @return A {@code Changer} that may be used to changer a set of
+ * file permissions
+ *
+ * @throws IllegalArgumentException
+ * If the value of the {@code exprs} parameter is invalid
+ */
+ public static Changer compile(String exprs) {
+ // minimum is who and operator (u= for example)
+ if (exprs.length() < 2)
+ throw new IllegalArgumentException("Invalid mode");
+
+ // permissions that the changer will add or remove
+ final Set<PosixFilePermission> toAdd = new HashSet<PosixFilePermission>();
+ final Set<PosixFilePermission> toRemove = new HashSet<PosixFilePermission>();
+
+ // iterate over each of expression modes
+ for (String expr: exprs.split(",")) {
+ // minimum of who and operator
+ if (expr.length() < 2)
+ throw new IllegalArgumentException("Invalid mode");
+
+ int pos = 0;
+
+ // who
+ boolean u = false;
+ boolean g = false;
+ boolean o = false;
+ boolean done = false;
+ for (;;) {
+ switch (expr.charAt(pos)) {
+ case 'u' : u = true; break;
+ case 'g' : g = true; break;
+ case 'o' : o = true; break;
+ case 'a' : u = true; g = true; o = true; break;
+ default : done = true;
+ }
+ if (done)
+ break;
+ pos++;
+ }
+ if (!u && !g && !o)
+ throw new IllegalArgumentException("Invalid mode");
+
+ // get operator and permissions
+ char op = expr.charAt(pos++);
+ String mask = (expr.length() == pos) ? "" : expr.substring(pos);
+
+ // operator
+ boolean add = (op == '+');
+ boolean remove = (op == '-');
+ boolean assign = (op == '=');
+ if (!add && !remove && !assign)
+ throw new IllegalArgumentException("Invalid mode");
+
+ // who= means remove all
+ if (assign && mask.length() == 0) {
+ assign = false;
+ remove = true;
+ mask = "rwx";
+ }
+
+ // permissions
+ boolean r = false;
+ boolean w = false;
+ boolean x = false;
+ for (int i=0; i<mask.length(); i++) {
+ switch (mask.charAt(i)) {
+ case 'r' : r = true; break;
+ case 'w' : w = true; break;
+ case 'x' : x = true; break;
+ default:
+ throw new IllegalArgumentException("Invalid mode");
+ }
+ }
+
+ // update permissions set
+ if (add) {
+ if (u) {
+ if (r) toAdd.add(OWNER_READ);
+ if (w) toAdd.add(OWNER_WRITE);
+ if (x) toAdd.add(OWNER_EXECUTE);
+ }
+ if (g) {
+ if (r) toAdd.add(GROUP_READ);
+ if (w) toAdd.add(GROUP_WRITE);
+ if (x) toAdd.add(GROUP_EXECUTE);
+ }
+ if (o) {
+ if (r) toAdd.add(OTHERS_READ);
+ if (w) toAdd.add(OTHERS_WRITE);
+ if (x) toAdd.add(OTHERS_EXECUTE);
+ }
+ }
+ if (remove) {
+ if (u) {
+ if (r) toRemove.add(OWNER_READ);
+ if (w) toRemove.add(OWNER_WRITE);
+ if (x) toRemove.add(OWNER_EXECUTE);
+ }
+ if (g) {
+ if (r) toRemove.add(GROUP_READ);
+ if (w) toRemove.add(GROUP_WRITE);
+ if (x) toRemove.add(GROUP_EXECUTE);
+ }
+ if (o) {
+ if (r) toRemove.add(OTHERS_READ);
+ if (w) toRemove.add(OTHERS_WRITE);
+ if (x) toRemove.add(OTHERS_EXECUTE);
+ }
+ }
+ if (assign) {
+ if (u) {
+ if (r) toAdd.add(OWNER_READ);
+ else toRemove.add(OWNER_READ);
+ if (w) toAdd.add(OWNER_WRITE);
+ else toRemove.add(OWNER_WRITE);
+ if (x) toAdd.add(OWNER_EXECUTE);
+ else toRemove.add(OWNER_EXECUTE);
+ }
+ if (g) {
+ if (r) toAdd.add(GROUP_READ);
+ else toRemove.add(GROUP_READ);
+ if (w) toAdd.add(GROUP_WRITE);
+ else toRemove.add(GROUP_WRITE);
+ if (x) toAdd.add(GROUP_EXECUTE);
+ else toRemove.add(GROUP_EXECUTE);
+ }
+ if (o) {
+ if (r) toAdd.add(OTHERS_READ);
+ else toRemove.add(OTHERS_READ);
+ if (w) toAdd.add(OTHERS_WRITE);
+ else toRemove.add(OTHERS_WRITE);
+ if (x) toAdd.add(OTHERS_EXECUTE);
+ else toRemove.add(OTHERS_EXECUTE);
+ }
+ }
+ }
+
+ // return changer
+ return new Changer() {
+ @Override
+ public Set<PosixFilePermission> change(Set<PosixFilePermission> perms) {
+ perms.addAll(toAdd);
+ perms.removeAll(toRemove);
+ return perms;
+ }
+ };
+ }
+
+ /**
+ * A task that <i>changes</i> a set of {@link PosixFilePermission} elements.
+ */
+ public interface Changer {
+ /**
+ * Applies the changes to the given set of permissions.
+ *
+ * @param perms
+ * The set of permissions to change
+ *
+ * @return The {@code perms} parameter
+ */
+ Set<PosixFilePermission> change(Set<PosixFilePermission> perms);
+ }
+
+ /**
+ * Changes the permissions of the file using the given Changer.
+ */
+ static void chmod(FileRef file, Changer changer) {
+ try {
+ Set<PosixFilePermission> perms = Attributes
+ .readPosixFileAttributes(file).permissions();
+ Attributes.setPosixFilePermissions(file, changer.change(perms));
+ } catch (IOException x) {
+ System.err.println(x);
+ }
+ }
+
+ /**
+ * Changes the permission of each file and directory visited
+ */
+ static class TreeVisitor implements FileVisitor<FileRef> {
+ private final Changer changer;
+
+ TreeVisitor(Changer changer) {
+ this.changer = changer;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(FileRef dir) {
+ chmod(dir, changer);
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectoryFailed(FileRef dir, IOException exc) {
+ System.err.println("WARNING: " + exc);
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(FileRef file, BasicFileAttributes attrs) {
+ chmod(file, changer);
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(FileRef dir, IOException exc) {
+ if (exc != null)
+ System.err.println("WARNING: " + exc);
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(FileRef file, IOException exc) {
+ System.err.println("WARNING: " + exc);
+ return CONTINUE;
+ }
+ }
+
+ static void usage() {
+ System.err.println("java Chmod [-R] symbolic-mode-list file...");
+ System.exit(-1);
+ }
+
+ public static void main(String[] args) throws IOException {
+ if (args.length < 2)
+ usage();
+ int argi = 0;
+ int maxDepth = 0;
+ if (args[argi].equals("-R")) {
+ if (args.length < 3)
+ usage();
+ argi++;
+ maxDepth = Integer.MAX_VALUE;
+ }
+
+ // compile the symbolic mode expressions
+ Changer changer = compile(args[argi++]);
+ TreeVisitor visitor = new TreeVisitor(changer);
+
+ Set<FileVisitOption> opts = Collections.emptySet();
+ while (argi < args.length) {
+ Path file = Paths.get(args[argi]);
+ Files.walkFileTree(file, opts, maxDepth, visitor);
+ argi++;
+ }
+ }
+}
diff --git a/src/share/sample/nio/file/Copy.java b/src/share/sample/nio/file/Copy.java
new file mode 100644
index 000000000..e1d5d044e
--- /dev/null
+++ b/src/share/sample/nio/file/Copy.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.file.*;
+import static java.nio.file.StandardCopyOption.*;
+import java.nio.file.attribute.*;
+import static java.nio.file.FileVisitResult.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Sample code that copies files in a similar manner to the cp(1) program.
+ */
+
+public class Copy {
+
+ /**
+ * Returns {@code true} if okay to overwrite a file ("cp -i")
+ */
+ static boolean okayToOverwrite(FileRef file) {
+ String answer = System.console().readLine("overwrite %s (yes/no)? ", file);
+ return (answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("yes"));
+ }
+
+ /**
+ * Copy source file to target location. If {@code prompt} is true then
+ * prompted user to overwrite target if it exists. The {@code preserve}
+ * parameter determines if file attributes should be copied/preserved.
+ */
+ static void copyFile(Path source, Path target, boolean prompt, boolean preserve) {
+ CopyOption[] options = (preserve) ?
+ new CopyOption[] { COPY_ATTRIBUTES, REPLACE_EXISTING } :
+ new CopyOption[] { REPLACE_EXISTING };
+ if (!prompt || target.notExists() || okayToOverwrite(target)) {
+ try {
+ source.copyTo(target, options);
+ } catch (IOException x) {
+ System.err.format("Unable to create: %s: %s%n", target, x);
+ }
+ }
+ }
+
+ /**
+ * A {@code FileVisitor} that copies a file-tree ("cp -r")
+ */
+ static class TreeCopier implements FileVisitor<Path> {
+ private final Path source;
+ private final Path target;
+ private final boolean prompt;
+ private final boolean preserve;
+
+ TreeCopier(Path source, Path target, boolean prompt, boolean preserve) {
+ this.source = source;
+ this.target = target;
+ this.prompt = prompt;
+ this.preserve = preserve;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir) {
+ // before visiting entries in a directory we copy the directory
+ // (okay if directory already exists).
+ CopyOption[] options = (preserve) ?
+ new CopyOption[] { COPY_ATTRIBUTES } : new CopyOption[0];
+
+ Path newdir = target.resolve(source.relativize(dir));
+ try {
+ dir.copyTo(newdir, options);
+ } catch (FileAlreadyExistsException x) {
+ // ignore
+ } catch (IOException x) {
+ System.err.format("Unable to create: %s: %s%n", newdir, x);
+ return SKIP_SUBTREE;
+ }
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) {
+ System.err.format("Unable to copy: %s: %s%n", dir, exc);
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ if (attrs.isDirectory()) {
+ System.err.println("cycle detected: " + file);
+ } else {
+ copyFile(file, target.resolve(source.relativize(file)),
+ prompt, preserve);
+ }
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ // fix up modification time of directory when done
+ if (exc == null && preserve) {
+ try {
+ BasicFileAttributes attrs = Attributes.readBasicFileAttributes(dir);
+ Path newdir = target.resolve(source.relativize(dir));
+ Attributes.setLastModifiedTime(newdir,
+ attrs.lastModifiedTime(), attrs.resolution());
+ } catch (IOException x) {
+ // ignore
+ }
+ }
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ System.err.format("Unable to copy: %s: %s%n", file, exc);
+ return CONTINUE;
+ }
+ }
+
+ static void usage() {
+ System.err.println("java Copy [-ip] source... target");
+ System.err.println("java Copy -r [-ip] source-dir... target");
+ System.exit(-1);
+ }
+
+ public static void main(String[] args) throws IOException {
+ boolean recursive = false;
+ boolean prompt = false;
+ boolean preserve = false;
+
+ // process options
+ int argi = 0;
+ while (argi < args.length) {
+ String arg = args[argi];
+ if (!arg.startsWith("-"))
+ break;
+ if (arg.length() < 2)
+ usage();
+ for (int i=1; i<arg.length(); i++) {
+ char c = arg.charAt(i);
+ switch (c) {
+ case 'r' : recursive = true; break;
+ case 'i' : prompt = true; break;
+ case 'p' : preserve = true; break;
+ default : usage();
+ }
+ }
+ argi++;
+ }
+
+ // remaining arguments are the source files(s) and the target location
+ int remaining = args.length - argi;
+ if (remaining < 2)
+ usage();
+ Path[] source = new Path[remaining-1];
+ int i=0;
+ while (remaining > 1) {
+ source[i++] = Paths.get(args[argi++]);
+ remaining--;
+ }
+ Path target = Paths.get(args[argi]);
+
+ // check if target is a directory
+ boolean isDir = false;
+ try {
+ isDir = Attributes.readBasicFileAttributes(target).isDirectory();
+ } catch (IOException x) {
+ }
+
+ // copy each source file/directory to target
+ for (i=0; i<source.length; i++) {
+ Path dest = (isDir) ? target.resolve(source[i].getName()) : target;
+
+ if (recursive) {
+ // follow links when copying files
+ EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
+ TreeCopier tc = new TreeCopier(source[i], dest, prompt, preserve);
+ Files.walkFileTree(source[i], opts, -1, tc);
+ } else {
+ // not recursive so source must not be a directory
+ try {
+ if (Attributes.readBasicFileAttributes(source[i]).isDirectory()) {
+ System.err.format("%s: is a directory%n", source[i]);
+ continue;
+ }
+ } catch (IOException x) { }
+ copyFile(source[i], dest, prompt, preserve);
+ }
+ }
+ }
+}
diff --git a/src/share/sample/nio/file/DiskUsage.java b/src/share/sample/nio/file/DiskUsage.java
new file mode 100644
index 000000000..ff16d8884
--- /dev/null
+++ b/src/share/sample/nio/file/DiskUsage.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+
+/**
+ * Example utility that works like the df(1M) program to print out disk space
+ * information
+ */
+
+public class DiskUsage {
+
+ static final long K = 1024;
+
+ static void printFileStore(FileStore store) throws IOException {
+ FileStoreSpaceAttributes attrs = Attributes.readFileStoreSpaceAttributes(store);
+
+ long total = attrs.totalSpace() / K;
+ long used = (attrs.totalSpace() - attrs.unallocatedSpace()) / K;
+ long avail = attrs.usableSpace() / K;
+
+ String s = store.toString();
+ if (s.length() > 20) {
+ System.out.println(s);
+ s = "";
+ }
+ System.out.format("%-20s %12d %12d %12d\n", s, total, used, avail);
+ }
+
+ public static void main(String[] args) throws IOException {
+ System.out.format("%-20s %12s %12s %12s\n", "Filesystem", "kbytes", "used", "avail");
+ if (args.length == 0) {
+ FileSystem fs = FileSystems.getDefault();
+ for (FileStore store: fs.getFileStores()) {
+ printFileStore(store);
+ }
+ } else {
+ for (String file: args) {
+ FileStore store = Paths.get(file).getFileStore();
+ printFileStore(store);
+ }
+ }
+ }
+}
diff --git a/src/share/sample/nio/file/FileType.java b/src/share/sample/nio/file/FileType.java
new file mode 100644
index 000000000..31fb45299
--- /dev/null
+++ b/src/share/sample/nio/file/FileType.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+
+public class FileType {
+ public static void main(String[] args) throws IOException {
+ if (args.length == 0) {
+ System.err.println("usage: java FileType file...");
+ System.exit(-1);
+ }
+ for (String arg: args) {
+ Path file = Paths.get(arg);
+ BasicFileAttributes attrs = Attributes.readBasicFileAttributes(file);
+
+ String type;
+ if (attrs.isDirectory()) {
+ type = "directory";
+ } else {
+ type = Files.probeContentType(file);
+ if (type == null)
+ type = "<not recognized>";
+ }
+ System.out.format("%s\t%s%n", file, type);
+ }
+ }
+}
diff --git a/src/share/sample/nio/file/WatchDir.java b/src/share/sample/nio/file/WatchDir.java
new file mode 100644
index 000000000..b8b6474dc
--- /dev/null
+++ b/src/share/sample/nio/file/WatchDir.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.file.*;
+import static java.nio.file.StandardWatchEventKind.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Example to watch a directory (or tree) for changes to files.
+ */
+
+public class WatchDir {
+
+ private final WatchService watcher;
+ private final Map<WatchKey,Path> keys;
+ private final boolean recursive;
+ private boolean trace = false;
+
+ @SuppressWarnings("unchecked")
+ static <T> WatchEvent<T> cast(WatchEvent<?> event) {
+ return (WatchEvent<T>)event;
+ }
+
+ /**
+ * Register the given directory with the WatchService
+ */
+ private void register(Path dir) throws IOException {
+ WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+ if (trace) {
+ FileRef prev = keys.get(key);
+ if (prev == null) {
+ System.out.format("register: %s\n", dir);
+ } else {
+ if (!dir.equals(prev)) {
+ System.out.format("update: %s -> %s\n", prev, dir);
+ }
+ }
+ }
+ keys.put(key, dir);
+ }
+
+ /**
+ * Register the given directory, and all its sub-directories, with the
+ * WatchService.
+ */
+ private void registerAll(final Path start) throws IOException {
+ // register directory and sub-directories
+ Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir) {
+ try {
+ register(dir);
+ } catch (IOException x) {
+ throw new IOError(x);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ /**
+ * Creates a WatchService and registers the given directory
+ */
+ WatchDir(Path dir, boolean recursive) throws IOException {
+ this.watcher = FileSystems.getDefault().newWatchService();
+ this.keys = new HashMap<WatchKey,Path>();
+ this.recursive = recursive;
+
+ if (recursive) {
+ System.out.format("Scanning %s ...\n", dir);
+ registerAll(dir);
+ System.out.println("Done.");
+ } else {
+ register(dir);
+ }
+
+ // enable trace after initial registration
+ this.trace = true;
+ }
+
+ /**
+ * Process all events for keys queued to the watcher
+ */
+ void processEvents() {
+ for (;;) {
+
+ // wait for key to be signalled
+ WatchKey key;
+ try {
+ key = watcher.take();
+ } catch (InterruptedException x) {
+ return;
+ }
+
+ Path dir = keys.get(key);
+ if (dir == null) {
+ System.err.println("WatchKey not recognized!!");
+ continue;
+ }
+
+ for (WatchEvent<?> event: key.pollEvents()) {
+ WatchEvent.Kind kind = event.kind();
+
+ // TBD - provide example of how OVERFLOW event is handled
+ if (kind == OVERFLOW) {
+ continue;
+ }
+
+ // Context for directory entry event is the file name of entry
+ WatchEvent<Path> ev = cast(event);
+ Path name = ev.context();
+ Path child = dir.resolve(name);
+
+ // print out event
+ System.out.format("%s: %s\n", event.kind().name(), child);
+
+ // if directory is created, and watching recursively, then
+ // register it and its sub-directories
+ if (recursive && (kind == ENTRY_CREATE)) {
+ try {
+ if (Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).isDirectory()) {
+ registerAll(child);
+ }
+ } catch (IOException x) {
+ // ignore to keep sample readbale
+ }
+ }
+ }
+
+ // reset key and remove from set if directory no longer accessible
+ boolean valid = key.reset();
+ if (!valid) {
+ keys.remove(key);
+
+ // all directories are inaccessible
+ if (keys.isEmpty()) {
+ break;
+ }
+ }
+ }
+ }
+
+ static void usage() {
+ System.err.println("usage: java WatchDir [-r] dir");
+ System.exit(-1);
+ }
+
+ public static void main(String[] args) throws IOException {
+ // parse arguments
+ if (args.length == 0 || args.length > 2)
+ usage();
+ boolean recursive = false;
+ int dirArg = 0;
+ if (args[0].equals("-r")) {
+ if (args.length < 2)
+ usage();
+ recursive = true;
+ dirArg++;
+ }
+
+ // register directory and process its events
+ Path dir = Paths.get(args[dirArg]);
+ new WatchDir(dir, recursive).processEvents();
+ }
+}
diff --git a/src/share/sample/nio/file/Xdd.java b/src/share/sample/nio/file/Xdd.java
new file mode 100644
index 000000000..f66597edd
--- /dev/null
+++ b/src/share/sample/nio/file/Xdd.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+
+/**
+ * Example code to list/set/get/delete the user-defined attributes of a file.
+ */
+
+public class Xdd {
+
+ static void usage() {
+ System.out.println("Usage: java Xdd <file>");
+ System.out.println(" java Xdd -set <name>=<value> <file>");
+ System.out.println(" java Xdd -get <name> <file>");
+ System.out.println(" java Xdd -del <name> <file>");
+ System.exit(-1);
+ }
+
+ public static void main(String[] args) throws IOException {
+ // one or three parameters
+ if (args.length != 1 && args.length != 3)
+ usage();
+
+ Path file = (args.length == 1) ?
+ Paths.get(args[0]) : Paths.get(args[2]);
+
+ // check that user defined attributes are supported by the file system
+ FileStore store = file.getFileStore();
+ if (!store.supportsFileAttributeView("xattr")) {
+ System.err.format("UserDefinedFileAttributeView not supported on %s\n", store);
+ System.exit(-1);
+
+ }
+ UserDefinedFileAttributeView view = file.
+ getFileAttributeView(UserDefinedFileAttributeView.class);
+
+ // list user defined attributes
+ if (args.length == 1) {
+ System.out.println(" Size Name");
+ System.out.println("-------- --------------------------------------");
+ for (String name: view.list()) {
+ System.out.format("%8d %s\n", view.size(name), name);
+ }
+ return;
+ }
+
+ // Add/replace a file's user defined attribute
+ if (args[0].equals("-set")) {
+ // name=value
+ String[] s = args[1].split("=");
+ if (s.length != 2)
+ usage();
+ String name = s[0];
+ String value = s[1];
+ view.write(name, Charset.defaultCharset().encode(value));
+ return;
+ }
+
+ // Print out the value of a file's user defined attribute
+ if (args[0].equals("-get")) {
+ String name = args[1];
+ int size = view.size(name);
+ ByteBuffer buf = ByteBuffer.allocateDirect(size);
+ view.read(name, buf);
+ buf.flip();
+ System.out.println(Charset.defaultCharset().decode(buf).toString());
+ return;
+ }
+
+ // Delete a file's user defined attribute
+ if (args[0].equals("-del")) {
+ view.delete(args[1]);
+ return;
+ }
+
+ // option not recognized
+ usage();
+ }
+ }
diff --git a/src/solaris/classes/sun/awt/X11/WindowDimensions.java b/src/solaris/classes/sun/awt/X11/WindowDimensions.java
index 08511f9e6..70447fee3 100644
--- a/src/solaris/classes/sun/awt/X11/WindowDimensions.java
+++ b/src/solaris/classes/sun/awt/X11/WindowDimensions.java
@@ -32,10 +32,18 @@ class WindowDimensions {
private Insets insets;
private boolean isClientSizeSet;
+ /**
+ * If isClient is true, the bounds represent the client window area.
+ * Otherwise, they represent the entire window area, with the insets included
+ */
public WindowDimensions(int x, int y, int width, int height, boolean isClient) {
this(new Rectangle(x, y, width, height), null, isClient);
}
+ /**
+ * If isClient is true, the bounds represent the client window area.
+ * Otherwise, they represent the entire window area, with the insets included
+ */
public WindowDimensions(Rectangle rec, Insets ins, boolean isClient) {
if (rec == null) {
throw new IllegalArgumentException("Client bounds can't be null");
@@ -46,10 +54,18 @@ class WindowDimensions {
setInsets(ins);
}
+ /**
+ * If isClient is true, the bounds represent the client window area.
+ * Otherwise, they represent the entire window area, with the insets included
+ */
public WindowDimensions(Point loc, Dimension size, Insets in, boolean isClient) {
this(new Rectangle(loc, size), in, isClient);
}
+ /**
+ * If isClient is true, the bounds represent the client window area.
+ * Otherwise, they represent the entire window area, with the insets included
+ */
public WindowDimensions(Rectangle bounds, boolean isClient) {
this(bounds, null, isClient);
}
diff --git a/src/solaris/classes/sun/awt/X11/XBaseWindow.java b/src/solaris/classes/sun/awt/X11/XBaseWindow.java
index f853d5067..ace8b5830 100644
--- a/src/solaris/classes/sun/awt/X11/XBaseWindow.java
+++ b/src/solaris/classes/sun/awt/X11/XBaseWindow.java
@@ -979,8 +979,13 @@ public class XBaseWindow {
*/
public void handleButtonPressRelease(XEvent xev) {
XButtonEvent xbe = xev.get_xbutton();
- final int buttonState = xbe.get_state() & (XConstants.Button1Mask | XConstants.Button2Mask
- | XConstants.Button3Mask | XConstants.Button4Mask | XConstants.Button5Mask);
+ int buttonState = 0;
+ for (int i = 0; i<XToolkit.getNumMouseButtons(); i++){
+ // A bug in WM implementation: extra buttons doesn't have state!=0 as they should on Release message.
+ if ((i != 4) && (i != 5)){
+ buttonState |= (xbe.get_state() & XConstants.buttonsMask[i]);
+ }
+ }
switch (xev.get_type()) {
case XConstants.ButtonPress:
if (buttonState == 0) {
@@ -1011,19 +1016,11 @@ public class XBaseWindow {
* Checks ButtonRelease released all Mouse buttons
*/
static boolean isFullRelease(int buttonState, int button) {
- switch (button) {
- case XConstants.Button1:
- return buttonState == XConstants.Button1Mask;
- case XConstants.Button2:
- return buttonState == XConstants.Button2Mask;
- case XConstants.Button3:
- return buttonState == XConstants.Button3Mask;
- case XConstants.Button4:
- return buttonState == XConstants.Button4Mask;
- case XConstants.Button5:
- return buttonState == XConstants.Button5Mask;
+ if (button < 0 || button > XToolkit.getNumMouseButtons()) {
+ return buttonState == 0;
+ } else {
+ return buttonState == XConstants.buttonsMask[button - 1];
}
- return buttonState == 0;
}
static boolean isGrabbedEvent(XEvent ev, XBaseWindow target) {
diff --git a/src/solaris/classes/sun/awt/X11/XComponentPeer.java b/src/solaris/classes/sun/awt/X11/XComponentPeer.java
index 651d10a29..8a14cfe5f 100644
--- a/src/solaris/classes/sun/awt/X11/XComponentPeer.java
+++ b/src/solaris/classes/sun/awt/X11/XComponentPeer.java
@@ -1534,13 +1534,23 @@ public class XComponentPeer extends XWindow implements ComponentPeer, DropTarget
}
XToolkit.awtLock();
try {
- XlibWrapper.SetRectangularShape(
- XToolkit.getDisplay(),
- getWindow(),
- shape.getLoX(), shape.getLoY(),
- shape.getHiX(), shape.getHiY(),
- (shape.isRectangular() ? null : shape)
- );
+ if (shape != null) {
+ XlibWrapper.SetRectangularShape(
+ XToolkit.getDisplay(),
+ getWindow(),
+ shape.getLoX(), shape.getLoY(),
+ shape.getHiX(), shape.getHiY(),
+ (shape.isRectangular() ? null : shape)
+ );
+ } else {
+ XlibWrapper.SetRectangularShape(
+ XToolkit.getDisplay(),
+ getWindow(),
+ 0, 0,
+ 0, 0,
+ null
+ );
+ }
} finally {
XToolkit.awtUnlock();
}
diff --git a/src/solaris/classes/sun/awt/X11/XConstants.java b/src/solaris/classes/sun/awt/X11/XConstants.java
index e9de6804b..7d9077ee6 100644
--- a/src/solaris/classes/sun/awt/X11/XConstants.java
+++ b/src/solaris/classes/sun/awt/X11/XConstants.java
@@ -197,12 +197,30 @@ final public class XConstants {
/* button masks. Used in same manner as Key masks above. Not to be confused
with button names below. */
-
- public static final int Button1Mask = (1<<8) ;
- public static final int Button2Mask = (1<<9) ;
- public static final int Button3Mask = (1<<10) ;
- public static final int Button4Mask = (1<<11) ;
- public static final int Button5Mask = (1<<12) ;
+ public static final int [] buttonsMask = new int []{ 1<<8,
+ 1<<9,
+ 1<<10,
+ 1<<11,
+ 1<<12,
+ 1<<13,
+ 1<<14,
+ 1<<15,
+ 1<<16,
+ 1<<17,
+ 1<<18,
+ 1<<19,
+ 1<<20,
+ 1<<21,
+ 1<<22,
+ 1<<23,
+ 1<<24,
+ 1<<25,
+ 1<<26,
+ 1<<27,
+ 1<<28,
+ 1<<29,
+ 1<<30,
+ 1<<31 };
public static final int AnyModifier = (1<<15) ; /* used in GrabButton, GrabKey */
@@ -211,11 +229,7 @@ final public class XConstants {
and ButtonRelease events. Not to be confused with button masks above.
Note that 0 is already defined above as "AnyButton". */
- public static final int Button1 = 1 ;
- public static final int Button2 = 2 ;
- public static final int Button3 = 3 ;
- public static final int Button4 = 4 ;
- public static final int Button5 = 5 ;
+ public static final int buttons [] = new int [] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
/* Notify modes */
diff --git a/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java b/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java
index 5984db224..42ccabee3 100644
--- a/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java
+++ b/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java
@@ -492,7 +492,14 @@ abstract class XDecoratedPeer extends XWindowPeer {
// do nothing but accept it.
Rectangle reqBounds = newDimensions.getBounds();
Rectangle newBounds = constrainBounds(reqBounds.x, reqBounds.y, reqBounds.width, reqBounds.height);
- newDimensions = new WindowDimensions(newBounds, newDimensions.getInsets(), newDimensions.isClientSizeSet());
+ Insets insets = newDimensions.getInsets();
+ // Inherit isClientSizeSet from newDimensions
+ if (newDimensions.isClientSizeSet()) {
+ newBounds = new Rectangle(newBounds.x, newBounds.y,
+ newBounds.width - insets.left - insets.right,
+ newBounds.height - insets.top - insets.bottom);
+ }
+ newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet());
}
XToolkit.awtLock();
try {
diff --git a/src/solaris/classes/sun/awt/X11/XDragSourceContextPeer.java b/src/solaris/classes/sun/awt/X11/XDragSourceContextPeer.java
index af3a3219c..a079fdb2b 100644
--- a/src/solaris/classes/sun/awt/X11/XDragSourceContextPeer.java
+++ b/src/solaris/classes/sun/awt/X11/XDragSourceContextPeer.java
@@ -694,8 +694,8 @@ public final class XDragSourceContextPeer
} finally {
xmotion.dispose();
}
- if (xbutton.get_button() == XConstants.Button1
- || xbutton.get_button() == XConstants.Button2) {
+ if (xbutton.get_button() == XConstants.buttons[0]
+ || xbutton.get_button() == XConstants.buttons[1]) {
// drag is initiated with Button1 or Button2 pressed and
// ended on release of either of these buttons (as the same
// behavior was with our old Motif DnD-based implementation)
diff --git a/src/solaris/classes/sun/awt/X11/XEmbedClientHelper.java b/src/solaris/classes/sun/awt/X11/XEmbedClientHelper.java
index 139f375fa..8be239075 100644
--- a/src/solaris/classes/sun/awt/X11/XEmbedClientHelper.java
+++ b/src/solaris/classes/sun/awt/X11/XEmbedClientHelper.java
@@ -31,6 +31,9 @@ import sun.awt.SunToolkit;
import java.awt.Component;
import java.awt.Container;
+import sun.awt.X11GraphicsConfig;
+import sun.awt.X11GraphicsDevice;
+
/**
* Helper class implementing XEmbed protocol handling routines(client side)
* Window which wants to participate in a protocol should create an instance,
@@ -39,20 +42,34 @@ import java.awt.Container;
public class XEmbedClientHelper extends XEmbedHelper implements XEventDispatcher {
private static final Logger xembedLog = Logger.getLogger("sun.awt.X11.xembed.XEmbedClientHelper");
- private XEmbeddedFramePeer embedded;
+ private XEmbeddedFramePeer embedded; // XEmbed client
+ private long server; // XEmbed server
+
private boolean active;
- private long server;
private boolean applicationActive;
XEmbedClientHelper() {
super();
}
- void install(XEmbeddedFramePeer embedded) {
- this.embedded = embedded;
+ void setClient(XEmbeddedFramePeer client) {
+ if (xembedLog.isLoggable(Level.FINE)) {
+ xembedLog.fine("XEmbed client: " + client);
+ }
+ if (embedded != null) {
+ XToolkit.removeEventDispatcher(embedded.getWindow(), this);
+ active = false;
+ }
+ embedded = client;
+ if (embedded != null) {
+ XToolkit.addEventDispatcher(embedded.getWindow(), this);
+ }
+ }
- if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Installing xembedder on " + embedded);
- XToolkit.addEventDispatcher(embedded.getWindow(), this);
+ void install() {
+ if (xembedLog.isLoggable(Level.FINE)) {
+ xembedLog.fine("Installing xembedder on " + embedded);
+ }
long[] info = new long[] { XEMBED_VERSION, XEMBED_MAPPED };
long data = Native.card32ToData(info);
try {
@@ -155,7 +172,24 @@ public class XEmbedClientHelper extends XEmbedHelper implements XEventDispatcher
}
public void handleReparentNotify(XEvent xev) {
XReparentEvent re = xev.get_xreparent();
- server = re.get_parent();
+ long newParent = re.get_parent();
+ if (active) {
+ // unregister accelerators, etc. for old parent
+ embedded.notifyStopped();
+ // check if newParent is a root window
+ X11GraphicsConfig gc = (X11GraphicsConfig)embedded.getGraphicsConfiguration();
+ X11GraphicsDevice gd = (X11GraphicsDevice)gc.getDevice();
+ if ((newParent == XlibUtil.getRootWindow(gd.getScreen())) ||
+ (newParent == XToolkit.getDefaultRootWindow()))
+ {
+ // reparenting to root means XEmbed termination
+ active = false;
+ } else {
+ // continue XEmbed with a new parent
+ server = newParent;
+ embedded.notifyStarted();
+ }
+ }
}
boolean requestFocus() {
if (active && embedded.focusAllowedFor()) {
@@ -201,12 +235,16 @@ public class XEmbedClientHelper extends XEmbedHelper implements XEventDispatcher
}
void registerAccelerator(AWTKeyStroke stroke, int id) {
- long sym = getX11KeySym(stroke);
- long mods = getX11Mods(stroke);
- sendMessage(server, XEMBED_REGISTER_ACCELERATOR, id, sym, mods);
+ if (active) {
+ long sym = getX11KeySym(stroke);
+ long mods = getX11Mods(stroke);
+ sendMessage(server, XEMBED_REGISTER_ACCELERATOR, id, sym, mods);
+ }
}
void unregisterAccelerator(int id) {
- sendMessage(server, XEMBED_UNREGISTER_ACCELERATOR, id, 0, 0);
+ if (active) {
+ sendMessage(server, XEMBED_UNREGISTER_ACCELERATOR, id, 0, 0);
+ }
}
long getX11KeySym(AWTKeyStroke stroke) {
diff --git a/src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java b/src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java
index 90eb7ff8d..892d7c94e 100644
--- a/src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java
+++ b/src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java
@@ -63,7 +63,10 @@ public class XEmbeddedFramePeer extends XFramePeer {
void postInit(XCreateWindowParams params) {
super.postInit(params);
if (embedder != null) {
- embedder.install(this);
+ // install X11 event dispatcher
+ embedder.setClient(this);
+ // reparent to XEmbed server
+ embedder.install();
} else if (getParentWindowHandle() != 0) {
XToolkit.awtLock();
try {
@@ -77,6 +80,15 @@ public class XEmbeddedFramePeer extends XFramePeer {
}
}
+ @Override
+ public void dispose() {
+ if (embedder != null) {
+ // uninstall X11 event dispatcher
+ embedder.setClient(null);
+ }
+ super.dispose();
+ }
+
public void updateMinimumSize() {
}
@@ -249,6 +261,14 @@ public class XEmbeddedFramePeer extends XFramePeer {
// XEmbed.
updateDropTarget();
}
+ void notifyStopped() {
+ if (embedder != null && embedder.isActive()) {
+ for (int i = strokes.size() - 1; i >= 0; i--) {
+ embedder.unregisterAccelerator(i);
+ }
+ }
+ }
+
long getFocusTargetWindow() {
return getWindow();
}
diff --git a/src/solaris/classes/sun/awt/X11/XKeysym.java b/src/solaris/classes/sun/awt/X11/XKeysym.java
index 4dae66c01..9ec3d1c71 100644
--- a/src/solaris/classes/sun/awt/X11/XKeysym.java
+++ b/src/solaris/classes/sun/awt/X11/XKeysym.java
@@ -63,6 +63,8 @@ public class XKeysym {
// TODO: or not to do: add reverse lookup javakeycode2keysym,
// for robot only it seems to me. After that, we can remove lookup table
// from XWindow.c altogether.
+ // Another use for reverse lookup: query keyboard state, for some keys.
+ static Hashtable<Integer, Long> javaKeycode2KeysymHash = new Hashtable<Integer, Long>();
static long keysym_lowercase = unsafe.allocateMemory(Native.getLongSize());
static long keysym_uppercase = unsafe.allocateMemory(Native.getLongSize());
public static char convertKeysym( long ks, int state ) {
@@ -196,6 +198,10 @@ public class XKeysym {
Keysym2JavaKeycode jkc = getJavaKeycode( ev );
return jkc == null ? java.awt.event.KeyEvent.VK_UNDEFINED : jkc.getJavaKeycode();
}
+ static long javaKeycode2Keysym( int jkey ) {
+ Long ks = javaKeycode2KeysymHash.get( jkey );
+ return (ks == null ? 0 : ks.longValue());
+ }
/**
Return keysym derived from a keycode and modifiers.
Usually an input method does this. However non-system input methods (e.g. Java IMs) do not.
@@ -1583,6 +1589,14 @@ public class XKeysym {
keysym2JavaKeycodeHash.put( Long.valueOf(XKeySymConstants.hpXK_mute_asciitilde), new Keysym2JavaKeycode(java.awt.event.KeyEvent.VK_DEAD_TILDE, java.awt.event.KeyEvent.KEY_LOCATION_STANDARD));
keysym2JavaKeycodeHash.put( Long.valueOf(XConstants.NoSymbol), new Keysym2JavaKeycode(java.awt.event.KeyEvent.VK_UNDEFINED, java.awt.event.KeyEvent.KEY_LOCATION_UNKNOWN));
+
+ /* Reverse search of keysym by keycode. */
+
+ /* Add keyboard locking codes. */
+ javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_CAPS_LOCK, XKeySymConstants.XK_Caps_Lock);
+ javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_NUM_LOCK, XKeySymConstants.XK_Num_Lock);
+ javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_SCROLL_LOCK, XKeySymConstants.XK_Scroll_Lock);
+ javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_KANA_LOCK, XKeySymConstants.XK_Kana_Lock);
};
}
diff --git a/src/solaris/classes/sun/awt/X11/XRobotPeer.java b/src/solaris/classes/sun/awt/X11/XRobotPeer.java
index 12084bf1a..2bfe6854f 100644
--- a/src/solaris/classes/sun/awt/X11/XRobotPeer.java
+++ b/src/solaris/classes/sun/awt/X11/XRobotPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2008 Sun Microsystems, Inc. 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
@@ -81,11 +81,16 @@ class XRobotPeer implements RobotPeer {
return pixelArray;
}
+ public int getNumberOfButtons(){
+ return getNumberOfButtonsImpl();
+ }
+
private static native synchronized void setup();
private static native synchronized void mouseMoveImpl(X11GraphicsConfig xgc, int x, int y);
private static native synchronized void mousePressImpl(int buttons);
private static native synchronized void mouseReleaseImpl(int buttons);
+ private static native synchronized int getNumberOfButtonsImpl();
private static native synchronized void mouseWheelImpl(int wheelAmt);
private static native synchronized void keyPressImpl(int keycode);
diff --git a/src/solaris/classes/sun/awt/X11/XToolkit.java b/src/solaris/classes/sun/awt/X11/XToolkit.java
index 9349ee8d1..c3011dfa6 100644
--- a/src/solaris/classes/sun/awt/X11/XToolkit.java
+++ b/src/solaris/classes/sun/awt/X11/XToolkit.java
@@ -27,6 +27,7 @@ package sun.awt.X11;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
+import java.awt.event.KeyEvent;
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragGestureListener;
@@ -61,6 +62,10 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
private static Logger keyEventLog = Logger.getLogger("sun.awt.X11.kye.XToolkit");
private static final Logger backingStoreLog = Logger.getLogger("sun.awt.X11.backingStore.XToolkit");
+ //There is 400 ms is set by default on Windows and 500 by default on KDE and GNOME.
+ //We use the same hardcoded constant.
+ private final static int AWT_MULTICLICK_DEFAULT_TIME = 500;
+
static final boolean PRIMARY_LOOP = false;
static final boolean SECONDARY_LOOP = true;
@@ -74,6 +79,25 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
// Dynamic Layout Resize client code setting
protected static boolean dynamicLayoutSetting = false;
+ //Is it allowed to generate events assigned to extra mouse buttons.
+ //Set to true by default.
+ private static boolean areExtraMouseButtonsEnabled = true;
+
+ /**
+ * Number of buttons.
+ * By default it's taken from the system. If system value does not
+ * fit into int type range, use our own MAX_BUTTONS_SUPPORT value.
+ */
+ private static int numberOfButtons = 0;
+
+ /* XFree standard mention 24 buttons as maximum:
+ * http://www.xfree86.org/current/mouse.4.html
+ * We workaround systems supporting more than 24 buttons.
+ * Otherwise, we have to use long type values as masks
+ * which leads to API change.
+ */
+ private static int MAX_BUTTONS_SUPPORT = 24;
+
/**
* True when the x settings have been loaded.
*/
@@ -273,6 +297,9 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
arrowCursor = XlibWrapper.XCreateFontCursor(XToolkit.getDisplay(),
XCursorFontConstants.XC_arrow);
+ areExtraMouseButtonsEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.enableExtraMouseButtons", "true"));
+ //set system property if not yet assigned
+ System.setProperty("sun.awt.enableExtraMouseButtons", ""+areExtraMouseButtonsEnabled);
} finally {
awtUnlock();
}
@@ -1080,6 +1107,19 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
public Map mapInputMethodHighlight(InputMethodHighlight highlight) {
return XInputMethod.mapInputMethodHighlight(highlight);
}
+ @Override
+ public boolean getLockingKeyState(int key) {
+ if (! (key == KeyEvent.VK_CAPS_LOCK || key == KeyEvent.VK_NUM_LOCK ||
+ key == KeyEvent.VK_SCROLL_LOCK || key == KeyEvent.VK_KANA_LOCK)) {
+ throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState");
+ }
+ awtLock();
+ try {
+ return getModifierState( key );
+ } finally {
+ awtUnlock();
+ }
+ }
public Clipboard getSystemClipboard() {
SecurityManager security = System.getSecurityManager();
@@ -1216,7 +1256,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
String multiclick_time_query = XlibWrapper.XGetDefault(XToolkit.getDisplay(), "*", "multiClickTime");
if (multiclick_time_query != null) {
awt_multiclick_time = (int)Long.parseLong(multiclick_time_query);
- // awt_multiclick_time = XtGetMultiClickTime(awt_display);
} else {
multiclick_time_query = XlibWrapper.XGetDefault(XToolkit.getDisplay(),
"OpenWindows", "MultiClickTimeout");
@@ -1226,20 +1265,19 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
milliseconds */
awt_multiclick_time = (int)Long.parseLong(multiclick_time_query) * 100;
} else {
- awt_multiclick_time = 200;
- // awt_multiclick_time = XtGetMultiClickTime(awt_display);
+ awt_multiclick_time = AWT_MULTICLICK_DEFAULT_TIME;
}
}
} catch (NumberFormatException nf) {
- awt_multiclick_time = 200;
+ awt_multiclick_time = AWT_MULTICLICK_DEFAULT_TIME;
} catch (NullPointerException npe) {
- awt_multiclick_time = 200;
+ awt_multiclick_time = AWT_MULTICLICK_DEFAULT_TIME;
}
} finally {
awtUnlock();
}
if (awt_multiclick_time == 0) {
- awt_multiclick_time = 200;
+ awt_multiclick_time = AWT_MULTICLICK_DEFAULT_TIME;
}
}
@@ -1383,10 +1421,15 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
}
}
- private int getNumMouseButtons() {
+ public static int getNumMouseButtons() {
awtLock();
try {
- return XlibWrapper.XGetPointerMapping(XToolkit.getDisplay(), 0, 0);
+ if (numberOfButtons == 0) {
+ numberOfButtons = Math.min(
+ XlibWrapper.XGetPointerMapping(XToolkit.getDisplay(), 0, 0),
+ MAX_BUTTONS_SUPPORT);
+ }
+ return numberOfButtons;
} finally {
awtUnlock();
}
@@ -1542,6 +1585,66 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
awtUnlock();
}
}
+ static boolean getModifierState( int jkc ) {
+ int iKeyMask = 0;
+ long ks = XKeysym.javaKeycode2Keysym( jkc );
+ int kc = XlibWrapper.XKeysymToKeycode(getDisplay(), ks);
+ if (kc == 0) {
+ return false;
+ }
+ awtLock();
+ try {
+ XModifierKeymap modmap = new XModifierKeymap(
+ XlibWrapper.XGetModifierMapping(getDisplay()));
+
+ int nkeys = modmap.get_max_keypermod();
+
+ long map_ptr = modmap.get_modifiermap();
+ for( int k = 0; k < 8; k++ ) {
+ for (int i = 0; i < nkeys; ++i) {
+ int keycode = Native.getUByte(map_ptr, k * nkeys + i);
+ if (keycode == 0) {
+ continue; // ignore zero keycode
+ }
+ if (kc == keycode) {
+ iKeyMask = 1 << k;
+ break;
+ }
+ }
+ if( iKeyMask != 0 ) {
+ break;
+ }
+ }
+ XlibWrapper.XFreeModifiermap(modmap.pData);
+ if (iKeyMask == 0 ) {
+ return false;
+ }
+ // Now we know to which modifier is assigned the keycode
+ // correspondent to the keysym correspondent to the java
+ // keycode. We are going to check a state of this modifier.
+ // If a modifier is a weird one, we cannot help it.
+ long window = 0;
+ try{
+ // get any application window
+ window = ((Long)(winMap.firstKey())).longValue();
+ }catch(NoSuchElementException nex) {
+ // get root window
+ window = getDefaultRootWindow();
+ }
+ boolean res = XlibWrapper.XQueryPointer(getDisplay(), window,
+ XlibWrapper.larg1, //root
+ XlibWrapper.larg2, //child
+ XlibWrapper.larg3, //root_x
+ XlibWrapper.larg4, //root_y
+ XlibWrapper.larg5, //child_x
+ XlibWrapper.larg6, //child_y
+ XlibWrapper.larg7);//mask
+ int mask = Native.getInt(XlibWrapper.larg7);
+ return ((mask & iKeyMask) != 0);
+ } finally {
+ awtUnlock();
+ }
+ }
/* Assign meaning - alt, meta, etc. - to X modifiers mod1 ... mod5.
* Only consider primary symbols on keycodes attached to modifiers.
@@ -2166,4 +2269,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
}
public static native void setNoisyXErrorHandler();
+
+ public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
+ return areExtraMouseButtonsEnabled;
+ }
}
diff --git a/src/solaris/classes/sun/awt/X11/XWindow.java b/src/solaris/classes/sun/awt/X11/XWindow.java
index 2c894ce1b..5601273d8 100644
--- a/src/solaris/classes/sun/awt/X11/XWindow.java
+++ b/src/solaris/classes/sun/awt/X11/XWindow.java
@@ -553,6 +553,10 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
}
static int getModifiers(int state, int button, int keyCode) {
+ return getModifiers(state, button, keyCode, 0, false);
+ }
+
+ static int getModifiers(int state, int button, int keyCode, int type, boolean wheel_mouse) {
int modifiers = 0;
if (((state & XConstants.ShiftMask) != 0) ^ (keyCode == KeyEvent.VK_SHIFT)) {
@@ -570,14 +574,23 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
if (((state & XToolkit.modeSwitchMask) != 0) ^ (keyCode == KeyEvent.VK_ALT_GRAPH)) {
modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
}
- if (((state & XConstants.Button1Mask) != 0) ^ (button == MouseEvent.BUTTON1)) {
- modifiers |= InputEvent.BUTTON1_DOWN_MASK;
- }
- if (((state & XConstants.Button2Mask) != 0) ^ (button == MouseEvent.BUTTON2)) {
- modifiers |= InputEvent.BUTTON2_DOWN_MASK;
- }
- if (((state & XConstants.Button3Mask) != 0) ^ (button == MouseEvent.BUTTON3)) {
- modifiers |= InputEvent.BUTTON3_DOWN_MASK;
+ //InputEvent.BUTTON_DOWN_MASK array is starting from BUTTON1_DOWN_MASK on index == 0.
+ // button currently reflects a real button number and starts from 1. (except NOBUTTON which is zero )
+
+ /* this is an attempt to refactor button IDs in : MouseEvent, InputEvent, XlibWrapper and XWindow.*/
+
+ //reflects a button number similar to MouseEvent.BUTTON1, 2, 3 etc.
+ for (int i = 0; i < XConstants.buttonsMask.length; i ++){
+ //modifier should be added if :
+ // 1) current button is now still in PRESSED state (means that user just pressed mouse but not released yet) or
+ // 2) if Xsystem reports that "state" represents that button was just released. This only happens on RELEASE with 1,2,3 buttons.
+ // ONLY one of these conditions should be TRUE to add that modifier.
+ if (((state & XConstants.buttonsMask[i]) != 0) != (button == XConstants.buttons[i])){
+ //exclude wheel buttons from adding their numbers as modifiers
+ if (!wheel_mouse) {
+ modifiers |= InputEvent.getMaskForButton(i+1);
+ }
+ }
}
return modifiers;
}
@@ -603,17 +616,6 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
return res;
}
- private static int getButtonMask(long mouseButton) {
- if (mouseButton == XConstants.Button1) {
- return XConstants.Button1Mask;
- } else if (mouseButton == XConstants.Button2) {
- return XConstants.Button2Mask;
- } else if (mouseButton == XConstants.Button3) {
- return XConstants.Button3Mask;
- }
- return 0;
- }
-
/**
* Returns true if this event is disabled and shouldn't be passed to Java.
* Default implementation returns false for all events.
@@ -648,7 +650,7 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
boolean popupTrigger = false;
int button=0;
boolean wheel_mouse = false;
- long lbutton = xbe.get_button();
+ int lbutton = xbe.get_button();
int type = xev.get_type();
when = xbe.get_time();
long jWhen = XToolkit.nowMillisUTC_offset(when);
@@ -663,7 +665,7 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
if (type == XConstants.ButtonPress) {
//Allow this mouse button to generate CLICK event on next ButtonRelease
- mouseButtonClickAllowed |= getButtonMask(lbutton);
+ mouseButtonClickAllowed |= XConstants.buttonsMask[lbutton];
XWindow lastWindow = (lastWindowRef != null) ? ((XWindow)lastWindowRef.get()):(null);
/*
multiclick checking
@@ -693,21 +695,22 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
}
}
- if (lbutton == XConstants.Button1)
- button = MouseEvent.BUTTON1;
- else if (lbutton == XConstants.Button2 )
- button = MouseEvent.BUTTON2;
- else if (lbutton == XConstants.Button3)
- button = MouseEvent.BUTTON3;
- else if (lbutton == XConstants.Button4) {
- button = 4;
- wheel_mouse = true;
- } else if (lbutton == XConstants.Button5) {
- button = 5;
+ button = XConstants.buttons[lbutton - 1];
+ // 4 and 5 buttons are usually considered assigned to a first wheel
+ if (lbutton == XConstants.buttons[3] ||
+ lbutton == XConstants.buttons[4]) {
wheel_mouse = true;
}
- modifiers = getModifiers(xbe.get_state(),button,0);
+ // mapping extra buttons to numbers starting from 4.
+ if ((button > XConstants.buttons[4]) && (!Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled())){
+ return;
+ }
+
+ if (button > XConstants.buttons[4]){
+ button -= 2;
+ }
+ modifiers = getModifiers(xbe.get_state(),button,0, type, wheel_mouse);
if (!wheel_mouse) {
MouseEvent me = new MouseEvent((Component)getEventSource(),
@@ -720,7 +723,7 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
postEventToEventQueue(me);
if ((type == XConstants.ButtonRelease) &&
- ((mouseButtonClickAllowed & getButtonMask(lbutton)) != 0) ) // No up-button in the drag-state
+ ((mouseButtonClickAllowed & XConstants.buttonsMask[lbutton]) != 0) ) // No up-button in the drag-state
{
postEventToEventQueue(me = new MouseEvent((Component)getEventSource(),
MouseEvent.MOUSE_CLICKED,
@@ -750,7 +753,7 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
/* Update the state variable AFTER the CLICKED event post. */
if (type == XConstants.ButtonRelease) {
/* Exclude this mouse button from allowed list.*/
- mouseButtonClickAllowed &= ~getButtonMask(lbutton);
+ mouseButtonClickAllowed &= ~XConstants.buttonsMask[lbutton];
}
}
@@ -761,7 +764,19 @@ public class XWindow extends XBaseWindow implements X11ComponentPeer {
return;
}
- int mouseKeyState = (xme.get_state() & (XConstants.Button1Mask | XConstants.Button2Mask | XConstants.Button3Mask));
+ int mouseKeyState = 0; //(xme.get_state() & (XConstants.buttonsMask[0] | XConstants.buttonsMask[1] | XConstants.buttonsMask[2]));
+
+ //this doesn't work for extra buttons because Xsystem is sending state==0 for every extra button event.
+ // we can't correct it in MouseEvent class as we done it with modifiers, because exact type (DRAG|MOVE)
+ // should be passed from XWindow.
+ //TODO: eliminate it with some other value obtained w/o AWTLock.
+ for (int i = 0; i < XToolkit.getNumMouseButtons(); i++){
+ // TODO : here is the bug in WM: extra buttons doesn't have state!=0 as they should.
+ if ((i != 4) && (i != 5)) {
+ mouseKeyState = mouseKeyState | (xme.get_state() & XConstants.buttonsMask[i]);
+ }
+ }
+
boolean isDragging = (mouseKeyState != 0);
int mouseEventType = 0;
diff --git a/src/solaris/classes/sun/awt/X11/XWindowPeer.java b/src/solaris/classes/sun/awt/X11/XWindowPeer.java
index 77b39c13d..5666cab8c 100644
--- a/src/solaris/classes/sun/awt/X11/XWindowPeer.java
+++ b/src/solaris/classes/sun/awt/X11/XWindowPeer.java
@@ -1936,7 +1936,13 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
new Object[] {xme, isGrabbed(), containsGlobal(xme.get_x_root(), xme.get_y_root())});
}
if (isGrabbed()) {
- boolean dragging = (xme.get_state() & (XConstants.Button1Mask | XConstants.Button2Mask | XConstants.Button3Mask)) != 0;
+ boolean dragging = false;
+ for (int i = 0; i<XToolkit.getNumMouseButtons(); i++){
+ // here is the bug in WM: extra buttons doesn't have state!=0 as they should.
+ if ((i != 4) && (i != 5)){
+ dragging = dragging || ((xme.get_state() & XConstants.buttonsMask[i]) != 0);
+ }
+ }
// When window is grabbed, all events are dispatched to
// it. Retarget them to the corresponding windows (notice
// that XBaseWindow.dispatchEvent does the opposite
@@ -1990,12 +1996,12 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
try {
grabLog.log(Level.FINER, " - Grab event target {0} (press target {1})", new Object[] {target, pressTarget});
if (xbe.get_type() == XConstants.ButtonPress
- && xbe.get_button() == XConstants.Button1)
+ && xbe.get_button() == XConstants.buttons[0])
{
// need to keep it to retarget mouse release
pressTarget = target;
} else if (xbe.get_type() == XConstants.ButtonRelease
- && xbe.get_button() == XConstants.Button1
+ && xbe.get_button() == XConstants.buttons[0]
&& pressTarget != target)
{
// during grab we do receive mouse release on different component (not on the source
diff --git a/src/solaris/classes/sun/awt/X11/XlibWrapper.java b/src/solaris/classes/sun/awt/X11/XlibWrapper.java
index 50be5f54d..eb6d5e775 100644
--- a/src/solaris/classes/sun/awt/X11/XlibWrapper.java
+++ b/src/solaris/classes/sun/awt/X11/XlibWrapper.java
@@ -485,6 +485,7 @@ static native String XSetLocaleModifiers(String modifier_list);
static native int XdbeEndIdiom(long display);
static native int XdbeSwapBuffers(long display, long swap_info, int num_windows);
+ static native void XQueryKeymap(long display, long vector);
static native long XKeycodeToKeysym(long display, int keycode, int index);
static native int XKeysymToKeycode(long display, long keysym);
diff --git a/src/solaris/classes/sun/awt/X11/keysym2ucs.h b/src/solaris/classes/sun/awt/X11/keysym2ucs.h
index c5750d8d1..c59ffde04 100644
--- a/src/solaris/classes/sun/awt/X11/keysym2ucs.h
+++ b/src/solaris/classes/sun/awt/X11/keysym2ucs.h
@@ -101,6 +101,8 @@ tojava static Hashtable<Long, Long> uppercaseHash = new Hashtable<Long, Long
tojava // TODO: or not to do: add reverse lookup javakeycode2keysym,
tojava // for robot only it seems to me. After that, we can remove lookup table
tojava // from XWindow.c altogether.
+tojava // Another use for reverse lookup: query keyboard state, for some keys.
+tojava static Hashtable<Integer, Long> javaKeycode2KeysymHash = new Hashtable<Integer, Long>();
tojava static long keysym_lowercase = unsafe.allocateMemory(Native.getLongSize());
tojava static long keysym_uppercase = unsafe.allocateMemory(Native.getLongSize());
tojava public static char convertKeysym( long ks, int state ) {
@@ -234,6 +236,10 @@ tojava static int getJavaKeycodeOnly( XKeyEvent ev ) {
tojava Keysym2JavaKeycode jkc = getJavaKeycode( ev );
tojava return jkc == null ? java.awt.event.KeyEvent.VK_UNDEFINED : jkc.getJavaKeycode();
tojava }
+tojava static long javaKeycode2Keysym( int jkey ) {
+tojava Long ks = javaKeycode2KeysymHash.get( jkey );
+tojava return (ks == null ? 0 : ks.longValue());
+tojava }
tojava /**
tojava Return keysym derived from a keycode and modifiers.
tojava Usually an input method does this. However non-system input methods (e.g. Java IMs) do not.
@@ -2634,6 +2640,14 @@ tojava keysym2JavaKeycodeHash.put( Long.valueOf(XKeySymConstants.hpXK_mu
tojava keysym2JavaKeycodeHash.put( Long.valueOf(XKeySymConstants.hpXK_mute_asciitilde), new Keysym2JavaKeycode(java.awt.event.KeyEvent.VK_DEAD_TILDE, java.awt.event.KeyEvent.KEY_LOCATION_STANDARD));
tojava
tojava keysym2JavaKeycodeHash.put( Long.valueOf(XConstants.NoSymbol), new Keysym2JavaKeycode(java.awt.event.KeyEvent.VK_UNDEFINED, java.awt.event.KeyEvent.KEY_LOCATION_UNKNOWN));
+tojava
+tojava /* Reverse search of keysym by keycode. */
+tojava
+tojava /* Add keyboard locking codes. */
+tojava javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_CAPS_LOCK, XKeySymConstants.XK_Caps_Lock);
+tojava javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_NUM_LOCK, XKeySymConstants.XK_Num_Lock);
+tojava javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_SCROLL_LOCK, XKeySymConstants.XK_Scroll_Lock);
+tojava javaKeycode2KeysymHash.put( java.awt.event.KeyEvent.VK_KANA_LOCK, XKeySymConstants.XK_Kana_Lock);
tojava };
tojava
tojava }
diff --git a/src/solaris/classes/sun/nio/ch/DatagramDispatcher.java b/src/solaris/classes/sun/nio/ch/DatagramDispatcher.java
index 8ac3e1e8d..9d0a1d1d7 100644
--- a/src/solaris/classes/sun/nio/ch/DatagramDispatcher.java
+++ b/src/solaris/classes/sun/nio/ch/DatagramDispatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -56,11 +56,11 @@ class DatagramDispatcher extends NativeDispatcher
}
void close(FileDescriptor fd) throws IOException {
- FileDispatcher.close0(fd);
+ FileDispatcherImpl.close0(fd);
}
void preClose(FileDescriptor fd) throws IOException {
- FileDispatcher.preClose0(fd);
+ FileDispatcherImpl.preClose0(fd);
}
static native int read0(FileDescriptor fd, long address, int len)
diff --git a/src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
new file mode 100644
index 000000000..949c817ff
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * Creates this platform's default asynchronous channel provider
+ */
+
+public class DefaultAsynchronousChannelProvider {
+
+ /**
+ * Prevent instantiation.
+ */
+ private DefaultAsynchronousChannelProvider() { }
+
+ /**
+ * Returns the default AsynchronousChannelProvider.
+ */
+ public static AsynchronousChannelProvider create() {
+ String osname = AccessController
+ .doPrivileged(new GetPropertyAction("os.name"));
+ if (osname.equals("SunOS"))
+ return new SolarisAsynchronousChannelProvider();
+ if (osname.equals("Linux"))
+ return new LinuxAsynchronousChannelProvider();
+ throw new InternalError("platform not recognized");
+ }
+
+}
diff --git a/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java b/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java
index 7317a8eab..f569c2dbe 100644
--- a/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java
+++ b/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -172,7 +172,7 @@ class DevPollArrayWrapper {
}
void closeDevPollFD() throws IOException {
- FileDispatcher.closeIntFD(wfd);
+ FileDispatcherImpl.closeIntFD(wfd);
pollArray.free();
}
diff --git a/src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java b/src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java
index cdf19cda2..f3f26e48e 100644
--- a/src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java
+++ b/src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -139,8 +139,8 @@ class DevPollSelectorImpl
interruptTriggered = true;
}
- FileDispatcher.closeIntFD(fd0);
- FileDispatcher.closeIntFD(fd1);
+ FileDispatcherImpl.closeIntFD(fd0);
+ FileDispatcherImpl.closeIntFD(fd1);
pollWrapper.release(fd0);
pollWrapper.closeDevPollFD();
diff --git a/src/solaris/classes/sun/nio/ch/EPoll.java b/src/solaris/classes/sun/nio/ch/EPoll.java
new file mode 100644
index 000000000..065dced7a
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/EPoll.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+/**
+ * Provides access to the Linux epoll facility.
+ */
+
+class EPoll {
+ private EPoll() { }
+
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ /**
+ * typedef union epoll_data {
+ * void *ptr;
+ * int fd;
+ * __uint32_t u32;
+ * __uint64_t u64;
+ * } epoll_data_t;
+ *
+ * struct epoll_event {
+ * __uint32_t events;
+ * epoll_data_t data;
+ * }
+ */
+ private static final int SIZEOF_EPOLLEVENT = eventSize();
+ private static final int OFFSETOF_EVENTS = eventsOffset();
+ private static final int OFFSETOF_FD = dataOffset();
+
+ // opcodes
+ static final int EPOLL_CTL_ADD = 1;
+ static final int EPOLL_CTL_DEL = 2;
+ static final int EPOLL_CTL_MOD = 3;
+
+ // flags
+ static final int EPOLLONESHOT = (1 << 30);
+
+ /**
+ * Allocates a poll array to handle up to {@code count} events.
+ */
+ static long allocatePollArray(int count) {
+ return unsafe.allocateMemory(count * SIZEOF_EPOLLEVENT);
+ }
+
+ /**
+ * Free a poll array
+ */
+ static void freePollArray(long address) {
+ unsafe.freeMemory(address);
+ }
+
+ /**
+ * Returns event[i];
+ */
+ static long getEvent(long address, int i) {
+ return address + (SIZEOF_EPOLLEVENT*i);
+ }
+
+ /**
+ * Returns event->data.fd
+ */
+ static int getDescriptor(long eventAddress) {
+ return unsafe.getInt(eventAddress + OFFSETOF_FD);
+ }
+
+ /**
+ * Returns event->events
+ */
+ static int getEvents(long eventAddress) {
+ return unsafe.getInt(eventAddress + OFFSETOF_EVENTS);
+ }
+
+ // -- Native methods --
+
+ private static native void init();
+
+ private static native int eventSize();
+
+ private static native int eventsOffset();
+
+ private static native int dataOffset();
+
+ static native int epollCreate() throws IOException;
+
+ static native int epollCtl(int epfd, int opcode, int fd, int events);
+
+ static native int epollWait(int epfd, long pollAddress, int numfds)
+ throws IOException;
+
+ static {
+ Util.load();
+ init();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java b/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java
index d8542e1b0..362bd9708 100644
--- a/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java
+++ b/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc. 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
@@ -224,7 +224,7 @@ class EPollArrayWrapper {
* Close epoll file descriptor and free poll array
*/
void closeEPollFD() throws IOException {
- FileDispatcher.closeIntFD(epfd);
+ FileDispatcherImpl.closeIntFD(epfd);
pollArray.free();
}
diff --git a/src/solaris/classes/sun/nio/ch/EPollPort.java b/src/solaris/classes/sun/nio/ch/EPollPort.java
new file mode 100644
index 000000000..a79cb7318
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/EPollPort.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.IOException;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import static sun.nio.ch.EPoll.*;
+
+/**
+ * AsynchronousChannelGroup implementation based on the Linux epoll facility.
+ */
+
+final class EPollPort
+ extends Port
+{
+ // maximum number of events to poll at a time
+ private static final int MAX_EPOLL_EVENTS = 512;
+
+ // errors
+ private static final int ENOENT = 2;
+
+ // epoll file descriptor
+ private final int epfd;
+
+ // true if epoll closed
+ private boolean closed;
+
+ // socket pair used for wakeup
+ private final int sp[];
+
+ // number of wakeups pending
+ private final AtomicInteger wakeupCount = new AtomicInteger();
+
+ // address of the poll array passed to epoll_wait
+ private final long address;
+
+ // encapsulates an event for a channel
+ static class Event {
+ final PollableChannel channel;
+ final int events;
+
+ Event(PollableChannel channel, int events) {
+ this.channel = channel;
+ this.events = events;
+ }
+
+ PollableChannel channel() { return channel; }
+ int events() { return events; }
+ }
+
+ // queue of events for cases that a polling thread dequeues more than one
+ // event
+ private final ArrayBlockingQueue<Event> queue;
+ private final Event NEED_TO_POLL = new Event(null, 0);
+ private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
+
+ EPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
+ throws IOException
+ {
+ super(provider, pool);
+
+ // open epoll
+ this.epfd = epollCreate();
+
+ // create socket pair for wakeup mechanism
+ int[] sv = new int[2];
+ try {
+ socketpair(sv);
+ // register one end with epoll
+ epollCtl(epfd, EPOLL_CTL_ADD, sv[0], POLLIN);
+ } catch (IOException x) {
+ close0(epfd);
+ throw x;
+ }
+ this.sp = sv;
+
+ // allocate the poll array
+ this.address = allocatePollArray(MAX_EPOLL_EVENTS);
+
+ // create the queue and offer the special event to ensure that the first
+ // threads polls
+ this.queue = new ArrayBlockingQueue<Event>(MAX_EPOLL_EVENTS);
+ this.queue.offer(NEED_TO_POLL);
+ }
+
+ EPollPort start() {
+ startThreads(new EventHandlerTask());
+ return this;
+ }
+
+ /**
+ * Release all resources
+ */
+ private void implClose() {
+ synchronized (this) {
+ if (closed)
+ return;
+ closed = true;
+ }
+ freePollArray(address);
+ close0(sp[0]);
+ close0(sp[1]);
+ close0(epfd);
+ }
+
+ private void wakeup() {
+ if (wakeupCount.incrementAndGet() == 1) {
+ // write byte to socketpair to force wakeup
+ try {
+ interrupt(sp[1]);
+ } catch (IOException x) {
+ throw new AssertionError(x);
+ }
+ }
+ }
+
+ @Override
+ void executeOnHandlerTask(Runnable task) {
+ synchronized (this) {
+ if (closed)
+ throw new RejectedExecutionException();
+ offerTask(task);
+ wakeup();
+ }
+ }
+
+ @Override
+ void shutdownHandlerTasks() {
+ /*
+ * If no tasks are running then just release resources; otherwise
+ * write to the one end of the socketpair to wakeup any polling threads.
+ */
+ int nThreads = threadCount();
+ if (nThreads == 0) {
+ implClose();
+ } else {
+ // send interrupt to each thread
+ while (nThreads-- > 0) {
+ wakeup();
+ }
+ }
+ }
+
+ // invoke by clients to register a file descriptor
+ @Override
+ void startPoll(int fd, int events) {
+ // update events (or add to epoll on first usage)
+ int err = epollCtl(epfd, EPOLL_CTL_MOD, fd, (events | EPOLLONESHOT));
+ if (err == ENOENT)
+ err = epollCtl(epfd, EPOLL_CTL_ADD, fd, (events | EPOLLONESHOT));
+ if (err != 0)
+ throw new AssertionError(); // should not happen
+ }
+
+ /*
+ * Task to process events from epoll and dispatch to the channel's
+ * onEvent handler.
+ *
+ * Events are retreived from epoll in batch and offered to a BlockingQueue
+ * where they are consumed by handler threads. A special "NEED_TO_POLL"
+ * event is used to signal one consumer to re-poll when all events have
+ * been consumed.
+ */
+ private class EventHandlerTask implements Runnable {
+ private Event poll() throws IOException {
+ try {
+ for (;;) {
+ int n = epollWait(epfd, address, MAX_EPOLL_EVENTS);
+ /*
+ * 'n' events have been read. Here we map them to their
+ * corresponding channel in batch and queue n-1 so that
+ * they can be handled by other handler threads. The last
+ * event is handled by this thread (and so is not queued).
+ */
+ fdToChannelLock.readLock().lock();
+ try {
+ while (n-- > 0) {
+ long eventAddress = getEvent(address, n);
+ int fd = getDescriptor(eventAddress);
+
+ // wakeup
+ if (fd == sp[0]) {
+ if (wakeupCount.decrementAndGet() == 0) {
+ // no more wakeups so drain pipe
+ drain1(sp[0]);
+ }
+
+ // queue special event if there are more events
+ // to handle.
+ if (n > 0) {
+ queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
+ continue;
+ }
+ return EXECUTE_TASK_OR_SHUTDOWN;
+ }
+
+ PollableChannel channel = fdToChannel.get(fd);
+ if (channel != null) {
+ int events = getEvents(eventAddress);
+ Event ev = new Event(channel, events);
+
+ // n-1 events are queued; This thread handles
+ // the last one except for the wakeup
+ if (n > 0) {
+ queue.offer(ev);
+ } else {
+ return ev;
+ }
+ }
+ }
+ } finally {
+ fdToChannelLock.readLock().unlock();
+ }
+ }
+ } finally {
+ // to ensure that some thread will poll when all events have
+ // been consumed
+ queue.offer(NEED_TO_POLL);
+ }
+ }
+
+ public void run() {
+ Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+ Invoker.getGroupAndInvokeCount();
+ boolean replaceMe = false;
+ Event ev;
+ try {
+ for (;;) {
+ // reset invoke count
+ if (myGroupAndInvokeCount != null)
+ myGroupAndInvokeCount.resetInvokeCount();
+
+ try {
+ replaceMe = false;
+ ev = queue.take();
+
+ // no events and this thread has been "selected" to
+ // poll for more.
+ if (ev == NEED_TO_POLL) {
+ try {
+ ev = poll();
+ } catch (IOException x) {
+ x.printStackTrace();
+ return;
+ }
+ }
+ } catch (InterruptedException x) {
+ continue;
+ }
+
+ // handle wakeup to execute task or shutdown
+ if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
+ Runnable task = pollTask();
+ if (task == null) {
+ // shutdown request
+ return;
+ }
+ // run task (may throw error/exception)
+ replaceMe = true;
+ task.run();
+ continue;
+ }
+
+ // process event
+ try {
+ ev.channel().onEvent(ev.events());
+ } catch (Error x) {
+ replaceMe = true; throw x;
+ } catch (RuntimeException x) {
+ replaceMe = true; throw x;
+ }
+ }
+ } finally {
+ // last handler to exit when shutdown releases resources
+ int remaining = threadExit(this, replaceMe);
+ if (remaining == 0 && isShutdown()) {
+ implClose();
+ }
+ }
+ }
+ }
+
+ // -- Native methods --
+
+ private static native void socketpair(int[] sv) throws IOException;
+
+ private static native void interrupt(int fd) throws IOException;
+
+ private static native void drain1(int fd) throws IOException;
+
+ private static native void close0(int fd);
+
+ static {
+ Util.load();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java b/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java
index 5dc17bb01..a9bf82353 100644
--- a/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java
+++ b/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc. 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
@@ -136,8 +136,8 @@ class EPollSelectorImpl
interruptTriggered = true;
}
- FileDispatcher.closeIntFD(fd0);
- FileDispatcher.closeIntFD(fd1);
+ FileDispatcherImpl.closeIntFD(fd0);
+ FileDispatcherImpl.closeIntFD(fd1);
pollWrapper.release(fd0);
pollWrapper.closeEPollFD();
diff --git a/src/solaris/classes/sun/nio/ch/FileDispatcher.java b/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java
index 335ac49b9..34d3451bb 100644
--- a/src/solaris/classes/sun/nio/ch/FileDispatcher.java
+++ b/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -27,12 +27,7 @@ package sun.nio.ch;
import java.io.*;
-/**
- * Allows different platforms to call different native methods
- * for read and write operations.
- */
-
-class FileDispatcher extends NativeDispatcher
+class FileDispatcherImpl extends FileDispatcher
{
static {
@@ -69,6 +64,28 @@ class FileDispatcher extends NativeDispatcher
return writev0(fd, address, len);
}
+ int force(FileDescriptor fd, boolean metaData) throws IOException {
+ return force0(fd, metaData);
+ }
+
+ int truncate(FileDescriptor fd, long size) throws IOException {
+ return truncate0(fd, size);
+ }
+
+ long size(FileDescriptor fd) throws IOException {
+ return size0(fd);
+ }
+
+ int lock(FileDescriptor fd, boolean blocking, long pos, long size,
+ boolean shared) throws IOException
+ {
+ return lock0(fd, blocking, pos, size, shared);
+ }
+
+ void release(FileDescriptor fd, long pos, long size) throws IOException {
+ release0(fd, pos, size);
+ }
+
void close(FileDescriptor fd) throws IOException {
close0(fd);
}
@@ -97,6 +114,20 @@ class FileDispatcher extends NativeDispatcher
static native long writev0(FileDescriptor fd, long address, int len)
throws IOException;
+ static native int force0(FileDescriptor fd, boolean metaData)
+ throws IOException;
+
+ static native int truncate0(FileDescriptor fd, long size)
+ throws IOException;
+
+ static native long size0(FileDescriptor fd) throws IOException;
+
+ static native int lock0(FileDescriptor fd, boolean blocking, long pos,
+ long size, boolean shared) throws IOException;
+
+ static native void release0(FileDescriptor fd, long pos, long size)
+ throws IOException;
+
static native void close0(FileDescriptor fd) throws IOException;
static native void preClose0(FileDescriptor fd) throws IOException;
diff --git a/src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java b/src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java
new file mode 100644
index 000000000..775380e8b
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.net.ProtocolFamily;
+import java.io.IOException;
+
+public class LinuxAsynchronousChannelProvider
+ extends AsynchronousChannelProvider
+{
+ private static volatile EPollPort defaultPort;
+
+ private EPollPort defaultEventPort() throws IOException {
+ if (defaultPort == null) {
+ synchronized (LinuxAsynchronousChannelProvider.class) {
+ if (defaultPort == null) {
+ defaultPort = new EPollPort(this, ThreadPool.getDefault()).start();
+ }
+ }
+ }
+ return defaultPort;
+ }
+
+ public LinuxAsynchronousChannelProvider() {
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
+ throws IOException
+ {
+ return new EPollPort(this, ThreadPool.create(nThreads, factory)).start();
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
+ throws IOException
+ {
+ return new EPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
+ }
+
+ private Port toPort(AsynchronousChannelGroup group) throws IOException {
+ if (group == null) {
+ return defaultEventPort();
+ } else {
+ if (!(group instanceof EPollPort))
+ throw new IllegalChannelGroupException();
+ return (Port)group;
+ }
+ }
+
+ @Override
+ public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
+ }
+
+ @Override
+ public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new UnixAsynchronousSocketChannelImpl(toPort(group));
+ }
+
+ @Override
+ public AsynchronousDatagramChannel openAsynchronousDatagramChannel(ProtocolFamily family,
+ AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new SimpleAsynchronousDatagramChannelImpl(family, toPort(group));
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/PollSelectorImpl.java b/src/solaris/classes/sun/nio/ch/PollSelectorImpl.java
index ab135c9b1..05fbda57d 100644
--- a/src/solaris/classes/sun/nio/ch/PollSelectorImpl.java
+++ b/src/solaris/classes/sun/nio/ch/PollSelectorImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2009 Sun Microsystems, Inc. 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
@@ -93,8 +93,8 @@ class PollSelectorImpl
synchronized (interruptLock) {
interruptTriggered = true;
}
- FileDispatcher.closeIntFD(fd0);
- FileDispatcher.closeIntFD(fd1);
+ FileDispatcherImpl.closeIntFD(fd0);
+ FileDispatcherImpl.closeIntFD(fd1);
fd0 = -1;
fd1 = -1;
pollWrapper.release(0);
diff --git a/src/solaris/classes/sun/nio/ch/Port.java b/src/solaris/classes/sun/nio/ch/Port.java
new file mode 100644
index 000000000..8b19637bc
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/Port.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.nio.channels.*;
+import java.io.IOException;
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Base implementation of AsynchronousChannelGroupImpl for Unix systems.
+ */
+
+abstract class Port extends AsynchronousChannelGroupImpl {
+ static final short POLLIN = 0x0001;
+ static final short POLLOUT = 0x0004;
+ static final short POLLERR = 0x0008;
+ static final short POLLHUP = 0x0010;
+
+ /**
+ * Implemented by clients registered with this port.
+ */
+ interface PollableChannel extends Closeable {
+ void onEvent(int events);
+ }
+
+ // maps fd to "pollable" channel
+ protected final ReadWriteLock fdToChannelLock = new ReentrantReadWriteLock();
+ protected final Map<Integer,PollableChannel> fdToChannel =
+ new HashMap<Integer,PollableChannel>();
+
+
+ Port(AsynchronousChannelProvider provider, ThreadPool pool) {
+ super(provider, pool);
+ }
+
+ /**
+ * Register channel identified by its file descriptor
+ */
+ final void register(int fd, PollableChannel ch) {
+ fdToChannelLock.writeLock().lock();
+ try {
+ if (isShutdown())
+ throw new ShutdownChannelGroupException();
+ fdToChannel.put(Integer.valueOf(fd), ch);
+ } finally {
+ fdToChannelLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Unregister channel identified by its file descriptor
+ */
+ final void unregister(int fd) {
+ boolean checkForShutdown = false;
+
+ fdToChannelLock.writeLock().lock();
+ try {
+ fdToChannel.remove(Integer.valueOf(fd));
+
+ // last key to be removed so check if group is shutdown
+ if (fdToChannel.isEmpty())
+ checkForShutdown = true;
+
+ } finally {
+ fdToChannelLock.writeLock().unlock();
+ }
+
+ // continue shutdown
+ if (checkForShutdown && isShutdown()) {
+ try {
+ shutdownNow();
+ } catch (IOException ignore) { }
+ }
+ }
+ /**
+ * Register file descriptor with polling mechanism for given events.
+ * The implementation should translate the events as required.
+ */
+ abstract void startPoll(int fd, int events);
+
+ @Override
+ final boolean isEmpty() {
+ fdToChannelLock.writeLock().lock();
+ try {
+ return fdToChannel.isEmpty();
+ } finally {
+ fdToChannelLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ final Object attachForeignChannel(final Channel channel, FileDescriptor fd) {
+ int fdVal = IOUtil.fdVal(fd);
+ register(fdVal, new PollableChannel() {
+ public void onEvent(int events) { }
+ public void close() throws IOException {
+ channel.close();
+ }
+ });
+ return Integer.valueOf(fdVal);
+ }
+
+ @Override
+ final void detachForeignChannel(Object key) {
+ unregister((Integer)key);
+ }
+
+ @Override
+ final void closeAllChannels() {
+ /**
+ * Close channels in batches of up to 128 channels. This allows close
+ * to remove the channel from the map without interference.
+ */
+ final int MAX_BATCH_SIZE = 128;
+ PollableChannel channels[] = new PollableChannel[MAX_BATCH_SIZE];
+ int count;
+ do {
+ // grab a batch of up to 128 channels
+ fdToChannelLock.writeLock().lock();
+ count = 0;
+ try {
+ for (Integer fd: fdToChannel.keySet()) {
+ channels[count++] = fdToChannel.get(fd);
+ if (count >= MAX_BATCH_SIZE)
+ break;
+ }
+ } finally {
+ fdToChannelLock.writeLock().unlock();
+ }
+
+ // close them
+ for (int i=0; i<count; i++) {
+ try {
+ channels[i].close();
+ } catch (IOException ignore) { }
+ }
+ } while (count > 0);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java b/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java
index bc58ba9ab..ced3b608e 100644
--- a/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java
+++ b/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -208,7 +208,7 @@ class SinkChannelImpl
static {
Util.load();
- nd = new FileDispatcher();
+ nd = new FileDispatcherImpl();
}
}
diff --git a/src/solaris/classes/sun/nio/ch/SocketDispatcher.java b/src/solaris/classes/sun/nio/ch/SocketDispatcher.java
index 46c8e135e..fc884c4c9 100644
--- a/src/solaris/classes/sun/nio/ch/SocketDispatcher.java
+++ b/src/solaris/classes/sun/nio/ch/SocketDispatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -36,26 +36,26 @@ class SocketDispatcher extends NativeDispatcher
{
int read(FileDescriptor fd, long address, int len) throws IOException {
- return FileDispatcher.read0(fd, address, len);
+ return FileDispatcherImpl.read0(fd, address, len);
}
long readv(FileDescriptor fd, long address, int len) throws IOException {
- return FileDispatcher.readv0(fd, address, len);
+ return FileDispatcherImpl.readv0(fd, address, len);
}
int write(FileDescriptor fd, long address, int len) throws IOException {
- return FileDispatcher.write0(fd, address, len);
+ return FileDispatcherImpl.write0(fd, address, len);
}
long writev(FileDescriptor fd, long address, int len) throws IOException {
- return FileDispatcher.writev0(fd, address, len);
+ return FileDispatcherImpl.writev0(fd, address, len);
}
void close(FileDescriptor fd) throws IOException {
- FileDispatcher.close0(fd);
+ FileDispatcherImpl.close0(fd);
}
void preClose(FileDescriptor fd) throws IOException {
- FileDispatcher.preClose0(fd);
+ FileDispatcherImpl.preClose0(fd);
}
}
diff --git a/src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java b/src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java
new file mode 100644
index 000000000..0da0935ca
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.net.ProtocolFamily;
+import java.io.IOException;
+
+public class SolarisAsynchronousChannelProvider
+ extends AsynchronousChannelProvider
+{
+ private static volatile SolarisEventPort defaultEventPort;
+
+ private SolarisEventPort defaultEventPort() throws IOException {
+ if (defaultEventPort == null) {
+ synchronized (SolarisAsynchronousChannelProvider.class) {
+ if (defaultEventPort == null) {
+ defaultEventPort =
+ new SolarisEventPort(this, ThreadPool.getDefault()).start();
+ }
+ }
+ }
+ return defaultEventPort;
+ }
+
+ public SolarisAsynchronousChannelProvider() {
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
+ throws IOException
+ {
+ return new SolarisEventPort(this, ThreadPool.create(nThreads, factory)).start();
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
+ throws IOException
+ {
+ return new SolarisEventPort(this, ThreadPool.wrap(executor, initialSize)).start();
+ }
+
+ private SolarisEventPort toEventPort(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ if (group == null) {
+ return defaultEventPort();
+ } else {
+ if (!(group instanceof SolarisEventPort))
+ throw new IllegalChannelGroupException();
+ return (SolarisEventPort)group;
+ }
+ }
+
+ @Override
+ public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new UnixAsynchronousServerSocketChannelImpl(toEventPort(group));
+ }
+
+ @Override
+ public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new UnixAsynchronousSocketChannelImpl(toEventPort(group));
+ }
+
+ @Override
+ public AsynchronousDatagramChannel openAsynchronousDatagramChannel(ProtocolFamily family,
+ AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new SimpleAsynchronousDatagramChannelImpl(family, toEventPort(group));
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/SolarisEventPort.java b/src/solaris/classes/sun/nio/ch/SolarisEventPort.java
new file mode 100644
index 000000000..908eecce0
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/SolarisEventPort.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.RejectedExecutionException;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+/**
+ * AsynchronousChannelGroup implementation based on the Solaris 10 event port
+ * framework.
+ */
+
+class SolarisEventPort
+ extends Port
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static final int addressSize = unsafe.addressSize();
+
+ private static int dependsArch(int value32, int value64) {
+ return (addressSize == 4) ? value32 : value64;
+ }
+
+ /*
+ * typedef struct port_event {
+ * int portev_events;
+ * ushort_t portev_source;
+ * ushort_t portev_pad;
+ * uintptr_t portev_object;
+ * void *portev_user;
+ * } port_event_t;
+ */
+ private static final int SIZEOF_PORT_EVENT = dependsArch(16, 24);
+ private static final int OFFSETOF_EVENTS = 0;
+ private static final int OFFSETOF_SOURCE = 4;
+ private static final int OFFSETOF_OBJECT = 8;
+
+ // port sources
+ private static final short PORT_SOURCE_USER = 3;
+ private static final short PORT_SOURCE_FD = 4;
+
+ // file descriptor to event port.
+ private final int port;
+
+ // true when port is closed
+ private boolean closed;
+
+ SolarisEventPort(AsynchronousChannelProvider provider, ThreadPool pool)
+ throws IOException
+ {
+ super(provider, pool);
+
+ // create event port
+ this.port = portCreate();
+ }
+
+ SolarisEventPort start() {
+ startThreads(new EventHandlerTask());
+ return this;
+ }
+
+ // releass resources
+ private void implClose() {
+ synchronized (this) {
+ if (closed)
+ return;
+ closed = true;
+ }
+ portClose(port);
+ }
+
+ private void wakeup() {
+ try {
+ portSend(port, 0);
+ } catch (IOException x) {
+ throw new AssertionError(x);
+ }
+ }
+
+ @Override
+ void executeOnHandlerTask(Runnable task) {
+ synchronized (this) {
+ if (closed)
+ throw new RejectedExecutionException();
+ offerTask(task);
+ wakeup();
+ }
+ }
+
+ @Override
+ void shutdownHandlerTasks() {
+ /*
+ * If no tasks are running then just release resources; otherwise
+ * write to the one end of the socketpair to wakeup any polling threads..
+ */
+ int nThreads = threadCount();
+ if (nThreads == 0) {
+ implClose();
+ } else {
+ // send user event to wakeup each thread
+ while (nThreads-- > 0) {
+ try {
+ portSend(port, 0);
+ } catch (IOException x) {
+ throw new AssertionError(x);
+ }
+ }
+ }
+ }
+
+ @Override
+ void startPoll(int fd, int events) {
+ // (re-)associate file descriptor
+ // no need to translate events
+ try {
+ portAssociate(port, PORT_SOURCE_FD, fd, events);
+ } catch (IOException x) {
+ throw new AssertionError(); // should not happen
+ }
+ }
+
+ /*
+ * Task to read a single event from the port and dispatch it to the
+ * channel's onEvent handler.
+ */
+ private class EventHandlerTask implements Runnable {
+ public void run() {
+ Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+ Invoker.getGroupAndInvokeCount();
+ boolean replaceMe = false;
+ long address = unsafe.allocateMemory(SIZEOF_PORT_EVENT);
+ try {
+ for (;;) {
+ // reset invoke count
+ if (myGroupAndInvokeCount != null)
+ myGroupAndInvokeCount.resetInvokeCount();
+
+ // wait for I/O completion event
+ // A error here is fatal (thread will not be replaced)
+ replaceMe = false;
+ try {
+ portGet(port, address);
+ } catch (IOException x) {
+ x.printStackTrace();
+ return;
+ }
+
+ // event source
+ short source = unsafe.getShort(address + OFFSETOF_SOURCE);
+ if (source != PORT_SOURCE_FD) {
+ // user event is trigger to invoke task or shutdown
+ if (source == PORT_SOURCE_USER) {
+ Runnable task = pollTask();
+ if (task == null) {
+ // shutdown request
+ return;
+ }
+ // run task (may throw error/exception)
+ replaceMe = true;
+ task.run();
+ }
+ // ignore
+ continue;
+ }
+
+ // pe->portev_object is file descriptor
+ int fd = (int)unsafe.getAddress(address + OFFSETOF_OBJECT);
+ // pe->portev_events
+ int events = unsafe.getInt(address + OFFSETOF_EVENTS);
+
+ // lookup channel
+ PollableChannel ch;
+ fdToChannelLock.readLock().lock();
+ try {
+ ch = fdToChannel.get(fd);
+ } finally {
+ fdToChannelLock.readLock().unlock();
+ }
+
+ // notify channel
+ if (ch != null) {
+ replaceMe = true;
+ // no need to translate events
+ ch.onEvent(events);
+ }
+ }
+ } finally {
+ // free per-thread resources
+ unsafe.freeMemory(address);
+ // last task to exit when shutdown release resources
+ int remaining = threadExit(this, replaceMe);
+ if (remaining == 0 && isShutdown())
+ implClose();
+ }
+ }
+ }
+
+ // -- Native methods --
+
+ private static native void init();
+
+ private static native int portCreate() throws IOException;
+
+ private static native void portAssociate(int port, int source, long object,
+ int events) throws IOException;
+
+ private static native void portGet(int port, long pe) throws IOException;
+
+ private static native int portGetn(int port, long address, int max)
+ throws IOException;
+
+ private static native void portSend(int port, int events) throws IOException;
+
+ private static native void portClose(int port);
+
+ static {
+ Util.load();
+ init();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java b/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java
index a55543e92..e1080c917 100644
--- a/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java
+++ b/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -208,7 +208,7 @@ class SourceChannelImpl
static {
Util.load();
- nd = new FileDispatcher();
+ nd = new FileDispatcherImpl();
}
}
diff --git a/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java b/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java
new file mode 100644
index 000000000..1a8c92352
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.net.InetSocketAddress;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Unix implementation of AsynchronousServerSocketChannel
+ */
+
+class UnixAsynchronousServerSocketChannelImpl
+ extends AsynchronousServerSocketChannelImpl
+ implements Port.PollableChannel
+{
+ private final static NativeDispatcher nd = new SocketDispatcher();
+
+ private final Port port;
+ private final int fdVal;
+
+ // flag to indicate an accept is outstanding
+ private final AtomicBoolean accepting = new AtomicBoolean();
+ private void enableAccept() {
+ accepting.set(false);
+ }
+
+ // used to ensure that the context for an asynchronous accept is visible
+ // the pooled thread that handles the I/O event
+ private final Object updateLock = new Object();
+
+ // pending accept
+ private PendingFuture<AsynchronousSocketChannel,Object> pendingAccept;
+
+ // context for permission check when security manager set
+ private AccessControlContext acc;
+
+
+ UnixAsynchronousServerSocketChannelImpl(Port port)
+ throws IOException
+ {
+ super(port);
+
+ try {
+ IOUtil.configureBlocking(fd, false);
+ } catch (IOException x) {
+ nd.close(fd); // prevent leak
+ throw x;
+ }
+ this.port = port;
+ this.fdVal = IOUtil.fdVal(fd);
+
+ // add mapping from file descriptor to this channel
+ port.register(fdVal, this);
+ }
+
+ // returns and clears the result of a pending accept
+ private PendingFuture<AsynchronousSocketChannel,Object> grabPendingAccept() {
+ synchronized (updateLock) {
+ PendingFuture<AsynchronousSocketChannel,Object> result = pendingAccept;
+ pendingAccept = null;
+ return result;
+ }
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // remove the mapping
+ port.unregister(fdVal);
+
+ // close file descriptor
+ nd.close(fd);
+
+ // if there is a pending accept then complete it
+ final PendingFuture<AsynchronousSocketChannel,Object> result =
+ grabPendingAccept();
+ if (result != null) {
+ // discard the stack trace as otherwise it may appear that implClose
+ // has thrown the exception.
+ AsynchronousCloseException x = new AsynchronousCloseException();
+ x.setStackTrace(new StackTraceElement[0]);
+ result.setFailure(x);
+
+ // invoke by submitting task rather than directly
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ }
+
+ @Override
+ public AsynchronousChannelGroupImpl group() {
+ return port;
+ }
+
+ /**
+ * Invoked by event handling thread when listener socket is polled
+ */
+ @Override
+ public void onEvent(int events) {
+ PendingFuture<AsynchronousSocketChannel,Object> result = grabPendingAccept();
+ if (result == null)
+ return; // may have been grabbed by asynchronous close
+
+ // attempt to accept connection
+ FileDescriptor newfd = new FileDescriptor();
+ InetSocketAddress[] isaa = new InetSocketAddress[1];
+ boolean accepted = false;
+ try {
+ begin();
+ int n = accept0(this.fd, newfd, isaa);
+
+ // spurious wakeup, is this possible?
+ if (n == IOStatus.UNAVAILABLE) {
+ synchronized (updateLock) {
+ this.pendingAccept = result;
+ }
+ port.startPoll(fdVal, Port.POLLIN);
+ return;
+ }
+
+ // connection accepted
+ accepted = true;
+
+ } catch (Throwable x) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ enableAccept();
+ result.setFailure(x);
+ } finally {
+ end();
+ }
+
+ // Connection accepted so finish it when not holding locks.
+ AsynchronousSocketChannel child = null;
+ if (accepted) {
+ try {
+ child = finishAccept(newfd, isaa[0], acc);
+ enableAccept();
+ result.setResult(child);
+ } catch (Throwable x) {
+ enableAccept();
+ if (!(x instanceof IOException) && !(x instanceof SecurityException))
+ x = new IOException(x);
+ result.setFailure(x);
+ }
+ }
+
+ // if an async cancel has already cancelled the operation then
+ // close the new channel so as to free resources
+ if (child != null && result.isCancelled()) {
+ try {
+ child.close();
+ } catch (IOException ignore) { }
+ }
+
+ // invoke the handler
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Completes the accept by creating the AsynchronousSocketChannel for
+ * the given file descriptor and remote address. If this method completes
+ * with an IOException or SecurityException then the channel/file descriptor
+ * will be closed.
+ */
+ private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,
+ final InetSocketAddress remote,
+ AccessControlContext acc)
+ throws IOException, SecurityException
+ {
+ AsynchronousSocketChannel ch = null;
+ try {
+ ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);
+ } catch (IOException x) {
+ nd.close(newfd);
+ throw x;
+ }
+
+ // permission check must always be in initiator's context
+ try {
+ if (acc != null) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkAccept(remote.getAddress().getHostAddress(),
+ remote.getPort());
+ }
+ return null;
+ }
+ }, acc);
+ } else {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkAccept(remote.getAddress().getHostAddress(),
+ remote.getPort());
+ }
+ }
+ } catch (SecurityException x) {
+ try {
+ ch.close();
+ } catch (IOException ignore) { }
+ throw x;
+ }
+ return ch;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <A> Future<AsynchronousSocketChannel> accept(A attachment,
+ final CompletionHandler<AsynchronousSocketChannel,? super A> handler)
+ {
+ // complete immediately if channel is closed
+ if (!isOpen()) {
+ CompletedFuture<AsynchronousSocketChannel,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invokeIndirectly(handler, result);
+ return result;
+ }
+ if (localAddress == null)
+ throw new NotYetBoundException();
+
+ // cancel was invoked with pending accept so connection may have been
+ // dropped.
+ if (isAcceptKilled())
+ throw new RuntimeException("Accept not allowed due cancellation");
+
+ // check and set flag to prevent concurrent accepting
+ if (!accepting.compareAndSet(false, true))
+ throw new AcceptPendingException();
+
+ // attempt accept
+ AbstractFuture<AsynchronousSocketChannel,A> result = null;
+ FileDescriptor newfd = new FileDescriptor();
+ InetSocketAddress[] isaa = new InetSocketAddress[1];
+ try {
+ begin();
+
+ int n = accept0(this.fd, newfd, isaa);
+ if (n == IOStatus.UNAVAILABLE) {
+ // no connection to accept
+ result = new PendingFuture<AsynchronousSocketChannel,A>(this, handler, attachment);
+
+ // need calling context when there is security manager as
+ // permission check may be done in a different thread without
+ // any application call frames on the stack
+ synchronized (this) {
+ this.acc = (System.getSecurityManager() == null) ?
+ null : AccessController.getContext();
+ this.pendingAccept =
+ (PendingFuture<AsynchronousSocketChannel,Object>)result;
+ }
+
+ // register for connections
+ port.startPoll(fdVal, Port.POLLIN);
+ return result;
+ }
+ } catch (Throwable x) {
+ // accept failed
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result = CompletedFuture.withFailure(this, x, attachment);
+ } finally {
+ end();
+ }
+
+ // connection accepted immediately
+ if (result == null) {
+ try {
+ AsynchronousSocketChannel ch = finishAccept(newfd, isaa[0], null);
+ result = CompletedFuture.withResult(this, ch, attachment);
+ } catch (Throwable x) {
+ result = CompletedFuture.withFailure(this, x, attachment);
+ }
+ }
+
+ // re-enable accepting and invoke handler
+ enableAccept();
+ Invoker.invokeIndirectly(handler, result);
+ return result;
+ }
+
+ // -- Native methods --
+
+ private static native void initIDs();
+
+ // Accepts a new connection, setting the given file descriptor to refer to
+ // the new socket and setting isaa[0] to the socket's remote address.
+ // Returns 1 on success, or IOStatus.UNAVAILABLE.
+ //
+ private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
+ InetSocketAddress[] isaa)
+ throws IOException;
+
+ static {
+ Util.load();
+ initIDs();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java b/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java
new file mode 100644
index 000000000..702b28cbb
--- /dev/null
+++ b/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java
@@ -0,0 +1,673 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 conne02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.ByteBuffer;
+import java.net.*;
+import java.util.concurrent.*;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * Unix implementation of AsynchronousSocketChannel
+ */
+
+class UnixAsynchronousSocketChannelImpl
+ extends AsynchronousSocketChannelImpl implements Port.PollableChannel
+{
+ private final static NativeDispatcher nd = new SocketDispatcher();
+ private static enum OpType { CONNECT, READ, WRITE };
+
+ private static final boolean disableSynchronousRead;
+ static {
+ String propValue = AccessController.doPrivileged(
+ new GetPropertyAction("sun.nio.ch.disableSynchronousRead", "false"));
+ disableSynchronousRead = (propValue.length() == 0) ?
+ true : Boolean.valueOf(propValue);
+ }
+
+ private final Port port;
+ private final int fdVal;
+
+ // used to ensure that the context for I/O operations that complete
+ // ascynrhonously is visible to the pooled threads handling I/O events.
+ private final Object updateLock = new Object();
+
+ // pending connect (updateLock)
+ private PendingFuture<Void,Object> pendingConnect;
+
+ // pending remote address (statLock)
+ private SocketAddress pendingRemote;
+
+ // pending read (updateLock)
+ private ByteBuffer[] readBuffers;
+ private boolean scatteringRead;
+ private PendingFuture<Number,Object> pendingRead;
+
+ // pending write (updateLock)
+ private ByteBuffer[] writeBuffers;
+ private boolean gatheringWrite;
+ private PendingFuture<Number,Object> pendingWrite;
+
+
+ UnixAsynchronousSocketChannelImpl(Port port)
+ throws IOException
+ {
+ super(port);
+
+ // set non-blocking
+ try {
+ IOUtil.configureBlocking(fd, false);
+ } catch (IOException x) {
+ nd.close(fd);
+ throw x;
+ }
+
+ this.port = port;
+ this.fdVal = IOUtil.fdVal(fd);
+
+ // add mapping from file descriptor to this channel
+ port.register(fdVal, this);
+ }
+
+ // Constructor for sockets created by UnixAsynchronousServerSocketChannelImpl
+ UnixAsynchronousSocketChannelImpl(Port port,
+ FileDescriptor fd,
+ InetSocketAddress remote)
+ throws IOException
+ {
+ super(port, fd, remote);
+
+ this.fdVal = IOUtil.fdVal(fd);
+ IOUtil.configureBlocking(fd, false);
+
+ try {
+ port.register(fdVal, this);
+ } catch (ShutdownChannelGroupException x) {
+ // ShutdownChannelGroupException thrown if we attempt to register a
+ // new channel after the group is shutdown
+ throw new IOException(x);
+ }
+
+ this.port = port;
+ }
+
+ @Override
+ public AsynchronousChannelGroupImpl group() {
+ return port;
+ }
+
+ // register for events if there are outstanding I/O operations
+ private void updateEvents() {
+ assert Thread.holdsLock(updateLock);
+ int events = 0;
+ if (pendingRead != null)
+ events |= Port.POLLIN;
+ if (pendingConnect != null || pendingWrite != null)
+ events |= Port.POLLOUT;
+ if (events != 0)
+ port.startPoll(fdVal, events);
+ }
+
+ /**
+ * Invoked by event handler thread when file descriptor is polled
+ */
+ @Override
+ public void onEvent(int events) {
+ boolean readable = (events & Port.POLLIN) > 0;
+ boolean writable = (events & Port.POLLOUT) > 0;
+ if ((events & (Port.POLLERR | Port.POLLHUP)) > 0) {
+ readable = true;
+ writable = true;
+ }
+
+ PendingFuture<Void,Object> connectResult = null;
+ PendingFuture<Number,Object> readResult = null;
+ PendingFuture<Number,Object> writeResult = null;
+
+ // map event to pending result
+ synchronized (updateLock) {
+ if (readable && (pendingRead != null)) {
+ readResult = pendingRead;
+ pendingRead = null;
+ }
+ if (writable) {
+ if (pendingWrite != null) {
+ writeResult = pendingWrite;
+ pendingWrite = null;
+ } else if (pendingConnect != null) {
+ connectResult = pendingConnect;
+ pendingConnect = null;
+ }
+ }
+ }
+
+ // complete the I/O operation. Special case for when channel is
+ // ready for both reading and writing. In that case, submit task to
+ // complete write if write operation has a completion handler.
+ if (readResult != null) {
+ if (writeResult != null)
+ finishWrite(writeResult, false);
+ finishRead(readResult, true);
+ return;
+ }
+ if (writeResult != null) {
+ finishWrite(writeResult, true);
+ }
+ if (connectResult != null) {
+ finishConnect(connectResult, true);
+ }
+ }
+
+ // returns and clears the result of a pending read
+ PendingFuture<Number,Object> grabPendingRead() {
+ synchronized (updateLock) {
+ PendingFuture<Number,Object> result = pendingRead;
+ pendingRead = null;
+ return result;
+ }
+ }
+
+ // returns and clears the result of a pending write
+ PendingFuture<Number,Object> grabPendingWrite() {
+ synchronized (updateLock) {
+ PendingFuture<Number,Object> result = pendingWrite;
+ pendingWrite = null;
+ return result;
+ }
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // remove the mapping
+ port.unregister(fdVal);
+
+ // close file descriptor
+ nd.close(fd);
+
+ // All outstanding I/O operations are required to fail
+ final PendingFuture<Void,Object> readyToConnect;
+ final PendingFuture<Number,Object> readyToRead;
+ final PendingFuture<Number,Object> readyToWrite;
+ synchronized (updateLock) {
+ readyToConnect = pendingConnect;
+ pendingConnect = null;
+ readyToRead = pendingRead;
+ pendingRead = null;
+ readyToWrite = pendingWrite;
+ pendingWrite = null;
+ }
+ if (readyToConnect != null) {
+ finishConnect(readyToConnect, false);
+ }
+ if (readyToRead != null) {
+ finishRead(readyToRead, false);
+ }
+ if (readyToWrite != null) {
+ finishWrite(readyToWrite, false);
+ }
+ }
+
+ @Override
+ public void onCancel(PendingFuture<?,?> task) {
+ if (task.getContext() == OpType.CONNECT)
+ killConnect();
+ if (task.getContext() == OpType.READ)
+ killConnect();
+ if (task.getContext() == OpType.WRITE)
+ killConnect();
+ }
+
+ // -- connect --
+
+ private void setConnected() throws IOException {
+ synchronized (stateLock) {
+ state = ST_CONNECTED;
+ localAddress = Net.localAddress(fd);
+ remoteAddress = pendingRemote;
+ }
+ }
+
+ private void finishConnect(PendingFuture<Void,Object> result,
+ boolean invokeDirect)
+ {
+ Throwable e = null;
+ try {
+ begin();
+ checkConnect(fdVal);
+ setConnected();
+ result.setResult(null);
+ } catch (Throwable x) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ e = x;
+ } finally {
+ end();
+ }
+ if (e != null) {
+ // close channel if connection cannot be established
+ try {
+ close();
+ } catch (IOException ignore) { }
+ result.setFailure(e);
+ }
+ if (invokeDirect) {
+ Invoker.invoke(result.handler(), result);
+ } else {
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <A> Future<Void> connect(SocketAddress remote,
+ A attachment,
+ CompletionHandler<Void,? super A> handler)
+ {
+ if (!isOpen()) {
+ CompletedFuture<Void,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ InetSocketAddress isa = Net.checkAddress(remote);
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
+
+ // check and set state
+ synchronized (stateLock) {
+ if (state == ST_CONNECTED)
+ throw new AlreadyConnectedException();
+ if (state == ST_PENDING)
+ throw new ConnectionPendingException();
+ state = ST_PENDING;
+ pendingRemote = remote;
+ }
+
+ AbstractFuture<Void,A> result = null;
+ Throwable e = null;
+ try {
+ begin();
+ int n = Net.connect(fd, isa.getAddress(), isa.getPort());
+ if (n == IOStatus.UNAVAILABLE) {
+ // connection could not be established immediately
+ result = new PendingFuture<Void,A>(this, handler, attachment, OpType.CONNECT);
+ synchronized (updateLock) {
+ this.pendingConnect = (PendingFuture<Void,Object>)result;
+ updateEvents();
+ }
+ return result;
+ }
+ setConnected();
+ result = CompletedFuture.withResult(this, null, attachment);
+ } catch (Throwable x) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ e = x;
+ } finally {
+ end();
+ }
+
+ // close channel if connect fails
+ if (e != null) {
+ try {
+ close();
+ } catch (IOException ignore) { }
+ result = CompletedFuture.withFailure(this, e, attachment);
+ }
+
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ // -- read --
+
+ @SuppressWarnings("unchecked")
+ private void finishRead(PendingFuture<Number,Object> result,
+ boolean invokeDirect)
+ {
+ int n = -1;
+ PendingFuture<Number,Object> pending = null;
+ try {
+ begin();
+
+ ByteBuffer[] dsts = readBuffers;
+ if (dsts.length == 1) {
+ n = IOUtil.read(fd, dsts[0], -1, nd, null);
+ } else {
+ n = (int)IOUtil.read(fd, dsts, nd);
+ }
+ if (n == IOStatus.UNAVAILABLE) {
+ // spurious wakeup, is this possible?
+ pending = result;
+ return;
+ }
+
+ // allow buffer(s) to be GC'ed.
+ readBuffers = null;
+
+ // allow another read to be initiated
+ boolean wasScatteringRead = scatteringRead;
+ enableReading();
+
+ // result is Integer or Long
+ if (wasScatteringRead) {
+ result.setResult(Long.valueOf(n));
+ } else {
+ result.setResult(Integer.valueOf(n));
+ }
+
+ } catch (Throwable x) {
+ enableReading();
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ } finally {
+ // restart poll in case of concurrent write
+ synchronized (updateLock) {
+ if (pending != null)
+ this.pendingRead = pending;
+ updateEvents();
+ }
+ end();
+ }
+
+ if (invokeDirect) {
+ Invoker.invoke(result.handler(), result);
+ } else {
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ }
+
+ private Runnable readTimeoutTask = new Runnable() {
+ public void run() {
+ PendingFuture<Number,Object> result = grabPendingRead();
+ if (result == null)
+ return; // already completed
+
+ // kill further reading before releasing waiters
+ enableReading(true);
+
+ // set completed and invoke handler
+ result.setFailure(new InterruptedByTimeoutException());
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ };
+
+ /**
+ * Initiates a read or scattering read operation
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ <V extends Number,A> Future<V> readImpl(ByteBuffer[] dsts,
+ boolean isScatteringRead,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler)
+ {
+ // A synchronous read is not attempted if disallowed by system property
+ // or, we are using a fixed thread pool and the completion handler may
+ // not be invoked directly (because the thread is not a pooled thread or
+ // there are too many handlers on the stack).
+ Invoker.GroupAndInvokeCount myGroupAndInvokeCount = null;
+ boolean invokeDirect = false;
+ boolean attemptRead = false;
+ if (!disableSynchronousRead) {
+ myGroupAndInvokeCount = Invoker.getGroupAndInvokeCount();
+ invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port);
+ attemptRead = (handler == null) || invokeDirect ||
+ !port.isFixedThreadPool(); // okay to attempt read with user thread pool
+ }
+
+ AbstractFuture<V,A> result;
+ try {
+ begin();
+
+ int n;
+ if (attemptRead) {
+ if (isScatteringRead) {
+ n = (int)IOUtil.read(fd, dsts, nd);
+ } else {
+ n = IOUtil.read(fd, dsts[0], -1, nd, null);
+ }
+ } else {
+ n = IOStatus.UNAVAILABLE;
+ }
+
+ if (n == IOStatus.UNAVAILABLE) {
+ result = new PendingFuture<V,A>(this, handler, attachment, OpType.READ);
+
+ // update evetns so that read will complete asynchronously
+ synchronized (updateLock) {
+ this.readBuffers = dsts;
+ this.scatteringRead = isScatteringRead;
+ this.pendingRead = (PendingFuture<Number,Object>)result;
+ updateEvents();
+ }
+
+ // schedule timeout
+ if (timeout > 0L) {
+ Future<?> timeoutTask =
+ port.schedule(readTimeoutTask, timeout, unit);
+ ((PendingFuture<V,A>)result).setTimeoutTask(timeoutTask);
+ }
+ return result;
+ }
+
+ // data available
+ enableReading();
+
+ // result type is Long or Integer
+ if (isScatteringRead) {
+ result = (CompletedFuture<V,A>)CompletedFuture
+ .withResult(this, Long.valueOf(n), attachment);
+ } else {
+ result = (CompletedFuture<V,A>)CompletedFuture
+ .withResult(this, Integer.valueOf(n), attachment);
+ }
+ } catch (Throwable x) {
+ enableReading();
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result = CompletedFuture.withFailure(this, x, attachment);
+ } finally {
+ end();
+ }
+
+ if (invokeDirect) {
+ Invoker.invokeDirect(myGroupAndInvokeCount, handler, result);
+ } else {
+ Invoker.invokeIndirectly(handler, result);
+ }
+ return result;
+ }
+
+ // -- write --
+
+ private void finishWrite(PendingFuture<Number,Object> result,
+ boolean invokeDirect)
+ {
+ PendingFuture<Number,Object> pending = null;
+ try {
+ begin();
+
+ ByteBuffer[] srcs = writeBuffers;
+ int n;
+ if (srcs.length == 1) {
+ n = IOUtil.write(fd, srcs[0], -1, nd, null);
+ } else {
+ n = (int)IOUtil.write(fd, srcs, nd);
+ }
+ if (n == IOStatus.UNAVAILABLE) {
+ // spurious wakeup, is this possible?
+ pending = result;
+ return;
+ }
+
+ // allow buffer(s) to be GC'ed.
+ writeBuffers = null;
+
+ // allow another write to be initiated
+ boolean wasGatheringWrite = gatheringWrite;
+ enableWriting();
+
+ // result is a Long or Integer
+ if (wasGatheringWrite) {
+ result.setResult(Long.valueOf(n));
+ } else {
+ result.setResult(Integer.valueOf(n));
+ }
+
+ } catch (Throwable x) {
+ enableWriting();
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result.setFailure(x);
+ } finally {
+ // restart poll in case of concurrent read
+ synchronized (this) {
+ if (pending != null)
+ this.pendingWrite = pending;
+ updateEvents();
+ }
+ end();
+ }
+ if (invokeDirect) {
+ Invoker.invoke(result.handler(), result);
+ } else {
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ }
+
+ private Runnable writeTimeoutTask = new Runnable() {
+ public void run() {
+ PendingFuture<Number,Object> result = grabPendingWrite();
+ if (result == null)
+ return; // already completed
+
+ // kill further writing before releasing waiters
+ enableWriting(true);
+
+ // set completed and invoke handler
+ result.setFailure(new InterruptedByTimeoutException());
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ };
+
+ /**
+ * Initiates a read or scattering read operation
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ <V extends Number,A> Future<V> writeImpl(ByteBuffer[] srcs,
+ boolean isGatheringWrite,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler)
+ {
+ Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+ Invoker.getGroupAndInvokeCount();
+ boolean invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port);
+ boolean attemptWrite = (handler == null) || invokeDirect ||
+ !port.isFixedThreadPool(); // okay to attempt read with user thread pool
+
+ AbstractFuture<V,A> result;
+ try {
+ begin();
+
+ int n;
+ if (attemptWrite) {
+ if (isGatheringWrite) {
+ n = (int)IOUtil.write(fd, srcs, nd);
+ } else {
+ n = IOUtil.write(fd, srcs[0], -1, nd, null);
+ }
+ } else {
+ n = IOStatus.UNAVAILABLE;
+ }
+
+ if (n == IOStatus.UNAVAILABLE) {
+ result = new PendingFuture<V,A>(this, handler, attachment, OpType.WRITE);
+
+ // update evetns so that read will complete asynchronously
+ synchronized (updateLock) {
+ this.writeBuffers = srcs;
+ this.gatheringWrite = isGatheringWrite;
+ this.pendingWrite = (PendingFuture<Number,Object>)result;
+ updateEvents();
+ }
+
+ // schedule timeout
+ if (timeout > 0L) {
+ Future<?> timeoutTask =
+ port.schedule(writeTimeoutTask, timeout, unit);
+ ((PendingFuture<V,A>)result).setTimeoutTask(timeoutTask);
+ }
+ return result;
+ }
+
+ // data available
+ enableWriting();
+ if (isGatheringWrite) {
+ result = (CompletedFuture<V,A>)CompletedFuture
+ .withResult(this, Long.valueOf(n), attachment);
+ } else {
+ result = (CompletedFuture<V,A>)CompletedFuture
+ .withResult(this, Integer.valueOf(n), attachment);
+ }
+ } catch (Throwable x) {
+ enableWriting();
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ result = CompletedFuture.withFailure(this, x, attachment);
+ } finally {
+ end();
+ }
+ if (invokeDirect) {
+ Invoker.invokeDirect(myGroupAndInvokeCount, handler, result);
+ } else {
+ Invoker.invokeIndirectly(handler, result);
+ }
+ return result;
+ }
+
+ // -- Native methods --
+
+ private static native void checkConnect(int fdVal) throws IOException;
+
+ static {
+ Util.load();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java
new file mode 100644
index 000000000..e86ff075c
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.spi.FileSystemProvider;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * Creates this platform's default FileSystemProvider.
+ */
+
+public class DefaultFileSystemProvider {
+ private DefaultFileSystemProvider() { }
+
+ @SuppressWarnings("unchecked")
+ private static FileSystemProvider createProvider(final String cn) {
+ return AccessController
+ .doPrivileged(new PrivilegedAction<FileSystemProvider>() {
+ public FileSystemProvider run() {
+ Class<FileSystemProvider> c;
+ try {
+ c = (Class<FileSystemProvider>)Class.forName(cn, true, null);
+ } catch (ClassNotFoundException x) {
+ throw new AssertionError(x);
+ }
+ try {
+ return c.newInstance();
+ } catch (IllegalAccessException x) {
+ throw new AssertionError(x);
+ } catch (InstantiationException x) {
+ throw new AssertionError(x);
+ }
+ }});
+ }
+
+ /**
+ * Returns the default FileSystemProvider.
+ */
+ public static FileSystemProvider create() {
+ String osname = AccessController
+ .doPrivileged(new GetPropertyAction("os.name"));
+ if (osname.equals("SunOS"))
+ return createProvider("sun.nio.fs.SolarisFileSystemProvider");
+ if (osname.equals("Linux"))
+ return createProvider("sun.nio.fs.LinuxFileSystemProvider");
+ throw new AssertionError("Platform not recognized");
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/DefaultFileTypeDetector.java b/src/solaris/classes/sun/nio/fs/DefaultFileTypeDetector.java
new file mode 100644
index 000000000..22db3165d
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/DefaultFileTypeDetector.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.spi.FileTypeDetector;
+
+public class DefaultFileTypeDetector {
+ private DefaultFileTypeDetector() { }
+
+ public static FileTypeDetector create() {
+ return new GnomeFileTypeDetector();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/GnomeFileTypeDetector.java b/src/solaris/classes/sun/nio/fs/GnomeFileTypeDetector.java
new file mode 100644
index 000000000..bf9e87a89
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/GnomeFileTypeDetector.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.FileRef;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * File type detector that uses the GNOME I/O library or the deprecated
+ * GNOME VFS to guess the MIME type of a file.
+ */
+
+public class GnomeFileTypeDetector
+ extends AbstractFileTypeDetector
+{
+ private static final String GNOME_VFS_MIME_TYPE_UNKNOWN =
+ "application/octet-stream";
+
+ // true if GIO available
+ private final boolean gioAvailable;
+
+ // true if GNOME VFS available and GIO is not available
+ private final boolean gnomeVfsAvailable;
+
+ public GnomeFileTypeDetector() {
+ gioAvailable = initializeGio();
+ if (gioAvailable) {
+ gnomeVfsAvailable = false;
+ } else {
+ gnomeVfsAvailable = initializeGnomeVfs();
+ }
+ }
+
+ @Override
+ public String implProbeContentType(FileRef obj) throws IOException {
+ if (!gioAvailable && !gnomeVfsAvailable)
+ return null;
+ if (!(obj instanceof UnixPath))
+ return null;
+
+ UnixPath path = (UnixPath)obj;
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(path.getByteArrayForSysCalls());
+ try {
+ if (gioAvailable) {
+ byte[] type = probeUsingGio(buffer.address());
+ return (type == null) ? null : new String(type);
+ } else {
+ byte[] type = probeUsingGnomeVfs(buffer.address());
+ if (type == null)
+ return null;
+ String s = new String(type);
+ return s.equals(GNOME_VFS_MIME_TYPE_UNKNOWN) ? null : s;
+ }
+
+ } finally {
+ buffer.release();
+ }
+
+ }
+
+ // GIO
+ private static native boolean initializeGio();
+ private static native byte[] probeUsingGio(long pathAddress);
+
+ // GNOME VFS
+ private static native boolean initializeGnomeVfs();
+ private static native byte[] probeUsingGnomeVfs(long pathAddress);
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java b/src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java
new file mode 100644
index 000000000..85f83e235
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+/**
+ * Linux implementation of DosFileAttributeView for use on file systems such
+ * as ext3 that have extended attributes enabled and SAMBA configured to store
+ * DOS attributes.
+ */
+
+class LinuxDosFileAttributeView
+ extends UnixFileAttributeViews.Basic implements DosFileAttributeView
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private static final String READONLY_NAME = "readonly";
+ private static final String ARCHIVE_NAME = "archive";
+ private static final String SYSTEM_NAME = "system";
+ private static final String HIDDEN_NAME = "hidden";
+
+ private static final String DOS_XATTR_NAME = "user.DOSATTRIB";
+ private static final byte[] DOS_XATTR_NAME_AS_BYTES = DOS_XATTR_NAME.getBytes();
+
+ private static final int DOS_XATTR_READONLY = 0x01;
+ private static final int DOS_XATTR_HIDDEN = 0x02;
+ private static final int DOS_XATTR_SYSTEM = 0x04;
+ private static final int DOS_XATTR_ARCHIVE = 0x20;
+
+ LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
+ super(file, followLinks);
+ }
+
+ @Override
+ public String name() {
+ return "dos";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(READONLY_NAME))
+ return readAttributes().isReadOnly();
+ if (attribute.equals(ARCHIVE_NAME))
+ return readAttributes().isArchive();
+ if (attribute.equals(SYSTEM_NAME))
+ return readAttributes().isSystem();
+ if (attribute.equals(HIDDEN_NAME))
+ return readAttributes().isHidden();
+ return super.getAttribute(attribute);
+ }
+
+ @Override
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(READONLY_NAME)) {
+ setReadOnly((Boolean)value);
+ return;
+ }
+ if (attribute.equals(ARCHIVE_NAME)) {
+ setArchive((Boolean)value);
+ return;
+ }
+ if (attribute.equals(SYSTEM_NAME)) {
+ setSystem((Boolean)value);
+ return;
+ }
+ if (attribute.equals(HIDDEN_NAME)) {
+ setHidden((Boolean)value);
+ return;
+ }
+ super.setAttribute(attribute, value);
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ AttributesBuilder builder = AttributesBuilder.create(first, rest);
+ DosFileAttributes attrs = readAttributes();
+ addBasicAttributesToBuilder(attrs, builder);
+ if (builder.match(READONLY_NAME))
+ builder.add(READONLY_NAME, attrs.isReadOnly());
+ if (builder.match(ARCHIVE_NAME))
+ builder.add(ARCHIVE_NAME, attrs.isArchive());
+ if (builder.match(SYSTEM_NAME))
+ builder.add(SYSTEM_NAME, attrs.isSystem());
+ if (builder.match(HIDDEN_NAME))
+ builder.add(HIDDEN_NAME, attrs.isHidden());
+ return builder.unmodifiableMap();
+ }
+
+ @Override
+ public DosFileAttributes readAttributes() throws IOException {
+ file.checkRead();
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ final UnixFileAttributes attrs = UnixFileAttributes.get(fd);
+ final int dosAttribute = getDosAttribute(fd);
+
+ return new DosFileAttributes() {
+ @Override
+ public long lastModifiedTime() {
+ return attrs.lastModifiedTime();
+ }
+ @Override
+ public long lastAccessTime() {
+ return attrs.lastAccessTime();
+ }
+ @Override
+ public long creationTime() {
+ return attrs.creationTime();
+ }
+ @Override
+ public TimeUnit resolution() {
+ return attrs.resolution();
+ }
+ @Override
+ public boolean isRegularFile() {
+ return attrs.isRegularFile();
+ }
+ @Override
+ public boolean isDirectory() {
+ return attrs.isDirectory();
+ }
+ @Override
+ public boolean isSymbolicLink() {
+ return attrs.isSymbolicLink();
+ }
+ @Override
+ public boolean isOther() {
+ return attrs.isOther();
+ }
+ @Override
+ public long size() {
+ return attrs.size();
+ }
+ @Override
+ public int linkCount() {
+ return attrs.linkCount();
+ }
+ @Override
+ public Object fileKey() {
+ return attrs.fileKey();
+ }
+ @Override
+ public boolean isReadOnly() {
+ return (dosAttribute & DOS_XATTR_READONLY) != 0;
+ }
+ @Override
+ public boolean isHidden() {
+ return (dosAttribute & DOS_XATTR_HIDDEN) != 0;
+ }
+ @Override
+ public boolean isArchive() {
+ return (dosAttribute & DOS_XATTR_ARCHIVE) != 0;
+ }
+ @Override
+ public boolean isSystem() {
+ return (dosAttribute & DOS_XATTR_SYSTEM) != 0;
+ }
+ };
+
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public void setReadOnly(boolean value) throws IOException {
+ updateDosAttribute(DOS_XATTR_READONLY, value);
+ }
+
+ @Override
+ public void setHidden(boolean value) throws IOException {
+ updateDosAttribute(DOS_XATTR_HIDDEN, value);
+ }
+
+ @Override
+ public void setArchive(boolean value) throws IOException {
+ updateDosAttribute(DOS_XATTR_ARCHIVE, value);
+ }
+
+ @Override
+ public void setSystem(boolean value) throws IOException {
+ updateDosAttribute(DOS_XATTR_SYSTEM, value);
+ }
+
+ /**
+ * Reads the value of the user.DOSATTRIB extended attribute
+ */
+ private int getDosAttribute(int fd) throws UnixException {
+ final int size = 24;
+
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ int len = LinuxNativeDispatcher
+ .fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size);
+
+ if (len > 0) {
+ // ignore null terminator
+ if (unsafe.getByte(buffer.address()+len-1) == 0)
+ len--;
+
+ // convert to String and parse
+ byte[] buf = new byte[len];
+ unsafe.copyMemory(null, buffer.address(), buf,
+ Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
+ String value = new String(buf); // platform encoding
+
+ // should be something like 0x20
+ if (value.length() >= 3 && value.startsWith("0x")) {
+ try {
+ return Integer.parseInt(value.substring(2), 16);
+ } catch (NumberFormatException x) {
+ // ignore
+ }
+ }
+ }
+ throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid");
+ } catch (UnixException x) {
+ // default value when attribute does not exist
+ if (x.errno() == ENODATA)
+ return 0;
+ throw x;
+ } finally {
+ buffer.release();
+ }
+ }
+
+ /**
+ * Updates the value of the user.DOSATTRIB extended attribute
+ */
+ private void updateDosAttribute(int flag, boolean enable) throws IOException {
+ file.checkWrite();
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ int oldValue = getDosAttribute(fd);
+ int newValue = oldValue;
+ if (enable) {
+ newValue |= flag;
+ } else {
+ newValue &= ~flag;
+ }
+ if (newValue != oldValue) {
+ byte[] value = ("0x" + Integer.toHexString(newValue)).getBytes();
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(value);
+ try {
+ LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES,
+ buffer.address(), value.length+1);
+ } finally {
+ buffer.release();
+ }
+ }
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ } finally {
+ close(fd);
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxFileStore.java b/src/solaris/classes/sun/nio/fs/LinuxFileStore.java
new file mode 100644
index 000000000..8c07ce5f6
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxFileStore.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * Linux implementation of FileStore
+ */
+
+class LinuxFileStore
+ extends UnixFileStore
+{
+ // used when checking if extended attributes are enabled or not
+ private volatile boolean xattrChecked;
+ private volatile boolean xattrEnabled;
+
+ LinuxFileStore(UnixPath file) throws IOException {
+ super(file);
+ }
+
+ LinuxFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
+ super(fs, entry);
+ }
+
+ /**
+ * Finds, and returns, the mount entry for the file system where the file
+ * resides.
+ */
+ @Override
+ UnixMountEntry findMountEntry() throws IOException {
+ UnixFileSystem fs = file().getFileSystem();
+
+ // step 1: get realpath
+ UnixPath path = null;
+ try {
+ byte[] rp = UnixNativeDispatcher.realpath(file());
+ path = new UnixPath(fs, rp);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file());
+ }
+
+ // step 2: find mount point
+ UnixPath parent = path.getParent();
+ while (parent != null) {
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(parent, true);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(parent);
+ }
+ if (attrs.dev() != dev())
+ break;
+ path = parent;
+ parent = parent.getParent();
+ }
+
+ // step 3: lookup mounted file systems
+ byte[] dir = path.asByteArray();
+ for (UnixMountEntry entry: fs.getMountEntries()) {
+ if (Arrays.equals(dir, entry.dir()))
+ return entry;
+ }
+
+ throw new IOException("Mount point not found in mtab");
+ }
+
+ // returns true if extended attributes enabled on file system where given
+ // file resides, returns false if disabled or unable to determine.
+ private boolean isExtendedAttributesEnabled(UnixPath path) {
+ try {
+ int fd = path.openForAttributeAccess(false);
+ try {
+ // fgetxattr returns size if called with size==0
+ LinuxNativeDispatcher.fgetxattr(fd, "user.java".getBytes(), 0L, 0);
+ return true;
+ } catch (UnixException e) {
+ // attribute does not exist
+ if (e.errno() == UnixConstants.ENODATA)
+ return true;
+ } finally {
+ UnixNativeDispatcher.close(fd);
+ }
+ } catch (IOException ignore) {
+ // nothing we can do
+ }
+ return false;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name) {
+ // support DosFileAttributeView and NamedAttributeView if extended
+ // attributes enabled
+ if (name.equals("dos") || name.equals("xattr")) {
+ // lookup fstypes.properties
+ FeatureStatus status = checkIfFeaturePresent("user_xattr");
+ if (status == FeatureStatus.PRESENT)
+ return true;
+ if (status == FeatureStatus.NOT_PRESENT)
+ return false;
+
+ // if file system is mounted with user_xattr option then assume
+ // extended attributes are enabled
+ if ((entry().hasOption("user_xattr")))
+ return true;
+
+ // user_xattr option not present but we special-case ext3/4 as we
+ // know that extended attributes are not enabled by default.
+ if (entry().fstype().equals("ext3") || entry().fstype().equals("ext4"))
+ return false;
+
+ // not ext3/4 so probe mount point
+ if (!xattrChecked) {
+ UnixPath dir = new UnixPath(file().getFileSystem(), entry().dir());
+ xattrEnabled = isExtendedAttributesEnabled(dir);
+ xattrChecked = true;
+ }
+ return xattrEnabled;
+ }
+
+ return super.supportsFileAttributeView(name);
+ }
+
+ @Override
+ boolean isLoopback() {
+ return false;
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxFileSystem.java b/src/solaris/classes/sun/nio/fs/LinuxFileSystem.java
new file mode 100644
index 000000000..d574c9dff
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxFileSystem.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+import static sun.nio.fs.LinuxNativeDispatcher.*;
+
+/**
+ * Linux implementation of FileSystem
+ */
+
+class LinuxFileSystem extends UnixFileSystem {
+ private final boolean hasInotify;
+ private final boolean hasAtSysCalls;
+
+ LinuxFileSystem(UnixFileSystemProvider provider, String dir) {
+ super(provider, dir);
+
+ // assume X.Y[-Z] format
+ String osversion = AccessController
+ .doPrivileged(new GetPropertyAction("os.version"));
+ String[] vers = osversion.split("\\.", 0);
+ assert vers.length >= 2;
+
+ int majorVersion = Integer.parseInt(vers[0]);
+ int minorVersion = Integer.parseInt(vers[1]);
+ int microVersion = 0;
+ if (vers.length > 2) {
+ String[] microVers = vers[2].split("-", 0);
+ microVersion = (microVers.length > 0) ?
+ Integer.parseInt(microVers[0]) : 0;
+ }
+
+ // inotify available since 2.6.13
+ this.hasInotify = ((majorVersion > 2) ||
+ (majorVersion == 2 && minorVersion > 6) ||
+ ((majorVersion == 2) && (minorVersion == 6) && (microVersion >= 13)));
+
+ // openat etc. available since 2.6.16
+ this.hasAtSysCalls = ((majorVersion > 2) ||
+ (majorVersion == 2 && minorVersion > 6) ||
+ ((majorVersion == 2) && (minorVersion == 6) && (microVersion >= 16)));
+ }
+
+ @Override
+ public WatchService newWatchService()
+ throws IOException
+ {
+ if (hasInotify) {
+ return new LinuxWatchService(this);
+ } else {
+ // use polling implementation on older kernels
+ return new PollingWatchService();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileAttributeView> V newFileAttributeView(Class<V> view,
+ UnixPath file,
+ LinkOption... options)
+ {
+ if (view == DosFileAttributeView.class)
+ return (V) new LinuxDosFileAttributeView(file, followLinks(options));
+ if (view == UserDefinedFileAttributeView.class)
+ return (V) new LinuxUserDefinedFileAttributeView(file, followLinks(options));
+ return super.newFileAttributeView(view, file, options);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public FileAttributeView newFileAttributeView(String name,
+ UnixPath file,
+ LinkOption... options)
+ {
+ if (name.equals("dos"))
+ return new LinuxDosFileAttributeView(file, followLinks(options));
+ if (name.equals("xattr"))
+ return new LinuxUserDefinedFileAttributeView(file, followLinks(options));
+ return super.newFileAttributeView(name, file, options);
+ }
+
+ // lazy initialization of the list of supported attribute views
+ private static class SupportedFileFileAttributeViewsHolder {
+ static final Set<String> supportedFileAttributeViews =
+ supportedFileAttributeViews();
+ private static Set<String> supportedFileAttributeViews() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(UnixFileSystem.standardFileAttributeViews());
+ // additional Linux-specific views
+ result.add("dos");
+ result.add("xattr");
+ return Collections.unmodifiableSet(result);
+ }
+ }
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
+ }
+
+ @Override
+ void copyNonPosixAttributes(int ofd, int nfd) {
+ LinuxUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
+ }
+
+ @Override
+ boolean supportsSecureDirectoryStreams() {
+ return hasAtSysCalls;
+ }
+
+ /**
+ * Returns object to iterate over entries in /etc/mtab
+ */
+ @Override
+ Iterable<UnixMountEntry> getMountEntries() {
+ ArrayList<UnixMountEntry> entries = new ArrayList<UnixMountEntry>();
+ try {
+ long fp = setmntent("/etc/mtab".getBytes(), "r".getBytes());
+ try {
+ for (;;) {
+ UnixMountEntry entry = new UnixMountEntry();
+ int res = getextmntent(fp, entry);
+ if (res < 0)
+ break;
+ entries.add(entry);
+ }
+ } finally {
+ endmntent(fp);
+ }
+
+ } catch (UnixException x) {
+ // nothing we can do
+ }
+ return entries;
+ }
+
+ @Override
+ FileStore getFileStore(UnixPath path) throws IOException {
+ return new LinuxFileStore(path);
+ }
+
+ @Override
+ FileStore getFileStore(UnixMountEntry entry) throws IOException {
+ return new LinuxFileStore(this, entry);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java
new file mode 100644
index 000000000..aa979c475
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Linux implementation of FileSystemProvider
+ */
+
+public class LinuxFileSystemProvider extends UnixFileSystemProvider {
+ public LinuxFileSystemProvider() {
+ super();
+ }
+
+ @Override
+ LinuxFileSystem newFileSystem(String dir) {
+ return new LinuxFileSystem(this, dir);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxNativeDispatcher.java b/src/solaris/classes/sun/nio/fs/LinuxNativeDispatcher.java
new file mode 100644
index 000000000..65547badc
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxNativeDispatcher.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Linux specific system calls.
+ */
+
+class LinuxNativeDispatcher extends UnixNativeDispatcher {
+ private LinuxNativeDispatcher() { }
+
+ /**
+ * FILE *setmntent(const char *filename, const char *type);
+ */
+ static long setmntent(byte[] filename, byte[] type) throws UnixException {
+ NativeBuffer pathBuffer = NativeBuffers.asNativeBuffer(filename);
+ NativeBuffer typeBuffer = NativeBuffers.asNativeBuffer(type);
+ try {
+ return setmntent0(pathBuffer.address(), typeBuffer.address());
+ } finally {
+ typeBuffer.release();
+ pathBuffer.release();
+ }
+ }
+ private static native long setmntent0(long pathAddress, long typeAddress)
+ throws UnixException;
+
+ /**
+ * int endmntent(FILE* filep);
+ */
+ static native void endmntent(long stream) throws UnixException;
+
+ /**
+ * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
+ */
+ static int fgetxattr(int filedes, byte[] name, long valueAddress,
+ int valueLen) throws UnixException
+ {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
+ try {
+ return fgetxattr0(filedes, buffer.address(), valueAddress, valueLen);
+ } finally {
+ buffer.release();
+ }
+ }
+
+ private static native int fgetxattr0(int filedes, long nameAddress,
+ long valueAdddress, int valueLen) throws UnixException;
+
+ /**
+ * fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
+ */
+ static void fsetxattr(int filedes, byte[] name, long valueAddress,
+ int valueLen) throws UnixException
+ {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
+ try {
+ fsetxattr0(filedes, buffer.address(), valueAddress, valueLen);
+ } finally {
+ buffer.release();
+ }
+ }
+
+ private static native void fsetxattr0(int filedes, long nameAddress,
+ long valueAdddress, int valueLen) throws UnixException;
+
+
+ /**
+ * fremovexattr(int filedes, const char *name);
+ */
+ static void fremovexattr(int filedes, byte[] name) throws UnixException {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
+ try {
+ fremovexattr0(filedes, buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+
+ private static native void fremovexattr0(int filedes, long nameAddress)
+ throws UnixException;
+
+ /**
+ * size_t flistxattr(int filedes, const char *list, size_t size)
+ */
+ static native int flistxattr(int filedes, long listAddress, int size)
+ throws UnixException;
+
+ // initialize
+ private static native void init();
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ init();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java b/src/solaris/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java
new file mode 100644
index 000000000..b51a9e9cb
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.util.*;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.UnixConstants.*;
+import static sun.nio.fs.LinuxNativeDispatcher.*;
+
+/**
+ * Linux implementation of UserDefinedFileAttributeView using extended attributes.
+ */
+
+class LinuxUserDefinedFileAttributeView
+ extends AbstractUserDefinedFileAttributeView
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // namespace for extended user attributes
+ private static final String USER_NAMESPACE = "user.";
+
+ // maximum bytes in extended attribute name (includes namespace)
+ private static final int XATTR_NAME_MAX = 255;
+
+ private byte[] nameAsBytes(UnixPath file, String name) throws IOException {
+ if (name == null)
+ throw new NullPointerException("'name' is null");
+ name = USER_NAMESPACE + name;
+ byte[] bytes = name.getBytes();
+ if (bytes.length > XATTR_NAME_MAX) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "'" + name + "' is too big");
+ }
+ return bytes;
+ }
+
+ // Parses buffer as array of NULL-terminated C strings.
+ private List<String> asList(long address, int size) {
+ final List<String> list = new ArrayList<String>();
+ int start = 0;
+ int pos = 0;
+ while (pos < size) {
+ if (unsafe.getByte(address + pos) == 0) {
+ int len = pos - start;
+ byte[] value = new byte[len];
+ unsafe.copyMemory(null, address+start, value,
+ Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
+ String s = new String(value);
+ if (s.startsWith(USER_NAMESPACE)) {
+ s = s.substring(USER_NAMESPACE.length());
+ list.add(s);
+ }
+ start = pos + 1;
+ }
+ pos++;
+ }
+ return list;
+ }
+
+ private final UnixPath file;
+ private final boolean followLinks;
+
+ LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ @Override
+ public List<String> list() throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ NativeBuffer buffer = null;
+ try {
+ int size = 1024;
+ buffer = NativeBuffers.getNativeBuffer(size);
+ for (;;) {
+ try {
+ int n = flistxattr(fd, buffer.address(), size);
+ List<String> list = asList(buffer.address(), n);
+ return Collections.unmodifiableList(list);
+ } catch (UnixException x) {
+ // allocate larger buffer if required
+ if (x.errno() == ERANGE && size < 32*1024) {
+ buffer.release();
+ size *= 2;
+ buffer = null;
+ buffer = NativeBuffers.getNativeBuffer(size);
+ continue;
+ }
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to get list of extended attributes: " +
+ x.getMessage());
+ }
+ }
+ } finally {
+ if (buffer != null)
+ buffer.release();
+ close(fd);
+ }
+ }
+
+ @Override
+ public int size(String name) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ // fgetxattr returns size if called with size==0
+ return fgetxattr(fd, nameAsBytes(file,name), 0L, 0);
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to get size of extended attribute '" + name +
+ "': " + x.getMessage());
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public int read(String name, ByteBuffer dst) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ if (dst.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+ int pos = dst.position();
+ int lim = dst.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ NativeBuffer nb;
+ long address;
+ if (dst instanceof sun.nio.ch.DirectBuffer) {
+ nb = null;
+ address = ((sun.nio.ch.DirectBuffer)dst).address() + pos;
+ } else {
+ // substitute with native buffer
+ nb = NativeBuffers.getNativeBuffer(rem);
+ address = nb.address();
+ }
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ try {
+ int n = fgetxattr(fd, nameAsBytes(file,name), address, rem);
+
+ // if remaining is zero then fgetxattr returns the size
+ if (rem == 0) {
+ if (n > 0)
+ throw new UnixException(ERANGE);
+ return 0;
+ }
+
+ // copy from buffer into backing array if necessary
+ if (nb != null) {
+ int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
+ unsafe.copyMemory(null, address, dst.array(), off, n);
+ }
+ dst.position(pos + n);
+ return n;
+ } catch (UnixException x) {
+ String msg = (x.errno() == ERANGE) ?
+ "Insufficient space in buffer" : x.getMessage();
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Error reading extended attribute '" + name + "': " + msg);
+ } finally {
+ close(fd);
+ }
+ } finally {
+ if (nb != null)
+ nb.release();
+ }
+ }
+
+ @Override
+ public int write(String name, ByteBuffer src) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), false, true);
+
+ int pos = src.position();
+ int lim = src.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ NativeBuffer nb;
+ long address;
+ if (src instanceof sun.nio.ch.DirectBuffer) {
+ nb = null;
+ address = ((sun.nio.ch.DirectBuffer)src).address() + pos;
+ } else {
+ // substitute with native buffer
+ nb = NativeBuffers.getNativeBuffer(rem);
+ address = nb.address();
+
+ if (src.hasArray()) {
+ // copy from backing array into buffer
+ int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
+ unsafe.copyMemory(src.array(), off, null, address, rem);
+ } else {
+ // backing array not accessible so transfer via temporary array
+ byte[] tmp = new byte[rem];
+ src.get(tmp);
+ src.position(pos); // reset position as write may fail
+ unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
+ address, rem);
+ }
+ }
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ try {
+ fsetxattr(fd, nameAsBytes(file,name), address, rem);
+ src.position(pos + rem);
+ return rem;
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Error writing extended attribute '" + name + "': " +
+ x.getMessage());
+ } finally {
+ close(fd);
+ }
+ } finally {
+ if (nb != null)
+ nb.release();
+ }
+ }
+
+ @Override
+ public void delete(String name) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), false, true);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ fremovexattr(fd, nameAsBytes(file,name));
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to delete extended attribute '" + name + "': " + x.getMessage());
+ } finally {
+ close(fd);
+ }
+ }
+
+ /**
+ * Used by copyTo/moveTo to copy extended attributes from source to target.
+ *
+ * @param ofd
+ * file descriptor for source file
+ * @param nfd
+ * file descriptor for target file
+ */
+ static void copyExtendedAttributes(int ofd, int nfd) {
+ NativeBuffer buffer = null;
+ try {
+
+ // call flistxattr to get list of extended attributes.
+ int size = 1024;
+ buffer = NativeBuffers.getNativeBuffer(size);
+ for (;;) {
+ try {
+ size = flistxattr(ofd, buffer.address(), size);
+ break;
+ } catch (UnixException x) {
+ // allocate larger buffer if required
+ if (x.errno() == ERANGE && size < 32*1024) {
+ buffer.release();
+ size *= 2;
+ buffer = null;
+ buffer = NativeBuffers.getNativeBuffer(size);
+ continue;
+ }
+
+ // unable to get list of attributes
+ return;
+ }
+ }
+
+ // parse buffer as array of NULL-terminated C strings.
+ long address = buffer.address();
+ int start = 0;
+ int pos = 0;
+ while (pos < size) {
+ if (unsafe.getByte(address + pos) == 0) {
+ // extract attribute name and copy attribute to target.
+ // FIXME: We can avoid needless copying by using address+pos
+ // as the address of the name.
+ int len = pos - start;
+ byte[] name = new byte[len];
+ unsafe.copyMemory(null, address+start, name,
+ Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
+ try {
+ copyExtendedAttribute(ofd, name, nfd);
+ } catch (UnixException ignore) {
+ // ignore
+ }
+ start = pos + 1;
+ }
+ pos++;
+ }
+
+ } finally {
+ if (buffer != null)
+ buffer.release();
+ }
+ }
+
+ private static void copyExtendedAttribute(int ofd, byte[] name, int nfd)
+ throws UnixException
+ {
+ int size = fgetxattr(ofd, name, 0L, 0);
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ long address = buffer.address();
+ size = fgetxattr(ofd, name, address, size);
+ fsetxattr(nfd, name, address, size);
+ } finally {
+ buffer.release();
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/LinuxWatchService.java b/src/solaris/classes/sun/nio/fs/LinuxWatchService.java
new file mode 100644
index 000000000..0d4a2ffdd
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/LinuxWatchService.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+/**
+ * Linux implementation of WatchService based on inotify.
+ *
+ * In summary a background thread polls inotify plus a socket used for the wakeup
+ * mechanism. Requests to add or remove a watch, or close the watch service,
+ * cause the thread to wakeup and process the request. Events are processed
+ * by the thread which causes it to signal/queue the corresponding watch keys.
+ */
+
+class LinuxWatchService
+ extends AbstractWatchService
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // background thread to read change events
+ private final Poller poller;
+
+ LinuxWatchService(UnixFileSystem fs) throws IOException {
+ // initialize inotify
+ int ifd = - 1;
+ try {
+ ifd = inotifyInit();
+ } catch (UnixException x) {
+ throw new IOException(x.errorString());
+ }
+
+ // configure inotify to be non-blocking
+ // create socketpair used in the close mechanism
+ int sp[] = new int[2];
+ try {
+ configureBlocking(ifd, false);
+ socketpair(sp);
+ configureBlocking(sp[0], false);
+ } catch (UnixException x) {
+ UnixNativeDispatcher.close(ifd);
+ throw new IOException(x.errorString());
+ }
+
+ this.poller = new Poller(fs, this, ifd, sp);
+ this.poller.start();
+ }
+
+ @Override
+ WatchKey register(Path dir,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ // delegate to poller
+ return poller.register(dir, events, modifiers);
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // delegate to poller
+ poller.close();
+ }
+
+ /**
+ * WatchKey implementation
+ */
+ private static class LinuxWatchKey extends AbstractWatchKey {
+ // inotify descriptor
+ private final int ifd;
+ // watch descriptor
+ private volatile int wd;
+
+ LinuxWatchKey(LinuxWatchService watcher, int ifd, int wd) {
+ super(watcher);
+ this.ifd = ifd;
+ this.wd = wd;
+ }
+
+ int descriptor() {
+ return wd;
+ }
+
+ void invalidate(boolean remove) {
+ if (remove) {
+ try {
+ inotifyRmWatch(ifd, wd);
+ } catch (UnixException x) {
+ // ignore
+ }
+ }
+ wd = -1;
+ }
+
+ @Override
+ public boolean isValid() {
+ return (wd != -1);
+ }
+
+ @Override
+ public void cancel() {
+ if (isValid()) {
+ // delegate to poller
+ ((LinuxWatchService)watcher()).poller.cancel(this);
+ }
+ }
+ }
+
+ /**
+ * Background thread to read from inotify
+ */
+ private static class Poller extends AbstractPoller {
+ /**
+ * struct inotify_event {
+ * int wd;
+ * uint32_t mask;
+ * uint32_t len;
+ * char name __flexarr; // present if len > 0
+ * } act_t;
+ */
+ private static final int SIZEOF_INOTIFY_EVENT = eventSize();
+ private static final int[] offsets = eventOffsets();
+ private static final int OFFSETOF_WD = offsets[0];
+ private static final int OFFSETOF_MASK = offsets[1];
+ private static final int OFFSETOF_LEN = offsets[3];
+ private static final int OFFSETOF_NAME = offsets[4];
+
+ private static final int IN_MODIFY = 0x00000002;
+ private static final int IN_ATTRIB = 0x00000004;
+ private static final int IN_MOVED_FROM = 0x00000040;
+ private static final int IN_MOVED_TO = 0x00000080;
+ private static final int IN_CREATE = 0x00000100;
+ private static final int IN_DELETE = 0x00000200;
+
+ private static final int IN_UNMOUNT = 0x00002000;
+ private static final int IN_Q_OVERFLOW = 0x00004000;
+ private static final int IN_IGNORED = 0x00008000;
+
+ // sizeof buffer for when polling inotify
+ private static final int BUFFER_SIZE = 8192;
+
+ private final UnixFileSystem fs;
+ private final LinuxWatchService watcher;
+
+ // inotify file descriptor
+ private final int ifd;
+ // socketpair used to shutdown polling thread
+ private final int socketpair[];
+ // maps watch descriptor to Key
+ private final Map<Integer,LinuxWatchKey> wdToKey;
+ // address of read buffer
+ private final long address;
+
+ Poller(UnixFileSystem fs, LinuxWatchService watcher, int ifd, int[] sp) {
+ this.fs = fs;
+ this.watcher = watcher;
+ this.ifd = ifd;
+ this.socketpair = sp;
+ this.wdToKey = new HashMap<Integer,LinuxWatchKey>();
+ this.address = unsafe.allocateMemory(BUFFER_SIZE);
+ }
+
+ @Override
+ void wakeup() throws IOException {
+ // write to socketpair to wakeup polling thread
+ try {
+ write(socketpair[1], address, 1);
+ } catch (UnixException x) {
+ throw new IOException(x.errorString());
+ }
+ }
+
+ @Override
+ Object implRegister(Path obj,
+ Set<? extends WatchEvent.Kind<?>> events,
+ WatchEvent.Modifier... modifiers)
+ {
+ UnixPath dir = (UnixPath)obj;
+
+ int mask = 0;
+ for (WatchEvent.Kind<?> event: events) {
+ if (event == StandardWatchEventKind.ENTRY_CREATE) {
+ mask |= IN_CREATE | IN_MOVED_TO;
+ continue;
+ }
+ if (event == StandardWatchEventKind.ENTRY_DELETE) {
+ mask |= IN_DELETE | IN_MOVED_FROM;
+ continue;
+ }
+ if (event == StandardWatchEventKind.ENTRY_MODIFY) {
+ mask |= IN_MODIFY | IN_ATTRIB;
+ continue;
+ }
+ }
+
+ // no modifiers supported at this time
+ if (modifiers.length > 0) {
+ for (WatchEvent.Modifier modifier: modifiers) {
+ if (modifier == null)
+ return new NullPointerException();
+ if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
+ continue; // ignore
+ return new UnsupportedOperationException("Modifier not supported");
+ }
+ }
+
+ // check file is directory
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(dir, true);
+ } catch (UnixException x) {
+ return x.asIOException(dir);
+ }
+ if (!attrs.isDirectory()) {
+ return new NotDirectoryException(dir.getPathForExecptionMessage());
+ }
+
+ // register with inotify (replaces existing mask if already registered)
+ int wd = -1;
+ try {
+ NativeBuffer buffer =
+ NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls());
+ try {
+ wd = inotifyAddWatch(ifd, buffer.address(), mask);
+ } finally {
+ buffer.release();
+ }
+ } catch (UnixException x) {
+ if (x.errno() == ENOSPC) {
+ return new IOException("User limit of inotify watches reached");
+ }
+ return x.asIOException(dir);
+ }
+
+ // ensure watch descriptor is in map
+ LinuxWatchKey key = wdToKey.get(wd);
+ if (key == null) {
+ key = new LinuxWatchKey(watcher, ifd, wd);
+ wdToKey.put(wd, key);
+ }
+ return key;
+ }
+
+ // cancel single key
+ @Override
+ void implCancelKey(WatchKey obj) {
+ LinuxWatchKey key = (LinuxWatchKey)obj;
+ if (key.isValid()) {
+ wdToKey.remove(key.descriptor());
+ key.invalidate(true);
+ }
+ }
+
+ // close watch service
+ @Override
+ void implCloseAll() {
+ // invalidate all keys
+ for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
+ entry.getValue().invalidate(true);
+ }
+ wdToKey.clear();
+
+ // free resources
+ unsafe.freeMemory(address);
+ UnixNativeDispatcher.close(socketpair[0]);
+ UnixNativeDispatcher.close(socketpair[1]);
+ UnixNativeDispatcher.close(ifd);
+ }
+
+ /**
+ * Poller main loop
+ */
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ int nReady, bytesRead;
+
+ // wait for close or inotify event
+ nReady = poll(ifd, socketpair[0]);
+
+ // read from inotify
+ try {
+ bytesRead = read(ifd, address, BUFFER_SIZE);
+ } catch (UnixException x) {
+ if (x.errno() != EAGAIN)
+ throw x;
+ bytesRead = 0;
+ }
+
+ // process any pending requests
+ if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) {
+ try {
+ read(socketpair[0], address, BUFFER_SIZE);
+ boolean shutdown = processRequests();
+ if (shutdown)
+ break;
+ } catch (UnixException x) {
+ if (x.errno() != UnixConstants.EAGAIN)
+ throw x;
+ }
+ }
+
+ // iterate over buffer to decode events
+ int offset = 0;
+ while (offset < bytesRead) {
+ long event = address + offset;
+ int wd = unsafe.getInt(event + OFFSETOF_WD);
+ int mask = unsafe.getInt(event + OFFSETOF_MASK);
+ int len = unsafe.getInt(event + OFFSETOF_LEN);
+
+ // file name
+ UnixPath name = null;
+ if (len > 0) {
+ int actual = len;
+
+ // null-terminated and maybe additional null bytes to
+ // align the next event
+ while (actual > 0) {
+ long last = event + OFFSETOF_NAME + actual - 1;
+ if (unsafe.getByte(last) != 0)
+ break;
+ actual--;
+ }
+ if (actual > 0) {
+ byte[] buf = new byte[actual];
+ unsafe.copyMemory(null, event + OFFSETOF_NAME,
+ buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual);
+ name = new UnixPath(fs, buf);
+ }
+ }
+
+ // process event
+ processEvent(wd, mask, name);
+
+ offset += (SIZEOF_INOTIFY_EVENT + len);
+ }
+ }
+ } catch (UnixException x) {
+ x.printStackTrace();
+ }
+ }
+
+
+ /**
+ * map inotify event to WatchEvent.Kind
+ */
+ private WatchEvent.Kind<?> maskToEventKind(int mask) {
+ if ((mask & IN_MODIFY) > 0)
+ return StandardWatchEventKind.ENTRY_MODIFY;
+ if ((mask & IN_ATTRIB) > 0)
+ return StandardWatchEventKind.ENTRY_MODIFY;
+ if ((mask & IN_CREATE) > 0)
+ return StandardWatchEventKind.ENTRY_CREATE;
+ if ((mask & IN_MOVED_TO) > 0)
+ return StandardWatchEventKind.ENTRY_CREATE;
+ if ((mask & IN_DELETE) > 0)
+ return StandardWatchEventKind.ENTRY_DELETE;
+ if ((mask & IN_MOVED_FROM) > 0)
+ return StandardWatchEventKind.ENTRY_DELETE;
+ return null;
+ }
+
+ /**
+ * Process event from inotify
+ */
+ private void processEvent(int wd, int mask, final UnixPath name) {
+ // overflow - signal all keys
+ if ((mask & IN_Q_OVERFLOW) > 0) {
+ for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
+ entry.getValue()
+ .signalEvent(StandardWatchEventKind.OVERFLOW, null);
+ }
+ return;
+ }
+
+ // lookup wd to get key
+ LinuxWatchKey key = wdToKey.get(wd);
+ if (key == null)
+ return; // should not happen
+
+ // file deleted
+ if ((mask & IN_IGNORED) > 0) {
+ wdToKey.remove(wd);
+ key.invalidate(false);
+ key.signal();
+ return;
+ }
+
+ // event for directory itself
+ if (name == null)
+ return;
+
+ // map to event and queue to key
+ WatchEvent.Kind<?> kind = maskToEventKind(mask);
+ if (kind != null) {
+ key.signalEvent(kind, name);
+ }
+ }
+ }
+
+ // -- native methods --
+
+ private static native void init();
+
+ // sizeof inotify_event
+ private static native int eventSize();
+
+ // offsets of inotify_event
+ private static native int[] eventOffsets();
+
+ private static native int inotifyInit() throws UnixException;
+
+ private static native int inotifyAddWatch(int fd, long pathAddress, int mask)
+ throws UnixException;
+
+ private static native void inotifyRmWatch(int fd, int wd)
+ throws UnixException;
+
+ private static native void configureBlocking(int fd, boolean blocking)
+ throws UnixException;
+
+ private static native void socketpair(int[] sv) throws UnixException;
+
+ private static native int poll(int fd1, int fd2) throws UnixException;
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ init();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java b/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java
new file mode 100644
index 000000000..e96361e20
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.UnixConstants.*;
+import static sun.nio.fs.SolarisConstants.*;
+import static sun.nio.fs.SolarisNativeDispatcher.*;
+
+
+/**
+ * Solaris implementation of AclFileAttributeView with native support for
+ * NFSv4 ACLs on ZFS.
+ */
+
+class SolarisAclFileAttributeView
+ extends AbstractAclFileAttributeView
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // Maximum number of entries allowed in an ACL
+ private static final int MAX_ACL_ENTRIES = 1024;
+
+ /**
+ * typedef struct ace {
+ * uid_t a_who;
+ * uitn32_t a_access_mark;
+ * uint16_t a_flags;
+ * uint16_t a_type;
+ * } act_t;
+ */
+ private static final short SIZEOF_ACE_T = 12;
+ private static final short OFFSETOF_UID = 0;
+ private static final short OFFSETOF_MASK = 4;
+ private static final short OFFSETOF_FLAGS = 8;
+ private static final short OFFSETOF_TYPE = 10;
+
+ private final UnixPath file;
+ private final boolean followLinks;
+
+ SolarisAclFileAttributeView(UnixPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ /**
+ * Permission checks to access file
+ */
+ private void checkAccess(UnixPath file,
+ boolean checkRead,
+ boolean checkWrite)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (checkRead)
+ file.checkRead();
+ if (checkWrite)
+ file.checkWrite();
+ sm.checkPermission(new RuntimePermission("accessUserInformation"));
+ }
+ }
+
+ /**
+ * Encode the ACL to the given buffer
+ */
+ private static void encode(List<AclEntry> acl, long address) {
+ long offset = address;
+ for (AclEntry ace: acl) {
+ int flags = 0;
+
+ // map UserPrincipal to uid and flags
+ UserPrincipal who = ace.principal();
+ if (!(who instanceof UnixUserPrincipals))
+ throw new ProviderMismatchException();
+ UnixUserPrincipals.User user = (UnixUserPrincipals.User)who;
+ int uid;
+ if (user.isSpecial()) {
+ uid = -1;
+ if (who.getName().equals(UnixUserPrincipals.SPECIAL_OWNER.getName()))
+ flags |= ACE_OWNER;
+ else if (who.getName().equals(UnixUserPrincipals.SPECIAL_GROUP.getName()))
+ flags |= ACE_GROUP;
+ else if (who.getName().equals(UnixUserPrincipals.SPECIAL_EVERYONE.getName()))
+ flags |= ACE_EVERYONE;
+ else
+ throw new AssertionError("Unable to map special identifier");
+ } else {
+ if (user instanceof UnixUserPrincipals.Group) {
+ uid = user.gid();
+ flags |= ACE_IDENTIFIER_GROUP;
+ } else {
+ uid = user.uid();
+ }
+ }
+
+ // map ACE type
+ int type;
+ switch (ace.type()) {
+ case ALLOW:
+ type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ break;
+ case DENY:
+ type = ACE_ACCESS_DENIED_ACE_TYPE;
+ break;
+ case AUDIT:
+ type = ACE_SYSTEM_AUDIT_ACE_TYPE;
+ break;
+ case ALARM:
+ type = ACE_SYSTEM_ALARM_ACE_TYPE;
+ break;
+ default:
+ throw new AssertionError("Unable to map ACE type");
+ }
+
+ // map permissions
+ Set<AclEntryPermission> aceMask = ace.permissions();
+ int mask = 0;
+ if (aceMask.contains(AclEntryPermission.READ_DATA))
+ mask |= ACE_READ_DATA;
+ if (aceMask.contains(AclEntryPermission.WRITE_DATA))
+ mask |= ACE_WRITE_DATA;
+ if (aceMask.contains(AclEntryPermission.APPEND_DATA))
+ mask |= ACE_APPEND_DATA;
+ if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS))
+ mask |= ACE_READ_NAMED_ATTRS;
+ if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS))
+ mask |= ACE_WRITE_NAMED_ATTRS;
+ if (aceMask.contains(AclEntryPermission.EXECUTE))
+ mask |= ACE_EXECUTE;
+ if (aceMask.contains(AclEntryPermission.DELETE_CHILD))
+ mask |= ACE_DELETE_CHILD;
+ if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES))
+ mask |= ACE_READ_ATTRIBUTES;
+ if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES))
+ mask |= ACE_WRITE_ATTRIBUTES;
+ if (aceMask.contains(AclEntryPermission.DELETE))
+ mask |= ACE_DELETE;
+ if (aceMask.contains(AclEntryPermission.READ_ACL))
+ mask |= ACE_READ_ACL;
+ if (aceMask.contains(AclEntryPermission.WRITE_ACL))
+ mask |= ACE_WRITE_ACL;
+ if (aceMask.contains(AclEntryPermission.WRITE_OWNER))
+ mask |= ACE_WRITE_OWNER;
+ if (aceMask.contains(AclEntryPermission.SYNCHRONIZE))
+ mask |= ACE_SYNCHRONIZE;
+
+ // FIXME - it would be desirable to know here if the file is a
+ // directory or not. Solaris returns EINVAL if an ACE has a directory
+ // -only flag and the file is not a directory.
+ Set<AclEntryFlag> aceFlags = ace.flags();
+ if (aceFlags.contains(AclEntryFlag.FILE_INHERIT))
+ flags |= ACE_FILE_INHERIT_ACE;
+ if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT))
+ flags |= ACE_DIRECTORY_INHERIT_ACE;
+ if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
+ flags |= ACE_NO_PROPAGATE_INHERIT_ACE;
+ if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY))
+ flags |= ACE_INHERIT_ONLY_ACE;
+
+ unsafe.putInt(offset + OFFSETOF_UID, uid);
+ unsafe.putInt(offset + OFFSETOF_MASK, mask);
+ unsafe.putShort(offset + OFFSETOF_FLAGS, (short)flags);
+ unsafe.putShort(offset + OFFSETOF_TYPE, (short)type);
+
+ offset += SIZEOF_ACE_T;
+ }
+ }
+
+ /**
+ * Decode the buffer, returning an ACL
+ */
+ private static List<AclEntry> decode(long address, int n) {
+ ArrayList<AclEntry> acl = new ArrayList<AclEntry>(n);
+ for (int i=0; i<n; i++) {
+ long offset = address + i*SIZEOF_ACE_T;
+
+ int uid = unsafe.getInt(offset + OFFSETOF_UID);
+ int mask = unsafe.getInt(offset + OFFSETOF_MASK);
+ int flags = (int)unsafe.getShort(offset + OFFSETOF_FLAGS);
+ int type = (int)unsafe.getShort(offset + OFFSETOF_TYPE);
+
+ // map uid and flags to UserPrincipal
+ UnixUserPrincipals.User who = null;
+ if (uid == -1) {
+ if ((flags & ACE_OWNER) > 0)
+ who = UnixUserPrincipals.SPECIAL_OWNER;
+ if ((flags & ACE_GROUP) > 0)
+ who = UnixUserPrincipals.SPECIAL_GROUP;
+ if ((flags & ACE_EVERYONE) > 0)
+ who = UnixUserPrincipals.SPECIAL_EVERYONE;
+ if (who == null)
+ throw new AssertionError("ACE who not handled");
+ } else {
+ // can be gid
+ if ((flags & ACE_IDENTIFIER_GROUP) > 0)
+ who = UnixUserPrincipals.fromGid(uid);
+ else
+ who = UnixUserPrincipals.fromUid(uid);
+ }
+
+ AclEntryType aceType = null;
+ switch (type) {
+ case ACE_ACCESS_ALLOWED_ACE_TYPE:
+ aceType = AclEntryType.ALLOW;
+ break;
+ case ACE_ACCESS_DENIED_ACE_TYPE:
+ aceType = AclEntryType.DENY;
+ break;
+ case ACE_SYSTEM_AUDIT_ACE_TYPE:
+ aceType = AclEntryType.AUDIT;
+ break;
+ case ACE_SYSTEM_ALARM_ACE_TYPE:
+ aceType = AclEntryType.ALARM;
+ break;
+ default:
+ assert false;
+ }
+
+ HashSet<AclEntryPermission> aceMask = new HashSet<AclEntryPermission>();
+ if ((mask & ACE_READ_DATA) > 0)
+ aceMask.add(AclEntryPermission.READ_DATA);
+ if ((mask & ACE_WRITE_DATA) > 0)
+ aceMask.add(AclEntryPermission.WRITE_DATA);
+ if ((mask & ACE_APPEND_DATA ) > 0)
+ aceMask.add(AclEntryPermission.APPEND_DATA);
+ if ((mask & ACE_READ_NAMED_ATTRS) > 0)
+ aceMask.add(AclEntryPermission.READ_NAMED_ATTRS);
+ if ((mask & ACE_WRITE_NAMED_ATTRS) > 0)
+ aceMask.add(AclEntryPermission.WRITE_NAMED_ATTRS);
+ if ((mask & ACE_EXECUTE) > 0)
+ aceMask.add(AclEntryPermission.EXECUTE);
+ if ((mask & ACE_DELETE_CHILD ) > 0)
+ aceMask.add(AclEntryPermission.DELETE_CHILD);
+ if ((mask & ACE_READ_ATTRIBUTES) > 0)
+ aceMask.add(AclEntryPermission.READ_ATTRIBUTES);
+ if ((mask & ACE_WRITE_ATTRIBUTES) > 0)
+ aceMask.add(AclEntryPermission.WRITE_ATTRIBUTES);
+ if ((mask & ACE_DELETE) > 0)
+ aceMask.add(AclEntryPermission.DELETE);
+ if ((mask & ACE_READ_ACL) > 0)
+ aceMask.add(AclEntryPermission.READ_ACL);
+ if ((mask & ACE_WRITE_ACL) > 0)
+ aceMask.add(AclEntryPermission.WRITE_ACL);
+ if ((mask & ACE_WRITE_OWNER) > 0)
+ aceMask.add(AclEntryPermission.WRITE_OWNER);
+ if ((mask & ACE_SYNCHRONIZE) > 0)
+ aceMask.add(AclEntryPermission.SYNCHRONIZE);
+
+ HashSet<AclEntryFlag> aceFlags = new HashSet<AclEntryFlag>();
+ if ((flags & ACE_FILE_INHERIT_ACE) > 0)
+ aceFlags.add(AclEntryFlag.FILE_INHERIT);
+ if ((flags & ACE_DIRECTORY_INHERIT_ACE) > 0)
+ aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT);
+ if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0)
+ aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
+ if ((flags & ACE_INHERIT_ONLY_ACE ) > 0)
+ aceFlags.add(AclEntryFlag.INHERIT_ONLY);
+
+ // build the ACL entry and add it to the list
+ AclEntry ace = AclEntry.newBuilder()
+ .setType(aceType)
+ .setPrincipal(who)
+ .setPermissions(aceMask).setFlags(aceFlags).build();
+ acl.add(ace);
+ }
+
+ return acl;
+ }
+
+ // Retrns true if NFSv4 ACLs not enabled on file system
+ private static boolean isAclsEnabled(int fd) {
+ try {
+ long enabled = fpathconf(fd, _PC_ACL_ENABLED);
+ if (enabled == _ACL_ACE_ENABLED)
+ return true;
+ } catch (UnixException x) {
+ }
+ return false;
+ }
+
+ @Override
+ public List<AclEntry> getAcl()
+ throws IOException
+ {
+ // permission check
+ checkAccess(file, true, false);
+
+ // open file (will fail if file is a link and not following links)
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ long address = unsafe.allocateMemory(SIZEOF_ACE_T * MAX_ACL_ENTRIES);
+ try {
+ // read ACL and decode it
+ int n = facl(fd, ACE_GETACL, MAX_ACL_ENTRIES, address);
+ assert n >= 0;
+ return decode(address, n);
+ } catch (UnixException x) {
+ if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, x.getMessage() + " (file system does not support NFSv4 ACLs)");
+ }
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ } finally {
+ unsafe.freeMemory(address);
+ }
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public void setAcl(List<AclEntry> acl) throws IOException {
+ // permission check
+ checkAccess(file, false, true);
+
+ // open file (will fail if file is a link and not following links)
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ // SECURITY: need to copy list as can change during processing
+ acl = new ArrayList<AclEntry>(acl);
+ int n = acl.size();
+
+ long address = unsafe.allocateMemory(SIZEOF_ACE_T * n);
+ try {
+ encode(acl, address);
+ facl(fd, ACE_SETACL, n, address);
+ } catch (UnixException x) {
+ if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, x.getMessage() + " (file system does not support NFSv4 ACLs)");
+ }
+ if (x.errno() == EINVAL && (n < 3))
+ throw new IOException("ACL must contain at least 3 entries");
+ x.rethrowAsIOException(file);
+ } finally {
+ unsafe.freeMemory(address);
+ }
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public UserPrincipal getOwner()
+ throws IOException
+ {
+ checkAccess(file, true, false);
+
+ try {
+ UnixFileAttributes attrs =
+ UnixFileAttributes.get(file, followLinks);
+ return UnixUserPrincipals.fromUid(attrs.uid());
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compile happy
+ }
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner) throws IOException {
+ checkAccess(file, true, false);
+
+ if (!(owner instanceof UnixUserPrincipals.User))
+ throw new ProviderMismatchException();
+ if (owner instanceof UnixUserPrincipals.Group)
+ throw new IOException("'owner' parameter is a group");
+ int uid = ((UnixUserPrincipals.User)owner).uid();
+
+ try {
+ if (followLinks) {
+ lchown(file, uid, -1);
+ } else {
+ chown(file, uid, -1);
+ }
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisFileStore.java b/src/solaris/classes/sun/nio/fs/SolarisFileStore.java
new file mode 100644
index 000000000..bdf71a559
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisFileStore.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.io.IOException;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.SolarisConstants.*;
+
+/**
+ * Solaris implementation of FileStore
+ */
+
+class SolarisFileStore
+ extends UnixFileStore
+{
+ private final boolean xattrEnabled;
+
+ SolarisFileStore(UnixPath file) throws IOException {
+ super(file);
+ this.xattrEnabled = xattrEnabled();
+ }
+
+ SolarisFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
+ super(fs, entry);
+ this.xattrEnabled = xattrEnabled();
+ }
+
+ // returns true if extended attributes enabled
+ private boolean xattrEnabled() {
+ long res = 0L;
+ try {
+ res = pathconf(file(), _PC_XATTR_ENABLED);
+ } catch (UnixException x) {
+ // ignore
+ }
+ return (res != 0L);
+ }
+
+ @Override
+ UnixMountEntry findMountEntry() throws IOException {
+ // On Solaris iterate over the entries in the mount table to find device
+ for (UnixMountEntry entry: file().getFileSystem().getMountEntries()) {
+ if (entry.dev() == dev()) {
+ return entry;
+ }
+ }
+ throw new IOException("Device not found in mnttab");
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name) {
+ if (name.equals("acl")) {
+ // lookup fstypes.properties
+ FeatureStatus status = checkIfFeaturePresent("nfsv4acl");
+ if (status == FeatureStatus.PRESENT)
+ return true;
+ if (status == FeatureStatus.NOT_PRESENT)
+ return false;
+ // AclFileAttributeView available on ZFS
+ return (type().equals("zfs"));
+ }
+ if (name.equals("xattr")) {
+ // lookup fstypes.properties
+ FeatureStatus status = checkIfFeaturePresent("xattr");
+ if (status == FeatureStatus.PRESENT)
+ return true;
+ if (status == FeatureStatus.NOT_PRESENT)
+ return false;
+ return xattrEnabled;
+ }
+
+ return super.supportsFileAttributeView(name);
+ }
+
+ @Override
+ boolean isLoopback() {
+ return type().equals("lofs");
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisFileSystem.java b/src/solaris/classes/sun/nio/fs/SolarisFileSystem.java
new file mode 100644
index 000000000..fa7f34f53
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisFileSystem.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+import static sun.nio.fs.UnixNativeDispatcher.*;
+
+/**
+ * Solaris implementation of FileSystem
+ */
+
+class SolarisFileSystem extends UnixFileSystem {
+ private final boolean hasSolaris11Features;
+
+ SolarisFileSystem(UnixFileSystemProvider provider, String dir) {
+ super(provider, dir);
+
+ // check os.version
+ String osversion = AccessController
+ .doPrivileged(new GetPropertyAction("os.version"));
+ String[] vers = osversion.split("\\.", 0);
+ assert vers.length >= 2;
+ int majorVersion = Integer.parseInt(vers[0]);
+ int minorVersion = Integer.parseInt(vers[1]);
+ this.hasSolaris11Features =
+ (majorVersion > 5 || (majorVersion == 5 && minorVersion >= 11));
+ }
+
+ @Override
+ boolean isSolaris() {
+ return true;
+ }
+
+ @Override
+ public WatchService newWatchService()
+ throws IOException
+ {
+ // FEN available since Solaris 11
+ if (hasSolaris11Features) {
+ return new SolarisWatchService(this);
+ } else {
+ return new PollingWatchService();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileAttributeView> V newFileAttributeView(Class<V> view,
+ UnixPath file, LinkOption... options)
+ {
+ if (view == AclFileAttributeView.class)
+ return (V) new SolarisAclFileAttributeView(file, followLinks(options));
+ if (view == UserDefinedFileAttributeView.class) {
+ return(V) new SolarisUserDefinedFileAttributeView(file, followLinks(options));
+ }
+ return super.newFileAttributeView(view, file, options);
+ }
+
+ @Override
+ protected FileAttributeView newFileAttributeView(String name,
+ UnixPath file,
+ LinkOption... options)
+ {
+ if (name.equals("acl"))
+ return new SolarisAclFileAttributeView(file, followLinks(options));
+ if (name.equals("xattr"))
+ return new SolarisUserDefinedFileAttributeView(file, followLinks(options));
+ return super.newFileAttributeView(name, file, options);
+ }
+
+ // lazy initialization of the list of supported attribute views
+ private static class SupportedFileFileAttributeViewsHolder {
+ static final Set<String> supportedFileAttributeViews =
+ supportedFileAttributeViews();
+ private static Set<String> supportedFileAttributeViews() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(UnixFileSystem.standardFileAttributeViews());
+ // additional Solaris-specific views
+ result.add("acl");
+ result.add("xattr");
+ return Collections.unmodifiableSet(result);
+ }
+ }
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
+ }
+
+ @Override
+ void copyNonPosixAttributes(int ofd, int nfd) {
+ SolarisUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
+ // TDB: copy ACL from source to target
+ }
+
+ @Override
+ boolean supportsSecureDirectoryStreams() {
+ return true;
+ }
+
+ /**
+ * Returns object to iterate over entries in /etc/mnttab
+ */
+ @Override
+ Iterable<UnixMountEntry> getMountEntries() {
+ ArrayList<UnixMountEntry> entries = new ArrayList<UnixMountEntry>();
+ try {
+ UnixPath mnttab = new UnixPath(this, "/etc/mnttab");
+ long fp = fopen(mnttab, "r");
+ try {
+ for (;;) {
+ UnixMountEntry entry = new UnixMountEntry();
+ int res = getextmntent(fp, entry);
+ if (res < 0)
+ break;
+ entries.add(entry);
+ }
+ } finally {
+ fclose(fp);
+ }
+ } catch (UnixException x) {
+ // nothing we can do
+ }
+ return entries;
+ }
+
+ @Override
+ FileStore getFileStore(UnixPath path) throws IOException {
+ return new SolarisFileStore(path);
+ }
+
+ @Override
+ FileStore getFileStore(UnixMountEntry entry) throws IOException {
+ return new SolarisFileStore(this, entry);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java
new file mode 100644
index 000000000..9d3c08457
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Solaris implementation of FileSystemProvider
+ */
+
+public class SolarisFileSystemProvider extends UnixFileSystemProvider {
+ public SolarisFileSystemProvider() {
+ super();
+ }
+
+ @Override
+ SolarisFileSystem newFileSystem(String dir) {
+ return new SolarisFileSystem(this, dir);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java b/src/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java
new file mode 100644
index 000000000..36f2326ce
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Solaris specific system calls.
+ */
+
+class SolarisNativeDispatcher extends UnixNativeDispatcher {
+ private SolarisNativeDispatcher() { }
+
+ /**
+ * int facl(int filedes, int cmd, int nentries, void aclbufp)
+ */
+ static native int facl(int fd, int cmd, int nentries, long aclbufp)
+ throws UnixException;
+
+
+ // initialize
+ private static native void init();
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ init();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java b/src/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java
new file mode 100644
index 000000000..040628f60
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.io.IOException;
+import java.util.*;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+import static sun.nio.fs.SolarisConstants.*;
+
+/**
+ * Solaris emulation of NamedAttributeView using extended attributes.
+ */
+
+class SolarisUserDefinedFileAttributeView
+ extends AbstractUserDefinedFileAttributeView
+{
+ private byte[] nameAsBytes(UnixPath file, String name) throws IOException {
+ byte[] bytes = name.getBytes();
+ // "", "." and ".." not allowed
+ if (bytes.length == 0 || bytes[0] == '.') {
+ if (bytes.length <= 1 ||
+ (bytes.length == 2 && bytes[1] == '.'))
+ {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "'" + name + "' is not a valid name");
+ }
+ }
+ return bytes;
+ }
+
+ private final UnixPath file;
+ private final boolean followLinks;
+
+ SolarisUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ @Override
+ public List<String> list() throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ try {
+ // open extended attribute directory
+ int dfd = openat(fd, ".".getBytes(), (O_RDONLY|O_XATTR), 0);
+ long dp;
+ try {
+ dp = fdopendir(dfd);
+ } catch (UnixException x) {
+ close(dfd);
+ throw x;
+ }
+
+ // read list of extended attributes
+ final List<String> list = new ArrayList<String>();
+ try {
+ byte[] name;
+ while ((name = readdir(dp)) != null) {
+ String s = new String(name);
+ if (!s.equals(".") && !s.equals(".."))
+ list.add(s);
+ }
+ } finally {
+ closedir(dp);
+ }
+ return Collections.unmodifiableList(list);
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to get list of extended attributes: " +
+ x.getMessage());
+ }
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public int size(String name) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ try {
+ // open attribute file
+ int afd = openat(fd, nameAsBytes(file,name), (O_RDONLY|O_XATTR), 0);
+ try {
+ // read attribute's attributes
+ UnixFileAttributes attrs = UnixFileAttributes.get(afd);
+ long size = attrs.size();
+ if (size > Integer.MAX_VALUE)
+ throw new ArithmeticException("Extended attribute value too large");
+ return (int)size;
+ } finally {
+ close(afd);
+ }
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to get size of extended attribute '" + name +
+ "': " + x.getMessage());
+ }
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public int read(String name, ByteBuffer dst) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ try {
+ // open attribute file
+ int afd = openat(fd, nameAsBytes(file,name), (O_RDONLY|O_XATTR), 0);
+
+ // wrap with channel
+ FileChannel fc = UnixChannelFactory.newFileChannel(afd, true, false);
+
+ // read to EOF (nothing we can do if I/O error occurs)
+ try {
+ if (fc.size() > dst.remaining())
+ throw new IOException("Extended attribute file too large");
+ int total = 0;
+ while (dst.hasRemaining()) {
+ int n = fc.read(dst);
+ if (n < 0)
+ break;
+ total += n;
+ }
+ return total;
+ } finally {
+ fc.close();
+ }
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to read extended attribute '" + name +
+ "': " + x.getMessage());
+ }
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public int write(String name, ByteBuffer src) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), false, true);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ try {
+ // open/create attribute file
+ int afd = openat(fd, nameAsBytes(file,name),
+ (O_CREAT|O_WRONLY|O_TRUNC|O_XATTR),
+ UnixFileModeAttribute.ALL_PERMISSIONS);
+
+ // wrap with channel
+ FileChannel fc = UnixChannelFactory.newFileChannel(afd, false, true);
+
+ // write value (nothing we can do if I/O error occurs)
+ try {
+ int rem = src.remaining();
+ while (src.hasRemaining()) {
+ fc.write(src);
+ }
+ return rem;
+ } finally {
+ fc.close();
+ }
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to write extended attribute '" + name +
+ "': " + x.getMessage());
+ }
+ } finally {
+ close(fd);
+ }
+ }
+
+ @Override
+ public void delete(String name) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), false, true);
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ int dfd = openat(fd, ".".getBytes(), (O_RDONLY|O_XATTR), 0);
+ try {
+ unlinkat(dfd, nameAsBytes(file,name), 0);
+ } finally {
+ close(dfd);
+ }
+ } catch (UnixException x) {
+ throw new FileSystemException(file.getPathForExecptionMessage(),
+ null, "Unable to delete extended attribute '" + name +
+ "': " + x.getMessage());
+ } finally {
+ close(fd);
+ }
+ }
+
+ /**
+ * Used by copyTo/moveTo to copy extended attributes from source to target.
+ *
+ * @param ofd
+ * file descriptor for source file
+ * @param nfd
+ * file descriptor for target file
+ */
+ static void copyExtendedAttributes(int ofd, int nfd) {
+ try {
+ // open extended attribute directory
+ int dfd = openat(ofd, ".".getBytes(), (O_RDONLY|O_XATTR), 0);
+ long dp = 0L;
+ try {
+ dp = fdopendir(dfd);
+ } catch (UnixException x) {
+ close(dfd);
+ throw x;
+ }
+
+ // copy each extended attribute
+ try {
+ byte[] name;
+ while ((name = readdir(dp)) != null) {
+ // ignore "." and ".."
+ if (name[0] == '.') {
+ if (name.length == 1)
+ continue;
+ if (name.length == 2 && name[1] == '.')
+ continue;
+ }
+ copyExtendedAttribute(ofd, name, nfd);
+ }
+ } finally {
+ closedir(dp);
+ }
+ } catch (UnixException ignore) {
+ }
+ }
+
+ private static void copyExtendedAttribute(int ofd, byte[] name, int nfd)
+ throws UnixException
+ {
+ // open source attribute file
+ int src = openat(ofd, name, (O_RDONLY|O_XATTR), 0);
+ try {
+ // create target attribute file
+ int dst = openat(nfd, name, (O_CREAT|O_WRONLY|O_TRUNC|O_XATTR),
+ UnixFileModeAttribute.ALL_PERMISSIONS);
+ try {
+ UnixCopyFile.transfer(dst, src, 0L);
+ } finally {
+ close(dst);
+ }
+ } finally {
+ close(src);
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/SolarisWatchService.java b/src/solaris/classes/sun/nio/fs/SolarisWatchService.java
new file mode 100644
index 000000000..6a11ebdd7
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/SolarisWatchService.java
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.UnixConstants.*;
+
+/**
+ * Solaris implementation of WatchService based on file events notification
+ * facility.
+ */
+
+class SolarisWatchService
+ extends AbstractWatchService
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static int addressSize = unsafe.addressSize();
+
+ private static int dependsArch(int value32, int value64) {
+ return (addressSize == 4) ? value32 : value64;
+ }
+
+ /*
+ * typedef struct port_event {
+ * int portev_events;
+ * ushort_t portev_source;
+ * ushort_t portev_pad;
+ * uintptr_t portev_object;
+ * void *portev_user;
+ * } port_event_t;
+ */
+ private static final int SIZEOF_PORT_EVENT = dependsArch(16, 24);
+ private static final int OFFSETOF_EVENTS = 0;
+ private static final int OFFSETOF_SOURCE = 4;
+ private static final int OFFSETOF_OBJECT = 8;
+
+ /*
+ * typedef struct file_obj {
+ * timestruc_t fo_atime;
+ * timestruc_t fo_mtime;
+ * timestruc_t fo_ctime;
+ * uintptr_t fo_pad[3];
+ * char *fo_name;
+ * } file_obj_t;
+ */
+ private static final int SIZEOF_FILEOBJ = dependsArch(40, 80);
+ private static final int OFFSET_FO_NAME = dependsArch(36, 72);
+
+ // port sources
+ private static final short PORT_SOURCE_USER = 3;
+ private static final short PORT_SOURCE_FILE = 7;
+
+ // user-watchable events
+ private static final int FILE_MODIFIED = 0x00000002;
+ private static final int FILE_ATTRIB = 0x00000004;
+ private static final int FILE_NOFOLLOW = 0x10000000;
+
+ // exception events
+ private static final int FILE_DELETE = 0x00000010;
+ private static final int FILE_RENAME_TO = 0x00000020;
+ private static final int FILE_RENAME_FROM = 0x00000040;
+ private static final int UNMOUNTED = 0x20000000;
+ private static final int MOUNTEDOVER = 0x40000000;
+
+ // background thread to read change events
+ private final Poller poller;
+
+ SolarisWatchService(UnixFileSystem fs) throws IOException {
+ int port = -1;
+ try {
+ port = portCreate();
+ } catch (UnixException x) {
+ throw new IOException(x.errorString());
+ }
+
+ this.poller = new Poller(fs, this, port);
+ this.poller.start();
+ }
+
+ @Override
+ WatchKey register(Path dir,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ // delegate to poller
+ return poller.register(dir, events, modifiers);
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // delegate to poller
+ poller.close();
+ }
+
+ /**
+ * WatchKey implementation
+ */
+ private class SolarisWatchKey extends AbstractWatchKey
+ implements DirectoryNode
+ {
+ private final UnixPath dir;
+ private final UnixFileKey fileKey;
+
+ // pointer to native file_obj object
+ private final long object;
+
+ // events (may be changed). set to null when watch key is invalid
+ private volatile Set<? extends WatchEvent.Kind<?>> events;
+
+ // map of entries in directory; created lazily; accessed only by
+ // poller thread.
+ private Map<Path,EntryNode> children;
+
+ SolarisWatchKey(SolarisWatchService watcher,
+ UnixPath dir,
+ UnixFileKey fileKey,
+ long object,
+ Set<? extends WatchEvent.Kind<?>> events)
+ {
+ super(watcher);
+ this.dir = dir;
+ this.fileKey = fileKey;
+ this.object = object;
+ this.events = events;
+ }
+
+ UnixPath getFileRef() {
+ return dir;
+ }
+
+ UnixFileKey getFileKey() {
+ return fileKey;
+ }
+
+ @Override
+ public long object() {
+ return object;
+ }
+
+ void invalidate() {
+ events = null;
+ }
+
+ Set<? extends WatchEvent.Kind<?>> events() {
+ return events;
+ }
+
+ void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
+ this.events = events;
+ }
+
+ @Override
+ public boolean isValid() {
+ return events != null;
+ }
+
+ @Override
+ public void cancel() {
+ if (isValid()) {
+ // delegate to poller
+ poller.cancel(this);
+ }
+ }
+
+ @Override
+ public void addChild(Path name, EntryNode node) {
+ if (children == null)
+ children = new HashMap<Path,EntryNode>();
+ children.put(name, node);
+ }
+
+ @Override
+ public void removeChild(Path name) {
+ children.remove(name);
+ }
+
+ @Override
+ public EntryNode getChild(Path name) {
+ if (children != null)
+ return children.get(name);
+ return null;
+ }
+ }
+
+ /**
+ * Background thread to read from port
+ */
+ private class Poller extends AbstractPoller {
+
+ // maximum number of events to read per call to port_getn
+ private static final int MAX_EVENT_COUNT = 128;
+
+ // events that map to ENTRY_DELETE
+ private static final int FILE_REMOVED =
+ (FILE_DELETE|FILE_RENAME_TO|FILE_RENAME_FROM);
+
+ // events that tell us not to re-associate the object
+ private static final int FILE_EXCEPTION =
+ (FILE_REMOVED|UNMOUNTED|MOUNTEDOVER);
+
+ // address of event buffers (used to receive events with port_getn)
+ private final long bufferAddress;
+
+ private final SolarisWatchService watcher;
+
+ // the I/O port
+ private final int port;
+
+ // maps file key (dev/inode) to WatchKey
+ private final Map<UnixFileKey,SolarisWatchKey> fileKey2WatchKey;
+
+ // maps file_obj object to Node
+ private final Map<Long,Node> object2Node;
+
+ /**
+ * Create a new instance
+ */
+ Poller(UnixFileSystem fs, SolarisWatchService watcher, int port) {
+ this.watcher = watcher;
+ this.port = port;
+ this.bufferAddress =
+ unsafe.allocateMemory(SIZEOF_PORT_EVENT * MAX_EVENT_COUNT);
+ this.fileKey2WatchKey = new HashMap<UnixFileKey,SolarisWatchKey>();
+ this.object2Node = new HashMap<Long,Node>();
+ }
+
+ @Override
+ void wakeup() throws IOException {
+ // write to port to wakeup polling thread
+ try {
+ portSend(port, 0);
+ } catch (UnixException x) {
+ throw new IOException(x.errorString());
+ }
+ }
+
+ @Override
+ Object implRegister(Path obj,
+ Set<? extends WatchEvent.Kind<?>> events,
+ WatchEvent.Modifier... modifiers)
+ {
+ // no modifiers supported at this time
+ if (modifiers.length > 0) {
+ for (WatchEvent.Modifier modifier: modifiers) {
+ if (modifier == null)
+ return new NullPointerException();
+ if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
+ continue; // ignore
+ return new UnsupportedOperationException("Modifier not supported");
+ }
+ }
+
+ UnixPath dir = (UnixPath)obj;
+
+ // check file is directory
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(dir, true);
+ } catch (UnixException x) {
+ return x.asIOException(dir);
+ }
+ if (!attrs.isDirectory()) {
+ return new NotDirectoryException(dir.getPathForExecptionMessage());
+ }
+
+ // return existing watch key after updating events if already
+ // registered
+ UnixFileKey fileKey = attrs.fileKey();
+ SolarisWatchKey watchKey = fileKey2WatchKey.get(fileKey);
+ if (watchKey != null) {
+ updateEvents(watchKey, events);
+ return watchKey;
+ }
+
+ // register directory
+ long object = 0L;
+ try {
+ object = registerImpl(dir, (FILE_MODIFIED | FILE_ATTRIB));
+ } catch (UnixException x) {
+ return x.asIOException(dir);
+ }
+
+ // create watch key and insert it into maps
+ watchKey = new SolarisWatchKey(watcher, dir, fileKey, object, events);
+ object2Node.put(object, watchKey);
+ fileKey2WatchKey.put(fileKey, watchKey);
+
+ // register all entries in directory
+ registerChildren(dir, watchKey, false);
+
+ return watchKey;
+ }
+
+ // cancel single key
+ @Override
+ void implCancelKey(WatchKey obj) {
+ SolarisWatchKey key = (SolarisWatchKey)obj;
+ if (key.isValid()) {
+ fileKey2WatchKey.remove(key.getFileKey());
+
+ // release resources for entries in directory
+ if (key.children != null) {
+ for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) {
+ EntryNode node = entry.getValue();
+ long object = node.object();
+ object2Node.remove(object);
+ releaseObject(object, true);
+ }
+ }
+
+ // release resources for directory
+ long object = key.object();
+ object2Node.remove(object);
+ releaseObject(object, true);
+
+ // and finally invalidate the key
+ key.invalidate();
+ }
+ }
+
+ // close watch service
+ @Override
+ void implCloseAll() {
+ // release all native resources
+ for (Long object: object2Node.keySet()) {
+ releaseObject(object, true);
+ }
+
+ // invalidate all keys
+ for (Map.Entry<UnixFileKey,SolarisWatchKey> entry: fileKey2WatchKey.entrySet()) {
+ entry.getValue().invalidate();
+ }
+
+ // clean-up
+ object2Node.clear();
+ fileKey2WatchKey.clear();
+
+ // free global resources
+ unsafe.freeMemory(bufferAddress);
+ UnixNativeDispatcher.close(port);
+ }
+
+ /**
+ * Poller main loop. Blocks on port_getn waiting for events and then
+ * processes them.
+ */
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ int n = portGetn(port, bufferAddress, MAX_EVENT_COUNT);
+ assert n > 0;
+
+ long address = bufferAddress;
+ for (int i=0; i<n; i++) {
+ boolean shutdown = processEvent(address);
+ if (shutdown)
+ return;
+ address += SIZEOF_PORT_EVENT;
+ }
+ }
+ } catch (UnixException x) {
+ x.printStackTrace();
+ }
+ }
+
+ /**
+ * Process a single port_event
+ *
+ * Returns true if poller thread is requested to shutdown.
+ */
+ boolean processEvent(long address) {
+ // pe->portev_source
+ short source = unsafe.getShort(address + OFFSETOF_SOURCE);
+ // pe->portev_object
+ long object = unsafe.getAddress(address + OFFSETOF_OBJECT);
+ // pe->portev_events
+ int events = unsafe.getInt(address + OFFSETOF_EVENTS);
+
+ // user event is trigger to process pending requests
+ if (source != PORT_SOURCE_FILE) {
+ if (source == PORT_SOURCE_USER) {
+ // process any pending requests
+ boolean shutdown = processRequests();
+ if (shutdown)
+ return true;
+ }
+ return false;
+ }
+
+ // lookup object to get Node
+ Node node = object2Node.get(object);
+ if (node == null) {
+ // should not happen
+ return false;
+ }
+
+ // As a workaround for 6642290 and 6636438/6636412 we don't use
+ // FILE_EXCEPTION events to tell use not to register the file.
+ // boolean reregister = (events & FILE_EXCEPTION) == 0;
+ boolean reregister = true;
+
+ // If node is EntryNode then event relates to entry in directory
+ // If node is a SolarisWatchKey (DirectoryNode) then event relates
+ // to a watched directory.
+ boolean isDirectory = (node instanceof SolarisWatchKey);
+ if (isDirectory) {
+ processDirectoryEvents((SolarisWatchKey)node, events);
+ } else {
+ boolean ignore = processEntryEvents((EntryNode)node, events);
+ if (ignore)
+ reregister = false;
+ }
+
+ // need to re-associate to get further events
+ if (reregister) {
+ try {
+ events = FILE_MODIFIED | FILE_ATTRIB;
+ if (!isDirectory) events |= FILE_NOFOLLOW;
+ portAssociate(port,
+ PORT_SOURCE_FILE,
+ object,
+ events);
+ } catch (UnixException x) {
+ // unable to re-register
+ reregister = false;
+ }
+ }
+
+ // object is not re-registered so release resources. If
+ // object is a watched directory then signal key
+ if (!reregister) {
+ // release resources
+ object2Node.remove(object);
+ releaseObject(object, false);
+
+ // if watch key then signal it
+ if (isDirectory) {
+ SolarisWatchKey key = (SolarisWatchKey)node;
+ fileKey2WatchKey.remove( key.getFileKey() );
+ key.invalidate();
+ key.signal();
+ } else {
+ // if entry then remove it from parent
+ EntryNode entry = (EntryNode)node;
+ SolarisWatchKey key = (SolarisWatchKey)entry.parent();
+ key.removeChild(entry.name());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Process directory events. If directory is modified then re-scan
+ * directory to register any new entries
+ */
+ void processDirectoryEvents(SolarisWatchKey key, int mask) {
+ if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) {
+ registerChildren(key.getFileRef(), key,
+ key.events().contains(StandardWatchEventKind.ENTRY_CREATE));
+ }
+ }
+
+ /**
+ * Process events for entries in registered directories. Returns {@code
+ * true} if events are ignored because the watch key has been cancelled.
+ */
+ boolean processEntryEvents(EntryNode node, int mask) {
+ SolarisWatchKey key = (SolarisWatchKey)node.parent();
+ Set<? extends WatchEvent.Kind<?>> events = key.events();
+ if (events == null) {
+ // key has been cancelled so ignore event
+ return true;
+ }
+
+ // entry modified
+ if (((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) &&
+ events.contains(StandardWatchEventKind.ENTRY_MODIFY))
+ {
+ key.signalEvent(StandardWatchEventKind.ENTRY_MODIFY, node.name());
+ }
+
+ // entry removed
+ if (((mask & (FILE_REMOVED)) != 0) &&
+ events.contains(StandardWatchEventKind.ENTRY_DELETE))
+ {
+ // Due to 6636438/6636412 we may get a remove event for cases
+ // where a rmdir/unlink/rename is attempted but fails. Until
+ // this issue is resolved we re-lstat the file to check if it
+ // exists. If it exists then we ignore the event. To keep the
+ // workaround simple we don't check the st_ino so it isn't
+ // effective when the file is replaced.
+ boolean removed = true;
+ try {
+ UnixFileAttributes
+ .get(key.getFileRef().resolve(node.name()), false);
+ removed = false;
+ } catch (UnixException x) { }
+
+ if (removed)
+ key.signalEvent(StandardWatchEventKind.ENTRY_DELETE, node.name());
+ }
+ return false;
+ }
+
+ /**
+ * Registers all entries in the given directory
+ *
+ * The {@code sendEvents} parameter indicates if ENTRY_CREATE events
+ * should be queued when new entries are found. When initially
+ * registering a directory then will always be false. When re-scanning
+ * a directory then it depends on if the event is enabled or not.
+ */
+ void registerChildren(UnixPath dir,
+ SolarisWatchKey parent,
+ boolean sendEvents)
+ {
+ // if the ENTRY_MODIFY event is not enabled then we don't need
+ // modification events for entries in the directory
+ int events = FILE_NOFOLLOW;
+ if (parent.events().contains(StandardWatchEventKind.ENTRY_MODIFY))
+ events |= (FILE_MODIFIED | FILE_ATTRIB);
+
+ DirectoryStream<Path> stream = null;
+ try {
+ stream = dir.newDirectoryStream();
+ } catch (IOException x) {
+ // nothing we can do
+ return;
+ }
+ try {
+ for (Path entry: stream) {
+ Path name = entry.getName();
+
+ // skip entry if already registered
+ if (parent.getChild(name) != null)
+ continue;
+
+ // send ENTRY_CREATE if enabled
+ if (sendEvents) {
+ parent.signalEvent(StandardWatchEventKind.ENTRY_CREATE, name);
+ }
+
+ // register it
+ long object = 0L;
+ try {
+ object = registerImpl((UnixPath)entry, events);
+ } catch (UnixException x) {
+ // can't register so ignore for now.
+ continue;
+ }
+
+ // create node
+ EntryNode node = new EntryNode(object, entry.getName(), parent);
+ // tell the parent about it
+ parent.addChild(entry.getName(), node);
+ object2Node.put(object, node);
+ }
+ } catch (ConcurrentModificationException x) {
+ // error during iteration which we ignore for now
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException x) { }
+ }
+ }
+
+ /**
+ * Update watch key's events. Where the ENTRY_MODIFY changes then we
+ * need to update the events of registered children.
+ */
+ void updateEvents(SolarisWatchKey key, Set<? extends WatchEvent.Kind<?>> events) {
+ // update events, rembering if ENTRY_MODIFY was previously
+ // enabled or disabled.
+ boolean wasModifyEnabled = key.events()
+ .contains(StandardWatchEventKind.ENTRY_MODIFY);
+ key.setEvents(events);
+
+ // check if ENTRY_MODIFY has changed
+ boolean isModifyEnabled = events
+ .contains(StandardWatchEventKind.ENTRY_MODIFY);
+ if (wasModifyEnabled == isModifyEnabled) {
+ return;
+ }
+
+ // if changed then update events of children
+ if (key.children != null) {
+ int ev = FILE_NOFOLLOW;
+ if (isModifyEnabled)
+ ev |= (FILE_MODIFIED | FILE_ATTRIB);
+
+ for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) {
+ long object = entry.getValue().object();
+ try {
+ portAssociate(port,
+ PORT_SOURCE_FILE,
+ object,
+ ev);
+ } catch (UnixException x) {
+ // nothing we can do.
+ }
+ }
+ }
+ }
+
+ /**
+ * Calls port_associate to register the given path.
+ * Returns pointer to fileobj structure that is allocated for
+ * the registration.
+ */
+ long registerImpl(UnixPath dir, int events)
+ throws UnixException
+ {
+ // allocate memory for the path (file_obj->fo_name field)
+ byte[] path = dir.getByteArrayForSysCalls();
+ int len = path.length;
+ long name = unsafe.allocateMemory(len+1);
+ unsafe.copyMemory(path, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
+ name, (long)len);
+ unsafe.putByte(name + len, (byte)0);
+
+ // allocate memory for filedatanode structure - this is the object
+ // to port_associate
+ long object = unsafe.allocateMemory(SIZEOF_FILEOBJ);
+ unsafe.setMemory(null, object, SIZEOF_FILEOBJ, (byte)0);
+ unsafe.putAddress(object + OFFSET_FO_NAME, name);
+
+ // associate the object with the port
+ try {
+ portAssociate(port,
+ PORT_SOURCE_FILE,
+ object,
+ events);
+ } catch (UnixException x) {
+ // debugging
+ if (x.errno() == EAGAIN) {
+ System.err.println("The maximum number of objects associated "+
+ "with the port has been reached");
+ }
+
+ unsafe.freeMemory(name);
+ unsafe.freeMemory(object);
+ throw x;
+ }
+ return object;
+ }
+
+ /**
+ * Frees all resources for an file_obj object; optionally remove
+ * association from port
+ */
+ void releaseObject(long object, boolean dissociate) {
+ // remove association
+ if (dissociate) {
+ try {
+ portDissociate(port, PORT_SOURCE_FILE, object);
+ } catch (UnixException x) {
+ // ignore
+ }
+ }
+
+ // free native memory
+ long name = unsafe.getAddress(object + OFFSET_FO_NAME);
+ unsafe.freeMemory(name);
+ unsafe.freeMemory(object);
+ }
+ }
+
+ /**
+ * A node with native (file_obj) resources
+ */
+ private static interface Node {
+ long object();
+ }
+
+ /**
+ * A directory node with a map of the entries in the directory
+ */
+ private static interface DirectoryNode extends Node {
+ void addChild(Path name, EntryNode node);
+ void removeChild(Path name);
+ EntryNode getChild(Path name);
+ }
+
+ /**
+ * An implementation of a node that is an entry in a directory.
+ */
+ private static class EntryNode implements Node {
+ private final long object;
+ private final Path name;
+ private final DirectoryNode parent;
+
+ EntryNode(long object, Path name, DirectoryNode parent) {
+ this.object = object;
+ this.name = name;
+ this.parent = parent;
+ }
+
+ @Override
+ public long object() {
+ return object;
+ }
+
+ Path name() {
+ return name;
+ }
+
+ DirectoryNode parent() {
+ return parent;
+ }
+ }
+
+ // -- native methods --
+
+ private static native void init();
+
+ private static native int portCreate() throws UnixException;
+
+ private static native void portAssociate(int port, int source, long object, int events)
+ throws UnixException;
+
+ private static native void portDissociate(int port, int source, long object)
+ throws UnixException;
+
+ private static native void portSend(int port, int events)
+ throws UnixException;
+
+ private static native int portGetn(int port, long address, int max)
+ throws UnixException;
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ init();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java b/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java
new file mode 100644
index 000000000..a8265dca7
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.channels.*;
+import java.io.FileDescriptor;
+import java.util.Set;
+
+import sun.nio.ch.FileChannelImpl;
+import sun.nio.ch.ThreadPool;
+import sun.nio.ch.SimpleAsynchronousFileChannelImpl;
+import sun.misc.SharedSecrets;
+import sun.misc.JavaIOFileDescriptorAccess;
+
+import com.sun.nio.file.ExtendedOpenOption;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+/**
+ * Factory for FileChannels and AsynchronousFileChannels
+ */
+
+class UnixChannelFactory {
+ private static final JavaIOFileDescriptorAccess fdAccess =
+ SharedSecrets.getJavaIOFileDescriptorAccess();
+
+ private UnixChannelFactory() {
+ }
+
+ /**
+ * Represents the flags from a user-supplied set of open options.
+ */
+ private static class Flags {
+ boolean read;
+ boolean write;
+ boolean append;
+ boolean truncateExisting;
+ boolean noFollowLinks;
+ boolean create;
+ boolean createNew;
+ boolean deleteOnClose;
+ boolean sync;
+ boolean dsync;
+
+ static Flags toFlags(Set<? extends OpenOption> options) {
+ Flags flags = new Flags();
+ for (OpenOption option: options) {
+ if (option instanceof StandardOpenOption) {
+ switch ((StandardOpenOption)option) {
+ case READ : flags.read = true; break;
+ case WRITE : flags.write = true; break;
+ case APPEND : flags.append = true; break;
+ case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
+ case CREATE : flags.create = true; break;
+ case CREATE_NEW : flags.createNew = true; break;
+ case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
+ case SPARSE : /* ignore */ break;
+ case SYNC : flags.sync = true; break;
+ case DSYNC : flags.dsync = true; break;
+ default: throw new UnsupportedOperationException();
+ }
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ flags.noFollowLinks = true;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException();
+ }
+ return flags;
+ }
+ }
+
+
+ /**
+ * Constructs a file channel from an existing (open) file descriptor
+ */
+ static FileChannel newFileChannel(int fd, boolean reading, boolean writing) {
+ FileDescriptor fdObj = new FileDescriptor();
+ fdAccess.set(fdObj, fd);
+ return FileChannelImpl.open(fdObj, reading, writing, null);
+ }
+
+ /**
+ * Constructs a file channel by opening a file using a dfd/path pair
+ */
+ static FileChannel newFileChannel(int dfd,
+ UnixPath path,
+ String pathForPermissionCheck,
+ Set<? extends OpenOption> options,
+ int mode)
+ throws UnixException
+ {
+ Flags flags = Flags.toFlags(options);
+
+ // default is reading; append => writing
+ if (!flags.read && !flags.write) {
+ if (flags.append) {
+ flags.write = true;
+ } else {
+ flags.read = true;
+ }
+ }
+
+ // validation
+ if (flags.read && flags.append)
+ throw new IllegalArgumentException("READ + APPEND not allowed");
+ if (flags.append && flags.truncateExisting)
+ throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
+
+ FileDescriptor fdObj = open(dfd, path, pathForPermissionCheck, flags, mode);
+ return FileChannelImpl.open(fdObj, flags.read, flags.write, null);
+ }
+
+ /**
+ * Constructs a file channel by opening the given file.
+ */
+ static FileChannel newFileChannel(UnixPath path,
+ Set<? extends OpenOption> options,
+ int mode)
+ throws UnixException
+ {
+ return newFileChannel(-1, path, null, options, mode);
+ }
+
+ /**
+ * Constructs an asynchronous file channel by opening the given file.
+ */
+ static AsynchronousFileChannel newAsynchronousFileChannel(UnixPath path,
+ Set<? extends OpenOption> options,
+ int mode,
+ ThreadPool pool)
+ throws UnixException
+ {
+ Flags flags = Flags.toFlags(options);
+
+ // default is reading
+ if (!flags.read && !flags.write) {
+ flags.read = true;
+ }
+
+ // validation
+ if (flags.append)
+ throw new UnsupportedOperationException("APPEND not allowed");
+
+ // for now use simple implementation
+ FileDescriptor fdObj = open(-1, path, null, flags, mode);
+ return SimpleAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);
+ }
+
+ /**
+ * Opens file based on parameters and options, returning a FileDescriptor
+ * encapsulating the handle to the open file.
+ */
+ static FileDescriptor open(int dfd,
+ UnixPath path,
+ String pathForPermissionCheck,
+ Flags flags,
+ int mode)
+ throws UnixException
+ {
+ // map to oflags
+ int oflags;
+ if (flags.read && flags.write) {
+ oflags = O_RDWR;
+ } else {
+ oflags = (flags.write) ? O_WRONLY : O_RDONLY;
+ }
+ if (flags.write) {
+ if (flags.truncateExisting)
+ oflags |= O_TRUNC;
+ if (flags.append)
+ oflags |= O_APPEND;
+
+ // create flags
+ if (flags.createNew) {
+ byte[] pathForSysCall = path.asByteArray();
+
+ // throw exception if file name is "." to avoid confusing error
+ if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
+ (pathForSysCall.length == 1 ||
+ (pathForSysCall[pathForSysCall.length-2] == '/')))
+ {
+ throw new UnixException(EEXIST);
+ }
+ oflags |= (O_CREAT | O_EXCL);
+ } else {
+ if (flags.create)
+ oflags |= O_CREAT;
+ }
+ }
+
+ // follow links by default
+ boolean followLinks = true;
+ if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) {
+ followLinks = false;
+ oflags |= O_NOFOLLOW;
+ }
+
+ if (flags.dsync)
+ oflags |= O_DSYNC;
+ if (flags.sync)
+ oflags |= O_SYNC;
+
+ // permission check before we open the file
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (pathForPermissionCheck == null)
+ pathForPermissionCheck = path.getPathForPermissionCheck();
+ if (flags.read)
+ sm.checkRead(pathForPermissionCheck);
+ if (flags.write)
+ sm.checkWrite(pathForPermissionCheck);
+ if (flags.deleteOnClose)
+ sm.checkDelete(pathForPermissionCheck);
+ }
+
+ int fd;
+ try {
+ if (dfd >= 0) {
+ fd = openat(dfd, path.asByteArray(), oflags, mode);
+ } else {
+ fd = UnixNativeDispatcher.open(path, oflags, mode);
+ }
+ } catch (UnixException x) {
+ // Linux error can be EISDIR or EEXIST when file exists
+ if (flags.createNew && (x.errno() == EISDIR)) {
+ x.setError(EEXIST);
+ }
+
+ // handle ELOOP to avoid confusing message
+ if (!followLinks && (x.errno() == ELOOP)) {
+ x = new UnixException(x.getMessage() + " (NOFOLLOW_LINKS specified)");
+ }
+
+ throw x;
+ }
+
+ // unlink file immediately if delete on close. The spec is clear that
+ // an implementation cannot guarantee to unlink the correct file when
+ // replaced by an attacker after it is opened.
+ if (flags.deleteOnClose) {
+ try {
+ if (dfd >= 0) {
+ unlinkat(dfd, path.asByteArray(), 0);
+ } else {
+ unlink(path);
+ }
+ } catch (UnixException ignore) {
+ // best-effort
+ }
+ }
+
+ // create java.io.FileDescriptor
+ FileDescriptor fdObj = new FileDescriptor();
+ fdAccess.set(fdObj, fd);
+ return fdObj;
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixCopyFile.java b/src/solaris/classes/sun/nio/fs/UnixCopyFile.java
new file mode 100644
index 000000000..808cc1e34
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixCopyFile.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.concurrent.ExecutionException;
+import com.sun.nio.file.ExtendedCopyOption;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+
+/**
+ * Unix implementation of Path#copyTo and Path#moveTo methods.
+ */
+
+class UnixCopyFile {
+ private UnixCopyFile() { }
+
+ // The flags that control how a file is copied or moved
+ private static class Flags {
+ boolean replaceExisting;
+ boolean atomicMove;
+ boolean followLinks;
+ boolean interruptible;
+
+ // the attributes to copy
+ boolean copyBasicAttributes;
+ boolean copyPosixAttributes;
+ boolean copyNonPosixAttributes;
+
+ // flags that indicate if we should fail if attributes cannot be copied
+ boolean failIfUnableToCopyBasic;
+ boolean failIfUnableToCopyPosix;
+ boolean failIfUnableToCopyNonPosix;
+
+ static Flags fromCopyOptions(CopyOption... options) {
+ Flags flags = new Flags();
+ flags.followLinks = true;
+ for (CopyOption option: options) {
+ if (option == StandardCopyOption.REPLACE_EXISTING) {
+ flags.replaceExisting = true;
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ flags.followLinks = false;
+ continue;
+ }
+ if (option == StandardCopyOption.COPY_ATTRIBUTES) {
+ // copy all attributes but only fail if basic attributes
+ // cannot be copied
+ flags.copyBasicAttributes = true;
+ flags.copyPosixAttributes = true;
+ flags.copyNonPosixAttributes = true;
+ flags.failIfUnableToCopyBasic = true;
+ continue;
+ }
+ if (option == ExtendedCopyOption.INTERRUPTIBLE) {
+ flags.interruptible = true;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException("Unsupported copy option");
+ }
+ return flags;
+ }
+
+ static Flags fromMoveOptions(CopyOption... options) {
+ Flags flags = new Flags();
+ for (CopyOption option: options) {
+ if (option == StandardCopyOption.ATOMIC_MOVE) {
+ flags.atomicMove = true;
+ continue;
+ }
+ if (option == StandardCopyOption.REPLACE_EXISTING) {
+ flags.replaceExisting = true;
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ // ignore
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException("Unsupported copy option");
+ }
+
+ // a move requires that all attributes be copied but only fail if
+ // the basic attributes cannot be copied
+ flags.copyBasicAttributes = true;
+ flags.copyPosixAttributes = true;
+ flags.copyNonPosixAttributes = true;
+ flags.failIfUnableToCopyBasic = true;
+ return flags;
+ }
+ }
+
+ // copy directory from source to target
+ private static void copyDirectory(UnixPath source,
+ UnixFileAttributes attrs,
+ UnixPath target,
+ Flags flags)
+ throws IOException
+ {
+ try {
+ mkdir(target, attrs.mode());
+ } catch (UnixException x) {
+ x.rethrowAsIOException(target);
+ }
+
+ // no attributes to copy
+ if (!flags.copyBasicAttributes &&
+ !flags.copyPosixAttributes &&
+ !flags.copyNonPosixAttributes) return;
+
+ // open target directory if possible (this can fail when copying a
+ // directory for which we don't have read access).
+ int dfd = -1;
+ try {
+ dfd = open(target, O_RDONLY, 0);
+ } catch (UnixException x) {
+ // access to target directory required to copy named attributes
+ if (flags.copyNonPosixAttributes && flags.failIfUnableToCopyNonPosix) {
+ try { rmdir(target); } catch (UnixException ignore) { }
+ x.rethrowAsIOException(target);
+ }
+ }
+
+ boolean done = false;
+ try {
+ // copy owner/group/permissions
+ if (flags.copyPosixAttributes){
+ try {
+ if (dfd >= 0) {
+ fchown(dfd, attrs.uid(), attrs.gid());
+ fchmod(dfd, attrs.mode());
+ } else {
+ chown(target, attrs.uid(), attrs.gid());
+ chmod(target, attrs.mode());
+ }
+ } catch (UnixException x) {
+ // unable to set owner/group
+ if (flags.failIfUnableToCopyPosix)
+ x.rethrowAsIOException(target);
+ }
+ }
+ // copy other attributes
+ if (flags.copyNonPosixAttributes && (dfd >= 0)) {
+ int sfd = -1;
+ try {
+ sfd = open(source, O_RDONLY, 0);
+ } catch (UnixException x) {
+ if (flags.failIfUnableToCopyNonPosix)
+ x.rethrowAsIOException(source);
+ }
+ if (sfd >= 0) {
+ source.getFileSystem().copyNonPosixAttributes(sfd, dfd);
+ close(sfd);
+ }
+ }
+ // copy time stamps last
+ if (flags.copyBasicAttributes) {
+ try {
+ if (dfd >= 0) {
+ futimes(dfd, attrs.lastAccessTime(),
+ attrs.lastModifiedTime());
+ } else {
+ utimes(target, attrs.lastAccessTime(),
+ attrs.lastModifiedTime());
+ }
+ } catch (UnixException x) {
+ // unable to set times
+ if (flags.failIfUnableToCopyBasic)
+ x.rethrowAsIOException(target);
+ }
+ }
+ done = true;
+ } finally {
+ if (dfd >= 0)
+ close(dfd);
+ if (!done) {
+ // rollback
+ try { rmdir(target); } catch (UnixException ignore) { }
+ }
+ }
+ }
+
+ // copy regular file from source to target
+ private static void copyFile(UnixPath source,
+ UnixFileAttributes attrs,
+ UnixPath target,
+ Flags flags,
+ long addressToPollForCancel)
+ throws IOException
+ {
+ int fi = -1;
+ try {
+ fi = open(source, O_RDONLY, 0);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(source);
+ }
+
+ try {
+ // open new file
+ int fo = -1;
+ try {
+ fo = open(target,
+ (O_WRONLY |
+ O_CREAT |
+ O_TRUNC),
+ attrs.mode());
+ } catch (UnixException x) {
+ x.rethrowAsIOException(target);
+ }
+
+ // set to true when file and attributes copied
+ boolean complete = false;
+ try {
+ // transfer bytes to target file
+ try {
+ transfer(fo, fi, addressToPollForCancel);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(source, target);
+ }
+ // copy owner/permissions
+ if (flags.copyPosixAttributes) {
+ try {
+ fchown(fo, attrs.uid(), attrs.gid());
+ fchmod(fo, attrs.mode());
+ } catch (UnixException x) {
+ if (flags.failIfUnableToCopyPosix)
+ x.rethrowAsIOException(target);
+ }
+ }
+ // copy non POSIX attributes (depends on file system)
+ if (flags.copyNonPosixAttributes) {
+ source.getFileSystem().copyNonPosixAttributes(fi, fo);
+ }
+ // copy time attributes
+ if (flags.copyBasicAttributes) {
+ try {
+ futimes(fo, attrs.lastAccessTime(), attrs.lastModifiedTime());
+ } catch (UnixException x) {
+ if (flags.failIfUnableToCopyBasic)
+ x.rethrowAsIOException(target);
+ }
+ }
+ complete = true;
+ } finally {
+ close(fo);
+
+ // copy of file or attributes failed so rollback
+ if (!complete) {
+ try {
+ unlink(target);
+ } catch (UnixException ignore) { }
+ }
+ }
+ } finally {
+ close(fi);
+ }
+ }
+
+ // copy symbolic link from source to target
+ private static void copyLink(UnixPath source,
+ UnixFileAttributes attrs,
+ UnixPath target,
+ Flags flags)
+ throws IOException
+ {
+ byte[] linktarget = null;
+ try {
+ linktarget = readlink(source);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(source);
+ }
+ try {
+ symlink(linktarget, target);
+
+ if (flags.copyPosixAttributes) {
+ try {
+ lchown(target, attrs.uid(), attrs.gid());
+ } catch (UnixException x) {
+ // ignore since link attributes not required to be copied
+ }
+ }
+ } catch (UnixException x) {
+ x.rethrowAsIOException(target);
+ }
+ }
+
+ // copy special file from source to target
+ private static void copySpecial(UnixPath source,
+ UnixFileAttributes attrs,
+ UnixPath target,
+ Flags flags)
+ throws IOException
+ {
+ try {
+ mknod(target, attrs.mode(), attrs.rdev());
+ } catch (UnixException x) {
+ x.rethrowAsIOException(target);
+ }
+ boolean done = false;
+ try {
+ if (flags.copyPosixAttributes) {
+ try {
+ chown(target, attrs.uid(), attrs.gid());
+ chmod(target, attrs.mode());
+ } catch (UnixException x) {
+ if (flags.failIfUnableToCopyPosix)
+ x.rethrowAsIOException(target);
+ }
+ }
+ if (flags.copyBasicAttributes) {
+ try {
+ utimes(target, attrs.lastAccessTime(), attrs.lastModifiedTime());
+ } catch (UnixException x) {
+ if (flags.failIfUnableToCopyBasic)
+ x.rethrowAsIOException(target);
+ }
+ }
+ done = true;
+ } finally {
+ if (!done) {
+ try { unlink(target); } catch (UnixException ignore) { }
+ }
+ }
+ }
+
+ // move file from source to target
+ static void move(UnixPath source, UnixPath target, CopyOption... options)
+ throws IOException
+ {
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ source.checkWrite();
+ target.checkWrite();
+ }
+
+ // translate options into flags
+ Flags flags = Flags.fromMoveOptions(options);
+
+ // handle atomic rename case
+ if (flags.atomicMove) {
+ try {
+ rename(source, target);
+ } catch (UnixException x) {
+ if (x.errno() == EXDEV) {
+ throw new AtomicMoveNotSupportedException(
+ source.getPathForExecptionMessage(),
+ target.getPathForExecptionMessage(),
+ x.errorString());
+ }
+ x.rethrowAsIOException(source, target);
+ }
+ return;
+ }
+
+ // move using rename or copy+delete
+ UnixFileAttributes sourceAttrs = null;
+ UnixFileAttributes targetAttrs = null;
+
+ // get attributes of source file (don't follow links)
+ try {
+ sourceAttrs = UnixFileAttributes.get(source, false);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(source);
+ }
+
+ // get attributes of target file (don't follow links)
+ try {
+ targetAttrs = UnixFileAttributes.get(target, false);
+ } catch (UnixException x) {
+ // ignore
+ }
+ boolean targetExists = (targetAttrs != null);
+
+ // if the target exists:
+ // 1. check if source and target are the same file
+ // 2. throw exception if REPLACE_EXISTING option is not set
+ // 3. delete target if REPLACE_EXISTING option set
+ if (targetExists) {
+ if (sourceAttrs.isSameFile(targetAttrs))
+ return; // nothing to do as files are identical
+ if (!flags.replaceExisting) {
+ throw new FileAlreadyExistsException(
+ target.getPathForExecptionMessage());
+ }
+
+ // attempt to delete target
+ try {
+ if (targetAttrs.isDirectory()) {
+ rmdir(target);
+ } else {
+ unlink(target);
+ }
+ } catch (UnixException x) {
+ // target is non-empty directory that can't be replaced.
+ if (targetAttrs.isDirectory() &&
+ (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+ {
+ throw new FileAlreadyExistsException(
+ source.getPathForExecptionMessage(),
+ target.getPathForExecptionMessage(),
+ x.getMessage());
+ }
+ x.rethrowAsIOException(target);
+ }
+ }
+
+ // first try rename
+ try {
+ rename(source, target);
+ return;
+ } catch (UnixException x) {
+ if (x.errno() != EXDEV && x.errno() != EISDIR) {
+ x.rethrowAsIOException(source, target);
+ }
+ }
+
+ // copy source to target
+ if (sourceAttrs.isDirectory()) {
+ copyDirectory(source, sourceAttrs, target, flags);
+ } else {
+ if (sourceAttrs.isSymbolicLink()) {
+ copyLink(source, sourceAttrs, target, flags);
+ } else {
+ if (sourceAttrs.isDevice()) {
+ copySpecial(source, sourceAttrs, target, flags);
+ } else {
+ copyFile(source, sourceAttrs, target, flags, 0L);
+ }
+ }
+ }
+
+ // delete source
+ try {
+ if (sourceAttrs.isDirectory()) {
+ rmdir(source);
+ } else {
+ unlink(source);
+ }
+ } catch (UnixException x) {
+ // file was copied but unable to unlink the source file so attempt
+ // to remove the target and throw a reasonable exception
+ try {
+ if (sourceAttrs.isDirectory()) {
+ rmdir(target);
+ } else {
+ unlink(target);
+ }
+ } catch (UnixException ignore) { }
+
+ if (sourceAttrs.isDirectory() &&
+ (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+ {
+ throw new DirectoryNotEmptyException(
+ source.getPathForExecptionMessage());
+ }
+ x.rethrowAsIOException(source);
+ }
+ }
+
+ // copy file from source to target
+ static void copy(final UnixPath source,
+ final UnixPath target,
+ CopyOption... options) throws IOException
+ {
+ // permission checks
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ source.checkRead();
+ target.checkWrite();
+ }
+
+ // translate options into flags
+ final Flags flags = Flags.fromCopyOptions(options);
+
+ UnixFileAttributes sourceAttrs = null;
+ UnixFileAttributes targetAttrs = null;
+
+ // get attributes of source file
+ try {
+ sourceAttrs = UnixFileAttributes.get(source, flags.followLinks);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(source);
+ }
+
+ // if source file is symbolic link then we must check LinkPermission
+ if (sm != null && sourceAttrs.isSymbolicLink()) {
+ sm.checkPermission(new LinkPermission("symbolic"));
+ }
+
+ // get attributes of target file (don't follow links)
+ try {
+ targetAttrs = UnixFileAttributes.get(target, false);
+ } catch (UnixException x) {
+ // ignore
+ }
+ boolean targetExists = (targetAttrs != null);
+
+ // if the target exists:
+ // 1. check if source and target are the same file
+ // 2. throw exception if REPLACE_EXISTING option is not set
+ // 3. try to unlink the target
+ if (targetExists) {
+ if (sourceAttrs.isSameFile(targetAttrs))
+ return; // nothing to do as files are identical
+ if (!flags.replaceExisting)
+ throw new FileAlreadyExistsException(
+ target.getPathForExecptionMessage());
+ try {
+ if (targetAttrs.isDirectory()) {
+ rmdir(target);
+ } else {
+ unlink(target);
+ }
+ } catch (UnixException x) {
+ // target is non-empty directory that can't be replaced.
+ if (targetAttrs.isDirectory() &&
+ (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+ {
+ throw new FileAlreadyExistsException(
+ source.getPathForExecptionMessage(),
+ target.getPathForExecptionMessage(),
+ x.getMessage());
+ }
+ x.rethrowAsIOException(target);
+ }
+ }
+
+ // do the copy
+ if (sourceAttrs.isDirectory()) {
+ copyDirectory(source, sourceAttrs, target, flags);
+ return;
+ }
+ if (sourceAttrs.isSymbolicLink()) {
+ copyLink(source, sourceAttrs, target, flags);
+ return;
+ }
+ if (!flags.interruptible) {
+ // non-interruptible file copy
+ copyFile(source, sourceAttrs, target, flags, 0L);
+ return;
+ }
+
+ // interruptible file copy
+ final UnixFileAttributes attrsToCopy = sourceAttrs;
+ Cancellable copyTask = new Cancellable() {
+ @Override public void implRun() throws IOException {
+ copyFile(source, attrsToCopy, target, flags,
+ addressToPollForCancel());
+ }
+ };
+ try {
+ Cancellable.runInterruptibly(copyTask);
+ } catch (ExecutionException e) {
+ Throwable t = e.getCause();
+ if (t instanceof IOException)
+ throw (IOException)t;
+ throw new IOException(t);
+ }
+ }
+
+ // -- native methods --
+
+ static native void transfer(int dst, int src, long addressToPollForCancel)
+ throws UnixException;
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ }
+
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java b/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java
new file mode 100644
index 000000000..a723d71ad
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.util.Iterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import java.util.concurrent.locks.*;
+import java.io.IOException;
+import static sun.nio.fs.UnixNativeDispatcher.*;
+
+/**
+ * Unix implementation of java.nio.file.DirectoryStream
+ */
+
+class UnixDirectoryStream
+ implements DirectoryStream<Path>
+{
+ // path to directory when originally opened
+ private final UnixPath dir;
+
+ // directory pointer (returned by opendir)
+ private final long dp;
+
+ // filter (may be null)
+ private final DirectoryStream.Filter<? super Path> filter;
+
+ // used to coorindate closing of directory stream
+ private final ReentrantReadWriteLock streamLock =
+ new ReentrantReadWriteLock(true);
+
+ // indicates if directory stream is open (synchronize on closeLock)
+ private volatile boolean isClosed;
+
+ // directory iterator
+ private Iterator<Path> iterator;
+
+ /**
+ * Initializes a new instance
+ */
+ UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter<? super Path> filter) {
+ this.dir = dir;
+ this.dp = dp;
+ this.filter = filter;
+ }
+
+ protected final UnixPath directory() {
+ return dir;
+ }
+
+ protected final Lock readLock() {
+ return streamLock.readLock();
+ }
+
+ protected final Lock writeLock() {
+ return streamLock.writeLock();
+ }
+
+ protected final boolean isOpen() {
+ return !isClosed;
+ }
+
+ protected final boolean closeImpl() throws IOException {
+ if (!isClosed) {
+ isClosed = true;
+ try {
+ closedir(dp);
+ } catch (UnixException x) {
+ throw new IOException(x.errorString());
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ writeLock().lock();
+ try {
+ closeImpl();
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ protected final Iterator<Path> iterator(DirectoryStream<Path> ds) {
+ if (isClosed) {
+ throw new IllegalStateException("Directory stream is closed");
+ }
+ synchronized (this) {
+ if (iterator != null)
+ throw new IllegalStateException("Iterator already obtained");
+ iterator = new UnixDirectoryIterator(ds);
+ return iterator;
+ }
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ return iterator(this);
+ }
+
+ /**
+ * Iterator implementation
+ */
+ private class UnixDirectoryIterator implements Iterator<Path> {
+ private final DirectoryStream<Path> stream;
+
+ // true when at EOF
+ private boolean atEof;
+
+ // next entry to return
+ private Path nextEntry;
+
+ // previous entry returned by next method (needed by remove method)
+ private Path prevEntry;
+
+ UnixDirectoryIterator(DirectoryStream<Path> stream) {
+ atEof = false;
+ this.stream = stream;
+ }
+
+ // Return true if file name is "." or ".."
+ private boolean isSelfOrParent(byte[] nameAsBytes) {
+ if (nameAsBytes[0] == '.') {
+ if ((nameAsBytes.length == 1) ||
+ (nameAsBytes.length == 2 && nameAsBytes[1] == '.')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Returns next entry (or null)
+ private Path readNextEntry() {
+ assert Thread.holdsLock(this);
+
+ for (;;) {
+ byte[] nameAsBytes = null;
+
+ // prevent close while reading
+ readLock().lock();
+ try {
+ if (isClosed)
+ throwAsConcurrentModificationException(new
+ ClosedDirectoryStreamException());
+ try {
+ nameAsBytes = readdir(dp);
+ } catch (UnixException x) {
+ try {
+ x.rethrowAsIOException(dir);
+ } catch (IOException ioe) {
+ throwAsConcurrentModificationException(ioe);
+ }
+ }
+ } finally {
+ readLock().unlock();
+ }
+
+ // EOF
+ if (nameAsBytes == null) {
+ return null;
+ }
+
+ // ignore "." and ".."
+ if (!isSelfOrParent(nameAsBytes)) {
+ Path entry = dir.resolve(nameAsBytes);
+
+ // return entry if no filter or filter accepts it
+ if (filter.accept(entry)) {
+ return entry;
+ }
+ }
+ }
+ }
+
+ @Override
+ public synchronized boolean hasNext() {
+ if (nextEntry == null && !atEof) {
+ nextEntry = readNextEntry();
+
+ // at EOF?
+ if (nextEntry == null)
+ atEof = true;
+ }
+ return nextEntry != null;
+ }
+
+ @Override
+ public synchronized Path next() {
+ if (nextEntry == null) {
+ if (!atEof) {
+ nextEntry = readNextEntry();
+ }
+ if (nextEntry == null) {
+ atEof = true;
+ throw new NoSuchElementException();
+ }
+ }
+ prevEntry = nextEntry;
+ nextEntry = null;
+ return prevEntry;
+ }
+
+ @Override
+ public void remove() {
+ if (isClosed) {
+ throw new ClosedDirectoryStreamException();
+ }
+ Path entry;
+ synchronized (this) {
+ if (prevEntry == null)
+ throw new IllegalStateException("No previous entry to remove");
+ entry = prevEntry;
+ prevEntry = null;
+ }
+
+ // use (race-free) unlinkat if available
+ try {
+ if (stream instanceof UnixSecureDirectoryStream) {
+ ((UnixSecureDirectoryStream)stream)
+ .implDelete(entry.getName(), false, 0);
+ } else {
+ entry.delete(true);
+ }
+ } catch (IOException ioe) {
+ throwAsConcurrentModificationException(ioe);
+ } catch (SecurityException se) {
+ throwAsConcurrentModificationException(se);
+ }
+ }
+ }
+
+ private static void throwAsConcurrentModificationException(Throwable t) {
+ ConcurrentModificationException cme = new ConcurrentModificationException();
+ cme.initCause(t);
+ throw cme;
+ }
+
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixException.java b/src/solaris/classes/sun/nio/fs/UnixException.java
new file mode 100644
index 000000000..3a9967540
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixException.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+
+/**
+ * Internal exception thrown by native methods when error detected.
+ */
+
+class UnixException extends Exception {
+ static final long serialVersionUID = 7227016794320723218L;
+
+ private int errno;
+ private String msg;
+
+ UnixException(int errno) {
+ this.errno = errno;
+ this.msg = null;
+ }
+
+ UnixException(String msg) {
+ this.errno = 0;
+ this.msg = msg;
+ }
+
+ int errno() {
+ return errno;
+ }
+
+ void setError(int errno) {
+ this.errno = errno;
+ this.msg = null;
+ }
+
+ String errorString() {
+ if (msg != null) {
+ return msg;
+ } else {
+ return new String(UnixNativeDispatcher.strerror(errno()));
+ }
+ }
+
+ @Override
+ public String getMessage() {
+ return errorString();
+ }
+
+ /**
+ * Map well known errors to specific exceptions where possible; otherwise
+ * return more general FileSystemException.
+ */
+ private IOException translateToIOException(String file, String other) {
+ // created with message rather than errno
+ if (msg != null)
+ return new IOException(msg);
+
+ // handle specific cases
+ if (errno() == UnixConstants.EACCES)
+ return new AccessDeniedException(file, other, null);
+ if (errno() == UnixConstants.ENOENT)
+ return new NoSuchFileException(file, other, null);
+ if (errno() == UnixConstants.EEXIST)
+ return new FileAlreadyExistsException(file, other, null);
+
+ // fallback to the more general exception
+ return new FileSystemException(file, other, errorString());
+ }
+
+ void rethrowAsIOException(String file) throws IOException {
+ IOException x = translateToIOException(file, null);
+ throw x;
+ }
+
+ void rethrowAsIOException(UnixPath file, UnixPath other) throws IOException {
+ String a = (file == null) ? null : file.getPathForExecptionMessage();
+ String b = (other == null) ? null : other.getPathForExecptionMessage();
+ IOException x = translateToIOException(a, b);
+ throw x;
+ }
+
+ void rethrowAsIOException(UnixPath file) throws IOException {
+ rethrowAsIOException(file, null);
+ }
+
+ IOException asIOException(UnixPath file) {
+ return translateToIOException(file.getPathForExecptionMessage(), null);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java b/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java
new file mode 100644
index 000000000..7bbf925b7
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+
+class UnixFileAttributeViews {
+
+ static class Basic extends AbstractBasicFileAttributeView {
+ protected final UnixPath file;
+ protected final boolean followLinks;
+
+ Basic(UnixPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ @Override
+ public BasicFileAttributes readAttributes() throws IOException {
+ file.checkRead();
+ try {
+ UnixFileAttributes attrs =
+ UnixFileAttributes.get(file, followLinks);
+ return attrs.asBasicFileAttributes();
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ }
+ @Override
+ public void setTimes(Long lastModifiedTime,
+ Long lastAccessTime,
+ Long createTime,
+ TimeUnit unit) throws IOException
+ {
+ // null => don't change
+ if (lastModifiedTime == null && lastAccessTime == null) {
+ // no effect
+ return;
+ }
+
+ file.checkWrite();
+
+ int fd = file.openForAttributeAccess(followLinks);
+ try {
+ UnixFileAttributes attrs = null;
+
+ // if not changing both attributes then need existing attributes
+ if (lastModifiedTime == null || lastAccessTime == null) {
+ try {
+ attrs = UnixFileAttributes.get(fd);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ }
+
+ // modified time = existing, now, or new value
+ long modTime;
+ if (lastModifiedTime == null) {
+ modTime = attrs.lastModifiedTime();
+ } else {
+ if (lastModifiedTime >= 0L) {
+ modTime = TimeUnit.MILLISECONDS.convert(lastModifiedTime, unit);
+ } else {
+ if (lastModifiedTime != -1L)
+ throw new IllegalArgumentException();
+ modTime = System.currentTimeMillis();
+ }
+ }
+
+ // access time = existing, now, or new value
+ long accTime;
+ if (lastAccessTime == null) {
+ accTime = attrs.lastAccessTime();
+ } else {
+ if (lastAccessTime >= 0L) {
+ accTime = TimeUnit.MILLISECONDS.convert(lastAccessTime, unit);
+ } else {
+ if (lastAccessTime != -1L)
+ throw new IllegalArgumentException();
+ accTime = System.currentTimeMillis();
+ }
+ }
+
+ try {
+ futimes(fd, accTime, modTime);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ } finally {
+ close(fd);
+ }
+ }
+ }
+
+ private static class Posix extends Basic implements PosixFileAttributeView {
+ private static final String PERMISSIONS_NAME = "permissions";
+ private static final String OWNER_NAME = "owner";
+ private static final String GROUP_NAME = "group";
+
+ Posix(UnixPath file, boolean followLinks) {
+ super(file, followLinks);
+ }
+
+ final void checkReadExtended() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ file.checkRead();
+ sm.checkPermission(new RuntimePermission("accessUserInformation"));
+ }
+ }
+
+ final void checkWriteExtended() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ file.checkWrite();
+ sm.checkPermission(new RuntimePermission("accessUserInformation"));
+ }
+ }
+
+ @Override
+ public String name() {
+ return "posix";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(PERMISSIONS_NAME))
+ return readAttributes().permissions();
+ if (attribute.equals(OWNER_NAME))
+ return readAttributes().owner();
+ if (attribute.equals(GROUP_NAME))
+ return readAttributes().group();
+ return super.getAttribute(attribute);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(PERMISSIONS_NAME)) {
+ setPermissions((Set<PosixFilePermission>)value);
+ return;
+ }
+ if (attribute.equals(OWNER_NAME)) {
+ setOwner((UserPrincipal)value);
+ return;
+ }
+ if (attribute.equals(GROUP_NAME)) {
+ setGroup((GroupPrincipal)value);
+ return;
+ }
+ super.setAttribute(attribute, value);
+ }
+
+ /**
+ * Invoked by readAttributes or sub-classes to add all matching posix
+ * attributes to the builder
+ */
+ final void addPosixAttributesToBuilder(PosixFileAttributes attrs,
+ AttributesBuilder builder)
+ {
+ if (builder.match(PERMISSIONS_NAME))
+ builder.add(PERMISSIONS_NAME, attrs.permissions());
+ if (builder.match(OWNER_NAME))
+ builder.add(OWNER_NAME, attrs.owner());
+ if (builder.match(GROUP_NAME))
+ builder.add(GROUP_NAME, attrs.group());
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ AttributesBuilder builder = AttributesBuilder.create(first, rest);
+ PosixFileAttributes attrs = readAttributes();
+ addBasicAttributesToBuilder(attrs, builder);
+ addPosixAttributesToBuilder(attrs, builder);
+ return builder.unmodifiableMap();
+ }
+
+ @Override
+ public UnixFileAttributes readAttributes() throws IOException {
+ checkReadExtended();
+ try {
+ return UnixFileAttributes.get(file, followLinks);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ }
+
+ // chmod
+ final void setMode(int mode) throws IOException {
+ checkWriteExtended();
+ try {
+ if (followLinks) {
+ chmod(file, mode);
+ } else {
+ int fd = file.openForAttributeAccess(false);
+ try {
+ fchmod(fd, mode);
+ } finally {
+ close(fd);
+ }
+ }
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ }
+
+ // chown
+ final void setOwners(int uid, int gid) throws IOException {
+ checkWriteExtended();
+ try {
+ if (followLinks) {
+ chown(file, uid, gid);
+ } else {
+ lchown(file, uid, gid);
+ }
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ }
+
+ @Override
+ public void setPermissions(Set<PosixFilePermission> perms)
+ throws IOException
+ {
+ setMode(UnixFileModeAttribute.toUnixMode(perms));
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner)
+ throws IOException
+ {
+ if (owner == null)
+ throw new NullPointerException("'owner' is null");
+ if (!(owner instanceof UnixUserPrincipals.User))
+ throw new ProviderMismatchException();
+ if (owner instanceof UnixUserPrincipals.Group)
+ throw new IOException("'owner' parameter can't be a group");
+ int uid = ((UnixUserPrincipals.User)owner).uid();
+ setOwners(uid, -1);
+ }
+
+ @Override
+ public UserPrincipal getOwner() throws IOException {
+ return readAttributes().owner();
+ }
+
+ @Override
+ public void setGroup(GroupPrincipal group)
+ throws IOException
+ {
+ if (group == null)
+ throw new NullPointerException("'owner' is null");
+ if (!(group instanceof UnixUserPrincipals.Group))
+ throw new ProviderMismatchException();
+ int gid = ((UnixUserPrincipals.Group)group).gid();
+ setOwners(-1, gid);
+ }
+ }
+
+ private static class Unix extends Posix {
+ private static final String MODE_NAME = "mode";
+ private static final String INO_NAME = "ino";
+ private static final String DEV_NAME = "dev";
+ private static final String RDEV_NAME = "rdev";
+ private static final String UID_NAME = "uid";
+ private static final String GID_NAME = "gid";
+ private static final String CTIME_NAME = "ctime";
+
+ Unix(UnixPath file, boolean followLinks) {
+ super(file, followLinks);
+ }
+
+ @Override
+ public String name() {
+ return "unix";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(MODE_NAME))
+ return readAttributes().mode();
+ if (attribute.equals(INO_NAME))
+ return readAttributes().ino();
+ if (attribute.equals(DEV_NAME))
+ return readAttributes().dev();
+ if (attribute.equals(RDEV_NAME))
+ return readAttributes().rdev();
+ if (attribute.equals(UID_NAME))
+ return readAttributes().uid();
+ if (attribute.equals(GID_NAME))
+ return readAttributes().gid();
+ if (attribute.equals(CTIME_NAME))
+ return readAttributes().ctime();
+ return super.getAttribute(attribute);
+ }
+
+ @Override
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(MODE_NAME)) {
+ setMode((Integer)value);
+ return;
+ }
+ if (attribute.equals(UID_NAME)) {
+ setOwners((Integer)value, -1);
+ return;
+ }
+ if (attribute.equals(GID_NAME)) {
+ setOwners(-1, (Integer)value);
+ return;
+ }
+ super.setAttribute(attribute, value);
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ AttributesBuilder builder = AttributesBuilder.create(first, rest);
+ UnixFileAttributes attrs = readAttributes();
+ addBasicAttributesToBuilder(attrs, builder);
+ addPosixAttributesToBuilder(attrs, builder);
+ if (builder.match(MODE_NAME))
+ builder.add(MODE_NAME, attrs.mode());
+ if (builder.match(INO_NAME))
+ builder.add(INO_NAME, attrs.ino());
+ if (builder.match(DEV_NAME))
+ builder.add(DEV_NAME, attrs.dev());
+ if (builder.match(RDEV_NAME))
+ builder.add(RDEV_NAME, attrs.rdev());
+ if (builder.match(UID_NAME))
+ builder.add(UID_NAME, attrs.uid());
+ if (builder.match(GID_NAME))
+ builder.add(GID_NAME, attrs.gid());
+ if (builder.match(CTIME_NAME))
+ builder.add(CTIME_NAME, attrs.ctime());
+ return builder.unmodifiableMap();
+ }
+ }
+
+ static BasicFileAttributeView createBasicView(UnixPath file, boolean followLinks) {
+ return new Basic(file, followLinks);
+ }
+
+ static PosixFileAttributeView createPosixView(UnixPath file, boolean followLinks) {
+ return new Posix(file, followLinks);
+ }
+
+ static PosixFileAttributeView createUnixView(UnixPath file, boolean followLinks) {
+ return new Unix(file, followLinks);
+ }
+
+ static FileOwnerAttributeView createOwnerView(UnixPath file, boolean followLinks) {
+ return new FileOwnerAttributeViewImpl(createPosixView(file, followLinks));
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java b/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java
new file mode 100644
index 000000000..c1882d753
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.concurrent.TimeUnit;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Unix implementation of PosixFileAttributes.
+ */
+
+class UnixFileAttributes
+ implements PosixFileAttributes
+{
+ private int st_mode;
+ private long st_ino;
+ private long st_dev;
+ private long st_rdev;
+ private int st_nlink;
+ private int st_uid;
+ private int st_gid;
+ private long st_size;
+ private long st_atime;
+ private long st_mtime;
+ private long st_ctime;
+
+ // created lazily
+ private volatile UserPrincipal owner;
+ private volatile GroupPrincipal group;
+ private volatile UnixFileKey key;
+
+ private UnixFileAttributes() {
+ }
+
+ // get the UnixFileAttributes for a given file
+ static UnixFileAttributes get(UnixPath path, boolean followLinks)
+ throws UnixException
+ {
+ UnixFileAttributes attrs = new UnixFileAttributes();
+ if (followLinks) {
+ UnixNativeDispatcher.stat(path, attrs);
+ } else {
+ UnixNativeDispatcher.lstat(path, attrs);
+ }
+ return attrs;
+ }
+
+ // get the UnixFileAttributes for an open file
+ static UnixFileAttributes get(int fd) throws UnixException {
+ UnixFileAttributes attrs = new UnixFileAttributes();
+ UnixNativeDispatcher.fstat(fd, attrs);
+ return attrs;
+ }
+
+ // get the UnixFileAttributes for a given file, relative to open directory
+ static UnixFileAttributes get(int dfd, UnixPath path, boolean followLinks)
+ throws UnixException
+ {
+ UnixFileAttributes attrs = new UnixFileAttributes();
+ int flag = (followLinks) ? 0 : UnixConstants.AT_SYMLINK_NOFOLLOW;
+ UnixNativeDispatcher.fstatat(dfd, path.asByteArray(), flag, attrs);
+ return attrs;
+ }
+
+ // package-private
+ boolean isSameFile(UnixFileAttributes attrs) {
+ return ((st_ino == attrs.st_ino) && (st_dev == attrs.st_dev));
+ }
+
+ // package-private
+ int mode() { return st_mode; }
+ long ino() { return st_ino; }
+ long dev() { return st_dev; }
+ long rdev() { return st_rdev; }
+ int uid() { return st_uid; }
+ int gid() { return st_gid; }
+ long ctime() { return st_ctime; }
+
+ boolean isDevice() {
+ int type = st_mode & UnixConstants.S_IFMT;
+ return (type == UnixConstants.S_IFCHR ||
+ type == UnixConstants.S_IFBLK ||
+ type == UnixConstants.S_IFIFO);
+ }
+
+ @Override
+ public long lastModifiedTime() {
+ return st_mtime;
+ }
+
+ @Override
+ public long lastAccessTime() {
+ return st_atime;
+ }
+
+ @Override
+ public long creationTime() {
+ return -1L;
+ }
+
+ @Override
+ public TimeUnit resolution() {
+ return TimeUnit.MILLISECONDS;
+ }
+
+ @Override
+ public boolean isRegularFile() {
+ return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFREG);
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR);
+ }
+
+ @Override
+ public boolean isSymbolicLink() {
+ return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFLNK);
+ }
+
+ @Override
+ public boolean isOther() {
+ int type = st_mode & UnixConstants.S_IFMT;
+ return (type != UnixConstants.S_IFREG &&
+ type != UnixConstants.S_IFDIR &&
+ type != UnixConstants.S_IFLNK);
+ }
+
+ @Override
+ public long size() {
+ return st_size;
+ }
+
+ @Override
+ public int linkCount() {
+ return st_nlink;
+ }
+
+ @Override
+ public UnixFileKey fileKey() {
+ if (key == null) {
+ synchronized (this) {
+ if (key == null) {
+ key = new UnixFileKey(st_dev, st_ino);
+ }
+ }
+ }
+ return key;
+ }
+
+ @Override
+ public UserPrincipal owner() {
+ if (owner == null) {
+ synchronized (this) {
+ if (owner == null) {
+ owner = UnixUserPrincipals.fromUid(st_uid);
+ }
+ }
+ }
+ return owner;
+ }
+
+ @Override
+ public GroupPrincipal group() {
+ if (group == null) {
+ synchronized (this) {
+ if (group == null) {
+ group = UnixUserPrincipals.fromGid(st_gid);
+ }
+ }
+ }
+ return group;
+ }
+
+ @Override
+ public Set<PosixFilePermission> permissions() {
+ int bits = (st_mode & UnixConstants.S_IAMB);
+ HashSet<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
+
+ if ((bits & UnixConstants.S_IRUSR) > 0)
+ perms.add(PosixFilePermission.OWNER_READ);
+ if ((bits & UnixConstants.S_IWUSR) > 0)
+ perms.add(PosixFilePermission.OWNER_WRITE);
+ if ((bits & UnixConstants.S_IXUSR) > 0)
+ perms.add(PosixFilePermission.OWNER_EXECUTE);
+
+ if ((bits & UnixConstants.S_IRGRP) > 0)
+ perms.add(PosixFilePermission.GROUP_READ);
+ if ((bits & UnixConstants.S_IWGRP) > 0)
+ perms.add(PosixFilePermission.GROUP_WRITE);
+ if ((bits & UnixConstants.S_IXGRP) > 0)
+ perms.add(PosixFilePermission.GROUP_EXECUTE);
+
+ if ((bits & UnixConstants.S_IROTH) > 0)
+ perms.add(PosixFilePermission.OTHERS_READ);
+ if ((bits & UnixConstants.S_IWOTH) > 0)
+ perms.add(PosixFilePermission.OTHERS_WRITE);
+ if ((bits & UnixConstants.S_IXOTH) > 0)
+ perms.add(PosixFilePermission.OTHERS_EXECUTE);
+
+ return perms;
+ }
+
+ // wrap this object with BasicFileAttributes object to prevent leaking of
+ // user information
+ BasicFileAttributes asBasicFileAttributes() {
+ return UnixAsBasicFileAttributes.wrap(this);
+ }
+
+ // unwrap BasicFileAttributes to get the underlying UnixFileAttributes
+ // object. Returns null is not wrapped.
+ static UnixFileAttributes toUnixFileAttributes(BasicFileAttributes attrs) {
+ if (attrs instanceof UnixFileAttributes)
+ return (UnixFileAttributes)attrs;
+ if (attrs instanceof UnixAsBasicFileAttributes) {
+ return ((UnixAsBasicFileAttributes)attrs).unwrap();
+ }
+ return null;
+ }
+
+ // wrap a UnixFileAttributes object as a BasicFileAttributes
+ private static class UnixAsBasicFileAttributes implements BasicFileAttributes {
+ private final UnixFileAttributes attrs;
+
+ private UnixAsBasicFileAttributes(UnixFileAttributes attrs) {
+ this.attrs = attrs;
+ }
+
+ static UnixAsBasicFileAttributes wrap(UnixFileAttributes attrs) {
+ return new UnixAsBasicFileAttributes(attrs);
+ }
+
+ UnixFileAttributes unwrap() {
+ return attrs;
+ }
+
+ @Override
+ public long lastModifiedTime() {
+ return attrs.lastModifiedTime();
+ }
+ @Override
+ public long lastAccessTime() {
+ return attrs.lastAccessTime();
+ }
+ @Override
+ public long creationTime() {
+ return attrs.creationTime();
+ }
+ @Override
+ public TimeUnit resolution() {
+ return attrs.resolution();
+ }
+ @Override
+ public boolean isRegularFile() {
+ return attrs.isRegularFile();
+ }
+ @Override
+ public boolean isDirectory() {
+ return attrs.isDirectory();
+ }
+ @Override
+ public boolean isSymbolicLink() {
+ return attrs.isSymbolicLink();
+ }
+ @Override
+ public boolean isOther() {
+ return attrs.isOther();
+ }
+ @Override
+ public long size() {
+ return attrs.size();
+ }
+ @Override
+ public int linkCount() {
+ return attrs.linkCount();
+ }
+ @Override
+ public Object fileKey() {
+ return attrs.fileKey();
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileKey.java b/src/solaris/classes/sun/nio/fs/UnixFileKey.java
new file mode 100644
index 000000000..4ab3aec6c
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileKey.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Container for device/inode to uniquely identify file.
+ */
+
+class UnixFileKey {
+ private final long st_dev;
+ private final long st_ino;
+
+ UnixFileKey(long st_dev, long st_ino) {
+ this.st_dev = st_dev;
+ this.st_ino = st_ino;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)(st_dev ^ (st_dev >>> 32)) +
+ (int)(st_ino ^ (st_ino >>> 32));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof UnixFileKey))
+ return false;
+ UnixFileKey other = (UnixFileKey)obj;
+ return (this.st_dev == other.st_dev) && (this.st_ino == other.st_ino);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileModeAttribute.java b/src/solaris/classes/sun/nio/fs/UnixFileModeAttribute.java
new file mode 100644
index 000000000..67ea59e94
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileModeAttribute.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+
+class UnixFileModeAttribute {
+ static final int ALL_PERMISSIONS =
+ UnixConstants.S_IRUSR | UnixConstants.S_IWUSR | UnixConstants.S_IXUSR |
+ UnixConstants.S_IRGRP | UnixConstants.S_IWGRP | UnixConstants.S_IXGRP |
+ UnixConstants.S_IROTH | UnixConstants.S_IWOTH | UnixConstants. S_IXOTH;
+
+ static final int ALL_READWRITE =
+ UnixConstants.S_IRUSR | UnixConstants.S_IWUSR |
+ UnixConstants.S_IRGRP | UnixConstants.S_IWGRP |
+ UnixConstants.S_IROTH | UnixConstants.S_IWOTH;
+
+ static final int TEMPFILE_PERMISSIONS =
+ UnixConstants.S_IRUSR | UnixConstants.S_IWUSR | UnixConstants.S_IXUSR;
+
+ private Set<PosixFilePermission> perms;
+
+ UnixFileModeAttribute() {
+ perms = Collections.emptySet();
+ }
+
+ static int toUnixMode(Set<PosixFilePermission> perms) {
+ int mode = 0;
+ for (PosixFilePermission perm: perms) {
+ if (perm == null)
+ throw new NullPointerException();
+ switch (perm) {
+ case OWNER_READ : mode |= UnixConstants.S_IRUSR; break;
+ case OWNER_WRITE : mode |= UnixConstants.S_IWUSR; break;
+ case OWNER_EXECUTE : mode |= UnixConstants.S_IXUSR; break;
+ case GROUP_READ : mode |= UnixConstants.S_IRGRP; break;
+ case GROUP_WRITE : mode |= UnixConstants.S_IWGRP; break;
+ case GROUP_EXECUTE : mode |= UnixConstants.S_IXGRP; break;
+ case OTHERS_READ : mode |= UnixConstants.S_IROTH; break;
+ case OTHERS_WRITE : mode |= UnixConstants.S_IWOTH; break;
+ case OTHERS_EXECUTE : mode |= UnixConstants.S_IXOTH; break;
+ }
+ }
+ return mode;
+ }
+
+ @SuppressWarnings("unchecked")
+ static int toUnixMode(int defaultMode, FileAttribute<?>... attrs) {
+ int mode = defaultMode;
+ for (FileAttribute<?> attr: attrs) {
+ String name = attr.name();
+ if (!name.equals("posix:permissions") && !name.equals("unix:permissions")) {
+ throw new UnsupportedOperationException("'" + attr.name() +
+ "' not supported as initial attribute");
+ }
+ mode = toUnixMode((Set<PosixFilePermission>)attr.value());
+ }
+ return mode;
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileStore.java b/src/solaris/classes/sun/nio/fs/UnixFileStore.java
new file mode 100644
index 000000000..483a4ea48
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileStore.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Base implementation of FileStore for Unix/like implementations.
+ */
+
+abstract class UnixFileStore
+ extends FileStore
+{
+ // original path of file that identified file system
+ private final UnixPath file;
+
+ // device ID
+ private final long dev;
+
+ // entry in the mount tab
+ private final UnixMountEntry entry;
+
+ // return the device ID where the given file resides
+ private static long devFor(UnixPath file) throws IOException {
+ try {
+ return UnixFileAttributes.get(file, true).dev();
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return 0L; // keep compiler happy
+ }
+ }
+
+ UnixFileStore(UnixPath file) throws IOException {
+ this.file = file;
+ this.dev = devFor(file);
+ this.entry = findMountEntry();
+ }
+
+ UnixFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
+ this.file = new UnixPath(fs, entry.dir());
+ this.dev = (entry.dev() == 0L) ? devFor(this.file) : entry.dev();
+ this.entry = entry;
+ }
+
+ /**
+ * Find the mount entry for the file store
+ */
+ abstract UnixMountEntry findMountEntry() throws IOException;
+
+ /**
+ * Returns true if this file store represents a loopback file system that
+ * will have the same device ID as undelrying file system.
+ */
+ abstract boolean isLoopback();
+
+ UnixPath file() {
+ return file;
+ }
+
+ long dev() {
+ return dev;
+ }
+
+ UnixMountEntry entry() {
+ return entry;
+ }
+
+ @Override
+ public String name() {
+ return entry.name();
+ }
+
+ @Override
+ public String type() {
+ return entry.fstype();
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return entry.isReadOnly();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> viewType)
+ {
+ if (viewType == FileStoreSpaceAttributeView.class)
+ return (V) new UnixFileStoreSpaceAttributeView(this);
+ return (V) null;
+ }
+
+ @Override
+ public FileStoreAttributeView getFileStoreAttributeView(String name) {
+ if (name.equals("space"))
+ return new UnixFileStoreSpaceAttributeView(this);
+ return null;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
+ if (type == BasicFileAttributeView.class)
+ return true;
+ if (type == PosixFileAttributeView.class ||
+ type == FileOwnerAttributeView.class)
+ {
+ // lookup fstypes.properties
+ FeatureStatus status = checkIfFeaturePresent("posix");
+ if (status == FeatureStatus.NOT_PRESENT)
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name) {
+ if (name.equals("basic") || name.equals("unix"))
+ return true;
+ if (name.equals("posix"))
+ return supportsFileAttributeView(PosixFileAttributeView.class);
+ if (name.equals("owner"))
+ return supportsFileAttributeView(FileOwnerAttributeView.class);
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object ob) {
+ if (ob == this)
+ return true;
+ if (!(ob instanceof UnixFileStore))
+ return false;
+ UnixFileStore other = (UnixFileStore)ob;
+ if (dev != other.dev)
+ return false;
+ // deviceIDs are equal but they may not be equal if one or both of
+ // them is a loopback file system
+ boolean thisIsLoopback = isLoopback();
+ if (thisIsLoopback != other.isLoopback())
+ return false; // one, but not both, are lofs
+ if (!thisIsLoopback)
+ return true; // neither is lofs
+ // both are lofs so compare mount points
+ return Arrays.equals(this.entry.dir(), other.entry.dir());
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)(dev ^ (dev >>> 32));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(new String(entry.dir()));
+ sb.append(" (");
+ sb.append(entry.name());
+ sb.append(")");
+ return sb.toString();
+ }
+
+ private static class UnixFileStoreSpaceAttributeView
+ extends AbstractFileStoreSpaceAttributeView
+ {
+ private final UnixFileStore fs;
+
+ UnixFileStoreSpaceAttributeView(UnixFileStore fs) {
+ this.fs = fs;
+ }
+
+ @Override
+ public FileStoreSpaceAttributes readAttributes()
+ throws IOException
+ {
+ UnixPath file = fs.file();
+ final UnixFileStoreAttributes attrs;
+ try {
+ attrs = UnixFileStoreAttributes.get(file);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compile happy
+ }
+
+ return new FileStoreSpaceAttributes() {
+ @Override
+ public long totalSpace() {
+ return attrs.blockSize() * attrs.totalBlocks();
+ }
+ @Override
+ public long usableSpace() {
+ return attrs.blockSize() * attrs.availableBlocks();
+ }
+ @Override
+ public long unallocatedSpace() {
+ return attrs.blockSize() * attrs.freeBlocks();
+ }
+ };
+ }
+ }
+
+ // -- fstypes.properties --
+
+ private static final Object loadLock = new Object();
+ private static volatile Properties props;
+
+ enum FeatureStatus {
+ PRESENT,
+ NOT_PRESENT,
+ UNKNOWN;
+ }
+
+ /**
+ * Returns status to indicate if file system supports a given feature
+ */
+ FeatureStatus checkIfFeaturePresent(String feature) {
+ if (props == null) {
+ synchronized (loadLock) {
+ if (props == null) {
+ props = AccessController.doPrivileged(
+ new PrivilegedAction<Properties>() {
+ @Override
+ public Properties run() {
+ return loadProperties();
+ }});
+ }
+ }
+ }
+
+ String value = props.getProperty(type());
+ if (value != null) {
+ String[] values = value.split("\\s");
+ for (String s: values) {
+ s = s.trim().toLowerCase();
+ if (s.equals(feature)) {
+ return FeatureStatus.PRESENT;
+ }
+ if (s.startsWith("no")) {
+ s = s.substring(2);
+ if (s.equals(feature)) {
+ return FeatureStatus.NOT_PRESENT;
+ }
+ }
+ }
+ }
+ return FeatureStatus.UNKNOWN;
+ }
+
+ private static Properties loadProperties() {
+ Properties result = new Properties();
+ String fstypes = System.getProperty("java.home") + "/lib/fstypes.properties";
+ FileRef file = Paths.get(fstypes);
+ try {
+ ReadableByteChannel rbc = file.newByteChannel();
+ try {
+ result.load(Channels.newReader(rbc, "UTF-8"));
+ } finally {
+ rbc.close();
+ }
+ } catch (IOException x) {
+ }
+ return result;
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileStoreAttributes.java b/src/solaris/classes/sun/nio/fs/UnixFileStoreAttributes.java
new file mode 100644
index 000000000..f2cbd17cc
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileStoreAttributes.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+class UnixFileStoreAttributes {
+ private long f_frsize; // block size
+ private long f_blocks; // total
+ private long f_bfree; // free
+ private long f_bavail; // usable
+
+ private UnixFileStoreAttributes() {
+ }
+
+ static UnixFileStoreAttributes get(UnixPath path) throws UnixException {
+ UnixFileStoreAttributes attrs = new UnixFileStoreAttributes();
+ UnixNativeDispatcher.statvfs(path, attrs);
+ return attrs;
+ }
+
+ long blockSize() {
+ return f_frsize;
+ }
+
+ long totalBlocks() {
+ return f_blocks;
+ }
+
+ long freeBlocks() {
+ return f_bfree;
+ }
+
+ long availableBlocks() {
+ return f_bavail;
+ }
+
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileSystem.java b/src/solaris/classes/sun/nio/fs/UnixFileSystem.java
new file mode 100644
index 000000000..6e46b2647
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileSystem.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.*;
+import java.io.IOException;
+import java.util.*;
+import java.util.regex.Pattern;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * Base implementation of FileSystem for Unix-like implementations.
+ */
+
+abstract class UnixFileSystem
+ extends FileSystem
+{
+ private final UnixFileSystemProvider provider;
+ private final byte[] defaultDirectory;
+ private final boolean needToResolveAgainstDefaultDirectory;
+ private final UnixPath rootDirectory;
+
+ // package-private
+ UnixFileSystem(UnixFileSystemProvider provider, String dir) {
+ this.provider = provider;
+ this.defaultDirectory = UnixPath.normalizeAndCheck(dir).getBytes();
+ if (this.defaultDirectory[0] != '/') {
+ throw new RuntimeException("default directory must be absolute");
+ }
+
+ // if process-wide chdir is allowed or default directory is not the
+ // process working directory then paths must be resolved against the
+ // default directory.
+ String propValue = AccessController.doPrivileged(
+ new GetPropertyAction("sun.nio.fs.chdirAllowed", "false"));
+ boolean chdirAllowed = (propValue.length() == 0) ?
+ true : Boolean.valueOf(propValue);
+ if (chdirAllowed) {
+ this.needToResolveAgainstDefaultDirectory = true;
+ } else {
+ byte[] cwd = UnixNativeDispatcher.getcwd();
+ boolean defaultIsCwd = (cwd.length == defaultDirectory.length);
+ if (defaultIsCwd) {
+ for (int i=0; i<cwd.length; i++) {
+ if (cwd[i] != defaultDirectory[i]) {
+ defaultIsCwd = false;
+ break;
+ }
+ }
+ }
+ this.needToResolveAgainstDefaultDirectory = !defaultIsCwd;
+ }
+
+ // the root directory
+ this.rootDirectory = new UnixPath(this, "/");
+ }
+
+ // package-private
+ byte[] defaultDirectory() {
+ return defaultDirectory;
+ }
+
+ boolean needToResolveAgainstDefaultDirectory() {
+ return needToResolveAgainstDefaultDirectory;
+ }
+
+ UnixPath rootDirectory() {
+ return rootDirectory;
+ }
+
+ boolean isSolaris() {
+ return false;
+ }
+
+ @Override
+ public final FileSystemProvider provider() {
+ return provider;
+ }
+
+ @Override
+ public final String getSeparator() {
+ return "/";
+ }
+
+ @Override
+ public final boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public final boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ public final void close() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Copies non-POSIX attributes from the source to target file.
+ *
+ * Copying a file preserving attributes, or moving a file, will preserve
+ * the file owner/group/permissions/timestamps but it does not preserve
+ * other non-POSIX attributes. This method is invoked by the
+ * copy or move operation to preserve these attributes. It should copy
+ * extended attributes, ACLs, or other attributes.
+ *
+ * @param sfd
+ * Open file descriptor to source file
+ * @param tfd
+ * Open file descriptor to target file
+ */
+ abstract void copyNonPosixAttributes(int sfd, int tfd);
+
+ /**
+ * Tells if directory relative system calls (openat, etc.) are available
+ * on this operating system.
+ */
+ abstract boolean supportsSecureDirectoryStreams();
+
+ /**
+ * Unix systems only have a single root directory (/)
+ */
+ @Override
+ public final Iterable<Path> getRootDirectories() {
+ final List<Path> allowedList =
+ Collections.unmodifiableList(Arrays.asList((Path)rootDirectory));
+ return new Iterable<Path>() {
+ public Iterator<Path> iterator() {
+ try {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkRead(rootDirectory.toString());
+ return allowedList.iterator();
+ } catch (SecurityException x) {
+ List<Path> disallowed = Collections.emptyList();
+ return disallowed.iterator();
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns object to iterate over entries in mounttab or equivalent
+ */
+ abstract Iterable<UnixMountEntry> getMountEntries();
+
+ /**
+ * Returns a FileStore to represent the file system where the given file
+ * reside.
+ */
+ abstract FileStore getFileStore(UnixPath path) throws IOException;
+
+ /**
+ * Returns a FileStore to represent the file system for the given mount
+ * mount.
+ */
+ abstract FileStore getFileStore(UnixMountEntry entry) throws IOException;
+
+ /**
+ * Iterator returned by getFileStores method.
+ */
+ private class FileStoreIterator implements Iterator<FileStore> {
+ private final Iterator<UnixMountEntry> entries;
+ private FileStore next;
+
+ FileStoreIterator() {
+ this.entries = getMountEntries().iterator();
+ }
+
+ private FileStore readNext() {
+ assert Thread.holdsLock(this);
+ for (;;) {
+ if (!entries.hasNext())
+ return null;
+ UnixMountEntry entry = entries.next();
+
+ // skip entries with the "ignore" option
+ if (entry.isIgnored())
+ continue;
+
+ // check permission to read mount point
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ sm.checkRead(new String(entry.dir()));
+ } catch (SecurityException x) {
+ continue;
+ }
+ }
+ try {
+ return getFileStore(entry);
+ } catch (IOException ignore) {
+ // ignore as per spec
+ }
+ }
+ }
+
+ @Override
+ public synchronized boolean hasNext() {
+ if (next != null)
+ return true;
+ next = readNext();
+ return next != null;
+ }
+
+ @Override
+ public synchronized FileStore next() {
+ if (next == null)
+ next = readNext();
+ if (next == null) {
+ throw new NoSuchElementException();
+ } else {
+ FileStore result = next;
+ next = null;
+ return result;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public final Iterable<FileStore> getFileStores() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
+ } catch (SecurityException se) {
+ return Collections.emptyList();
+ }
+ }
+ return new Iterable<FileStore>() {
+ public Iterator<FileStore> iterator() {
+ return new FileStoreIterator();
+ }
+ };
+ }
+
+ @Override
+ public final UnixPath getPath(String path) {
+ return new UnixPath(this, path);
+ }
+
+ @Override
+ public PathMatcher getPathMatcher(String syntaxAndInput) {
+ int pos = syntaxAndInput.indexOf(':');
+ if (pos <= 0 || pos == syntaxAndInput.length())
+ throw new IllegalArgumentException();
+ String syntax = syntaxAndInput.substring(0, pos);
+ String input = syntaxAndInput.substring(pos+1);
+
+ String expr;
+ if (syntax.equals(GLOB_SYNTAX)) {
+ expr = Globs.toUnixRegexPattern(input);
+ } else {
+ if (syntax.equals(REGEX_SYNTAX)) {
+ expr = input;
+ } else {
+ throw new UnsupportedOperationException("Syntax '" + syntax +
+ "' not recognized");
+ }
+ }
+
+ // return matcher
+ final Pattern pattern = Pattern.compile(expr);
+ return new PathMatcher() {
+ @Override
+ public boolean matches(Path path) {
+ return pattern.matcher(path.toString()).matches();
+ }
+ };
+ }
+ private static final String GLOB_SYNTAX = "glob";
+ private static final String REGEX_SYNTAX = "regex";
+
+ protected boolean followLinks(LinkOption... options) {
+ boolean followLinks = true;
+ for (LinkOption option: options) {
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ followLinks = false;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new AssertionError("Should not get here");
+ }
+ return followLinks;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <V extends FileAttributeView> V newFileAttributeView(Class<V> view,
+ UnixPath file,
+ LinkOption... options)
+ {
+ if (view == null)
+ throw new NullPointerException();
+ boolean followLinks = followLinks(options);
+ Class<?> c = view;
+ if (c == BasicFileAttributeView.class)
+ return (V) UnixFileAttributeViews.createBasicView(file, followLinks);
+ if (c == PosixFileAttributeView.class)
+ return (V) UnixFileAttributeViews.createPosixView(file, followLinks);
+ if (c == FileOwnerAttributeView.class)
+ return (V) UnixFileAttributeViews.createOwnerView(file, followLinks);
+ return (V) null;
+ }
+
+ static List<String> standardFileAttributeViews() {
+ return Arrays.asList("basic", "posix", "unix", "owner");
+ }
+
+ protected FileAttributeView newFileAttributeView(String name,
+ UnixPath file,
+ LinkOption... options)
+ {
+ boolean followLinks = followLinks(options);
+ if (name.equals("basic"))
+ return UnixFileAttributeViews.createBasicView(file, followLinks);
+ if (name.equals("posix"))
+ return UnixFileAttributeViews.createPosixView(file, followLinks);
+ if (name.equals("unix"))
+ return UnixFileAttributeViews.createUnixView(file, followLinks);
+ if (name.equals("owner"))
+ return UnixFileAttributeViews.createOwnerView(file, followLinks);
+ return null;
+ }
+
+ @Override
+ public final UserPrincipalLookupService getUserPrincipalLookupService() {
+ return theLookupService;
+ }
+
+ private static final UserPrincipalLookupService theLookupService =
+ new UserPrincipalLookupService() {
+ @Override
+ public UserPrincipal lookupPrincipalByName(String name)
+ throws IOException
+ {
+ return UnixUserPrincipals.lookupUser(name);
+ }
+
+ @Override
+ public GroupPrincipal lookupPrincipalByGroupName(String group)
+ throws IOException
+ {
+ return UnixUserPrincipals.lookupGroup(group);
+ }
+ };
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java
new file mode 100644
index 000000000..878fe25c5
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.FileSystemProvider;
+import java.nio.channels.*;
+import java.net.URI;
+import java.util.concurrent.ExecutorService;
+import java.io.IOException;
+import java.util.*;
+
+import sun.nio.ch.ThreadPool;
+
+/**
+ * Base implementation of FileSystemProvider
+ */
+
+public abstract class UnixFileSystemProvider
+ extends FileSystemProvider
+{
+ private static final String USER_DIR = "user.dir";
+ private final UnixFileSystem theFileSystem;
+
+ public UnixFileSystemProvider() {
+ String userDir = System.getProperty(USER_DIR);
+ theFileSystem = newFileSystem(userDir);
+ }
+
+ /**
+ * Constructs a new file system using the given default directory.
+ */
+ abstract UnixFileSystem newFileSystem(String dir);
+
+ @Override
+ public final String getScheme() {
+ return "file";
+ }
+
+ private void checkUri(URI uri) {
+ if (!uri.getScheme().equalsIgnoreCase(getScheme()))
+ throw new IllegalArgumentException("URI does not match this provider");
+ if (uri.getAuthority() != null)
+ throw new IllegalArgumentException("Authority component present");
+ if (uri.getPath() == null)
+ throw new IllegalArgumentException("Path component is undefined");
+ if (!uri.getPath().equals("/"))
+ throw new IllegalArgumentException("Path component should be '/'");
+ if (uri.getQuery() != null)
+ throw new IllegalArgumentException("Query component present");
+ if (uri.getFragment() != null)
+ throw new IllegalArgumentException("Fragment component present");
+ }
+
+ @Override
+ public final FileSystem newFileSystem(URI uri, Map<String,?> env) {
+ checkUri(uri);
+ throw new FileSystemAlreadyExistsException();
+ }
+
+ @Override
+ public final FileSystem getFileSystem(URI uri) {
+ checkUri(uri);
+ return theFileSystem;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ return UnixUriUtils.fromUri(theFileSystem, uri);
+ }
+
+ private UnixPath checkPath(Path obj) {
+ if (obj == null)
+ throw new NullPointerException();
+ if (!(obj instanceof UnixPath))
+ throw new ProviderMismatchException();
+ return (UnixPath)obj;
+ }
+
+ @Override
+ public final FileChannel newFileChannel(Path obj,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ UnixPath file = checkPath(obj);
+ int mode = UnixFileModeAttribute
+ .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
+ try {
+ return UnixChannelFactory.newFileChannel(file, options, mode);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null;
+ }
+ }
+
+ @Override
+ public final AsynchronousFileChannel newAsynchronousFileChannel(Path obj,
+ Set<? extends OpenOption> options,
+ ExecutorService executor,
+ FileAttribute<?>... attrs) throws IOException
+ {
+ UnixPath file = checkPath(obj);
+ int mode = UnixFileModeAttribute
+ .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
+ ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
+ try {
+ return UnixChannelFactory
+ .newAsynchronousFileChannel(file, options, mode, pool);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null;
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixMountEntry.java b/src/solaris/classes/sun/nio/fs/UnixMountEntry.java
new file mode 100644
index 000000000..c4769e7a7
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixMountEntry.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Represents an entry in the mount table.
+ */
+
+class UnixMountEntry {
+ private byte[] name; // file system name
+ private byte[] dir; // directory (mount point)
+ private byte[] fstype; // ufs, nfs, ...
+ private byte[] opts; // mount options
+ private long dev; // device ID
+
+ private volatile String fstypeAsString;
+ private volatile String optionsAsString;
+
+ UnixMountEntry() {
+ }
+
+ String name() {
+ return new String(name);
+ }
+
+ String fstype() {
+ if (fstypeAsString == null)
+ fstypeAsString = new String(fstype);
+ return fstypeAsString;
+ }
+
+ byte[] dir() {
+ return dir;
+ }
+
+ long dev() {
+ return dev;
+ }
+
+ /**
+ * Tells whether the mount entry has the given option.
+ */
+ boolean hasOption(String requested) {
+ if (optionsAsString == null)
+ optionsAsString = new String(opts);
+ for (String opt: optionsAsString.split("\\,", 0)) {
+ if (opt.equals(requested))
+ return true;
+ }
+ return false;
+ }
+
+ // generic option
+ boolean isIgnored() {
+ return hasOption("ignore");
+ }
+
+ // generic option
+ boolean isReadOnly() {
+ return hasOption("ro");
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java
new file mode 100644
index 000000000..29140e3ec
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Unix system and library calls.
+ */
+
+class UnixNativeDispatcher {
+ protected UnixNativeDispatcher() { }
+
+ // returns a NativeBuffer containing the given path
+ private static NativeBuffer copyToNativeBuffer(UnixPath path) {
+ byte[] cstr = path.getByteArrayForSysCalls();
+ int size = cstr.length + 1;
+ NativeBuffer buffer = NativeBuffers.getNativeBufferFromCache(size);
+ if (buffer == null) {
+ buffer = NativeBuffers.allocNativeBuffer(size);
+ } else {
+ // buffer already contains the path
+ if (buffer.owner() == path)
+ return buffer;
+ }
+ NativeBuffers.copyCStringToNativeBuffer(cstr, buffer);
+ buffer.setOwner(path);
+ return buffer;
+ }
+
+ /**
+ * char *getcwd(char *buf, size_t size);
+ */
+ static native byte[] getcwd();
+
+ /**
+ * int dup(int filedes)
+ */
+ static native int dup(int filedes) throws UnixException;
+
+ /**
+ * int open(const char* path, int oflag, mode_t mode)
+ */
+ static int open(UnixPath path, int flags, int mode) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ return open0(buffer.address(), flags, mode);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int open0(long pathAddress, int flags, int mode)
+ throws UnixException;
+
+ /**
+ * int openat(int dfd, const char* path, int oflag, mode_t mode)
+ */
+ static int openat(int dfd, byte[] path, int flags, int mode) throws UnixException {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(path);
+ try {
+ return openat0(dfd, buffer.address(), flags, mode);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int openat0(int dfd, long pathAddress, int flags, int mode)
+ throws UnixException;
+
+ /**
+ * close(int filedes)
+ */
+ static native void close(int fd);
+
+ /**
+ * FILE* fopen(const char *filename, const char* mode);
+ */
+ static long fopen(UnixPath filename, String mode) throws UnixException {
+ NativeBuffer pathBuffer = copyToNativeBuffer(filename);
+ NativeBuffer modeBuffer = NativeBuffers.asNativeBuffer(mode.getBytes());
+ try {
+ return fopen0(pathBuffer.address(), modeBuffer.address());
+ } finally {
+ modeBuffer.release();
+ pathBuffer.release();
+ }
+ }
+ private static native long fopen0(long pathAddress, long modeAddress)
+ throws UnixException;
+
+ /**
+ * fclose(FILE* stream)
+ */
+ static native void fclose(long stream) throws UnixException;
+
+ /**
+ * link(const char* existing, const char* new)
+ */
+ static void link(UnixPath existing, UnixPath newfile) throws UnixException {
+ NativeBuffer existingBuffer = copyToNativeBuffer(existing);
+ NativeBuffer newBuffer = copyToNativeBuffer(newfile);
+ try {
+ link0(existingBuffer.address(), newBuffer.address());
+ } finally {
+ newBuffer.release();
+ existingBuffer.release();
+ }
+ }
+ private static native void link0(long existingAddress, long newAddress)
+ throws UnixException;
+
+ /**
+ * unlink(const char* path)
+ */
+ static void unlink(UnixPath path) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ unlink0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void unlink0(long pathAddress) throws UnixException;
+
+ /**
+ * unlinkat(int dfd, const char* path, int flag)
+ */
+ static void unlinkat(int dfd, byte[] path, int flag) throws UnixException {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(path);
+ try {
+ unlinkat0(dfd, buffer.address(), flag);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void unlinkat0(int dfd, long pathAddress, int flag)
+ throws UnixException;
+
+ /**
+ * mknod(const char* path, mode_t mode, dev_t dev)
+ */
+ static void mknod(UnixPath path, int mode, long dev) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ mknod0(buffer.address(), mode, dev);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void mknod0(long pathAddress, int mode, long dev)
+ throws UnixException;
+
+ /**
+ * rename(const char* old, const char* new)
+ */
+ static void rename(UnixPath from, UnixPath to) throws UnixException {
+ NativeBuffer fromBuffer = copyToNativeBuffer(from);
+ NativeBuffer toBuffer = copyToNativeBuffer(to);
+ try {
+ rename0(fromBuffer.address(), toBuffer.address());
+ } finally {
+ toBuffer.release();
+ fromBuffer.release();
+ }
+ }
+ private static native void rename0(long fromAddress, long toAddress)
+ throws UnixException;
+
+ /**
+ * renameat(int fromfd, const char* old, int tofd, const char* new)
+ */
+ static void renameat(int fromfd, byte[] from, int tofd, byte[] to) throws UnixException {
+ NativeBuffer fromBuffer = NativeBuffers.asNativeBuffer(from);
+ NativeBuffer toBuffer = NativeBuffers.asNativeBuffer(to);
+ try {
+ renameat0(fromfd, fromBuffer.address(), tofd, toBuffer.address());
+ } finally {
+ toBuffer.release();
+ fromBuffer.release();
+ }
+ }
+ private static native void renameat0(int fromfd, long fromAddress, int tofd, long toAddress)
+ throws UnixException;
+
+ /**
+ * mkdir(const char* path, mode_t mode)
+ */
+ static void mkdir(UnixPath path, int mode) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ mkdir0(buffer.address(), mode);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void mkdir0(long pathAddress, int mode) throws UnixException;
+
+ /**
+ * rmdir(const char* path)
+ */
+ static void rmdir(UnixPath path) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ rmdir0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void rmdir0(long pathAddress) throws UnixException;
+
+ /**
+ * readlink(const char* path, char* buf, size_t bufsize)
+ *
+ * @return link target
+ */
+ static byte[] readlink(UnixPath path) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ return readlink0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native byte[] readlink0(long pathAddress) throws UnixException;
+
+ /**
+ * realpath(const char* path, char* resolved_name)
+ *
+ * @return resolved path
+ */
+ static byte[] realpath(UnixPath path) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ return realpath0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native byte[] realpath0(long pathAddress) throws UnixException;
+
+ /**
+ * symlink(const char* name1, const char* name2)
+ */
+ static void symlink(byte[] name1, UnixPath name2) throws UnixException {
+ NativeBuffer targetBuffer = NativeBuffers.asNativeBuffer(name1);
+ NativeBuffer linkBuffer = copyToNativeBuffer(name2);
+ try {
+ symlink0(targetBuffer.address(), linkBuffer.address());
+ } finally {
+ linkBuffer.release();
+ targetBuffer.release();
+ }
+ }
+ private static native void symlink0(long name1, long name2)
+ throws UnixException;
+
+ /**
+ * stat(const char* path, struct stat* buf)
+ */
+ static void stat(UnixPath path, UnixFileAttributes attrs) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ stat0(buffer.address(), attrs);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void stat0(long pathAddress, UnixFileAttributes attrs)
+ throws UnixException;
+
+ /**
+ * lstat(const char* path, struct stat* buf)
+ */
+ static void lstat(UnixPath path, UnixFileAttributes attrs) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ lstat0(buffer.address(), attrs);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void lstat0(long pathAddress, UnixFileAttributes attrs)
+ throws UnixException;
+
+ /**
+ * fstat(int filedes, struct stat* buf)
+ */
+ static native void fstat(int fd, UnixFileAttributes attrs) throws UnixException;
+
+ /**
+ * fstatat(int filedes,const char* path, struct stat* buf, int flag)
+ */
+ static void fstatat(int dfd, byte[] path, int flag, UnixFileAttributes attrs)
+ throws UnixException
+ {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(path);
+ try {
+ fstatat0(dfd, buffer.address(), flag, attrs);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void fstatat0(int dfd, long pathAddress, int flag,
+ UnixFileAttributes attrs) throws UnixException;
+
+ /**
+ * chown(const char* path, uid_t owner, gid_t group)
+ */
+ static void chown(UnixPath path, int uid, int gid) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ chown0(buffer.address(), uid, gid);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void chown0(long pathAddress, int uid, int gid)
+ throws UnixException;
+
+ /**
+ * lchown(const char* path, uid_t owner, gid_t group)
+ */
+ static void lchown(UnixPath path, int uid, int gid) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ lchown0(buffer.address(), uid, gid);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void lchown0(long pathAddress, int uid, int gid)
+ throws UnixException;
+
+ /**
+ * fchown(int filedes, uid_t owner, gid_t group)
+ */
+ static native void fchown(int fd, int uid, int gid) throws UnixException;
+
+ /**
+ * chmod(const char* path, mode_t mode)
+ */
+ static void chmod(UnixPath path, int mode) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ chmod0(buffer.address(), mode);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void chmod0(long pathAddress, int mode)
+ throws UnixException;
+
+ /**
+ * fchmod(int fildes, mode_t mode)
+ */
+ static native void fchmod(int fd, int mode) throws UnixException;
+
+ /**
+ * utimes(conar char* path, const struct timeval times[2])
+ */
+ static void utimes(UnixPath path, long times0, long times1)
+ throws UnixException
+ {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ utimes0(buffer.address(), times0, times1);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void utimes0(long pathAddress, long times0, long times1)
+ throws UnixException;
+
+ /**
+ * futimes(int fildes,, const struct timeval times[2])
+ */
+ static native void futimes(int fd, long times0, long times1) throws UnixException;
+
+ /**
+ * DIR *opendir(const char* dirname)
+ */
+ static long opendir(UnixPath path) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ return opendir0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native long opendir0(long pathAddress) throws UnixException;
+
+ /**
+ * DIR* fdopendir(int filedes)
+ */
+ static native long fdopendir(int dfd) throws UnixException;
+
+
+ /**
+ * closedir(DIR* dirp)
+ */
+ static native void closedir(long dir) throws UnixException;
+
+ /**
+ * struct dirent* readdir(DIR *dirp)
+ *
+ * @return dirent->d_name
+ */
+ static native byte[] readdir(long dir) throws UnixException;
+
+ /**
+ * size_t read(int fildes, void* buf, size_t nbyte)
+ */
+ static native int read(int fildes, long buf, int nbyte) throws UnixException;
+
+ /**
+ * size_t writeint fildes, void* buf, size_t nbyte)
+ */
+ static native int write(int fildes, long buf, int nbyte) throws UnixException;
+
+ /**
+ * access(const char* path, int amode);
+ */
+ static void access(UnixPath path, int amode) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ access0(buffer.address(), amode);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void access0(long pathAddress, int amode) throws UnixException;
+
+ /**
+ * struct passwd *getpwuid(uid_t uid);
+ *
+ * @return passwd->pw_name
+ */
+ static native byte[] getpwuid(int uid) throws UnixException;
+
+ /**
+ * struct group *getgrgid(gid_t gid);
+ *
+ * @return group->gr_name
+ */
+ static native byte[] getgrgid(int gid) throws UnixException;
+
+ /**
+ * struct passwd *getpwnam(const char *name);
+ *
+ * @return passwd->pw_uid
+ */
+ static int getpwnam(String name) throws UnixException {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(name.getBytes());
+ try {
+ return getpwnam0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int getpwnam0(long nameAddress) throws UnixException;
+
+ /**
+ * struct group *getgrnam(const char *name);
+ *
+ * @return group->gr_name
+ */
+ static int getgrnam(String name) throws UnixException {
+ NativeBuffer buffer = NativeBuffers.asNativeBuffer(name.getBytes());
+ try {
+ return getgrnam0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int getgrnam0(long nameAddress) throws UnixException;
+
+ /**
+ * int getextmntent(FILE *fp, struct extmnttab *mp, int len);
+ */
+ static native int getextmntent(long fp, UnixMountEntry entry) throws UnixException;
+
+ /**
+ * statvfs(const char* path, struct statvfs *buf)
+ */
+ static void statvfs(UnixPath path, UnixFileStoreAttributes attrs)
+ throws UnixException
+ {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ statvfs0(buffer.address(), attrs);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void statvfs0(long pathAddress, UnixFileStoreAttributes attrs)
+ throws UnixException;
+
+ /**
+ * long int pathconf(const char *path, int name);
+ */
+ static long pathconf(UnixPath path, int name) throws UnixException {
+ NativeBuffer buffer = copyToNativeBuffer(path);
+ try {
+ return pathconf0(buffer.address(), name);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native long pathconf0(long pathAddress, int name)
+ throws UnixException;
+
+ /**
+ * long fpathconf(int fildes, int name);
+ */
+ static native long fpathconf(int filedes, int name) throws UnixException;
+
+ /**
+ * char* strerror(int errnum)
+ */
+ static native byte[] strerror(int errnum);
+
+ // initialize field IDs
+ private static native void initIDs();
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }});
+ initIDs();
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixPath.java b/src/solaris/classes/sun/nio/fs/UnixPath.java
new file mode 100644
index 000000000..c1330f005
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixPath.java
@@ -0,0 +1,1228 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.AbstractPath;
+import java.nio.charset.*;
+import java.nio.channels.*;
+import java.security.AccessController;
+import java.io.*;
+import java.net.URI;
+import java.util.*;
+import java.lang.ref.SoftReference;
+import sun.security.util.SecurityConstants;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+/**
+ * Solaris/Linux implementation of java.nio.file.Path
+ */
+
+class UnixPath
+ extends AbstractPath
+{
+ private static ThreadLocal<SoftReference<CharsetEncoder>> encoder =
+ new ThreadLocal<SoftReference<CharsetEncoder>>();
+
+ // FIXME - eliminate this reference to reduce space
+ private final UnixFileSystem fs;
+
+ // internal representation
+ private final byte[] path;
+
+ // String representation (created lazily)
+ private volatile String stringValue;
+
+ // cached hashcode (created lazily, no need to be volatile)
+ private int hash;
+
+ // array of offsets of elements in path (created lazily)
+ private volatile int[] offsets;
+
+ // file permissions (created lazily)
+ private volatile FilePermission[] perms;
+
+ UnixPath(UnixFileSystem fs, byte[] path) {
+ this.fs = fs;
+ this.path = path;
+ }
+
+ UnixPath(UnixFileSystem fs, String input) {
+ // removes redundant slashes and checks for invalid characters
+ this(fs, encode(normalizeAndCheck(input)));
+ }
+
+ // package-private
+ // removes redundant slashes and check input for invalid characters
+ static String normalizeAndCheck(String input) {
+ int n = input.length();
+ if (n == 0)
+ throw new InvalidPathException(input, "Path is empty");
+ char prevChar = 0;
+ for (int i=0; i < n; i++) {
+ char c = input.charAt(i);
+ if (c == '\u0000')
+ throw new InvalidPathException(input, "Nul character not allowed");
+ if ((c == '/') && (prevChar == '/'))
+ return normalize(input, n, i - 1);
+ prevChar = c;
+ }
+ if (prevChar == '/')
+ return normalize(input, n, n - 1);
+ return input;
+ }
+
+ private static String normalize(String input, int len, int off) {
+ if (len == 0)
+ return input;
+ int n = len;
+ while ((n > 0) && (input.charAt(n - 1) == '/')) n--;
+ if (n == 0)
+ return "/";
+ StringBuilder sb = new StringBuilder(input.length());
+ if (off > 0)
+ sb.append(input.substring(0, off));
+ char prevChar = 0;
+ for (int i=off; i < n; i++) {
+ char c = input.charAt(i);
+ if ((c == '/') && (prevChar == '/'))
+ continue;
+ sb.append(c);
+ prevChar = c;
+ }
+ return sb.toString();
+ }
+
+ // encodes the given path-string into a sequence of bytes
+ private static byte[] encode(String input) {
+ SoftReference<CharsetEncoder> ref = encoder.get();
+ CharsetEncoder ce = (ref != null) ? ref.get() : null;
+ if (ce == null) {
+ ce = Charset.defaultCharset().newEncoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ encoder.set(new SoftReference<CharsetEncoder>(ce));
+ }
+
+ char[] ca = input.toCharArray();
+
+ // size output buffer for worse-case size
+ byte[] ba = new byte[(int)(ca.length * (double)ce.maxBytesPerChar())];
+
+ // encode
+ ByteBuffer bb = ByteBuffer.wrap(ba);
+ CharBuffer cb = CharBuffer.wrap(ca);
+ ce.reset();
+ CoderResult cr = ce.encode(cb, bb, true);
+ boolean error;
+ if (!cr.isUnderflow()) {
+ error = true;
+ } else {
+ cr = ce.flush(bb);
+ error = !cr.isUnderflow();
+ }
+ if (error) {
+ throw new InvalidPathException(input,
+ "Malformed input or input contains unmappable chacraters");
+ }
+
+ // trim result to actual length if required
+ int len = bb.position();
+ if (len != ba.length)
+ ba = Arrays.copyOf(ba, len);
+
+ return ba;
+ }
+
+ // package-private
+ byte[] asByteArray() {
+ return path;
+ }
+
+ // use this path when making system/library calls
+ byte[] getByteArrayForSysCalls() {
+ // resolve against default directory if required (chdir allowed or
+ // file system default directory is not working directory)
+ if (getFileSystem().needToResolveAgainstDefaultDirectory()) {
+ return resolve(getFileSystem().defaultDirectory(), path);
+ } else {
+ return path;
+ }
+ }
+
+ // use this message when throwing exceptions
+ String getPathForExecptionMessage() {
+ return toString();
+ }
+
+ // use this path for permission checks
+ String getPathForPermissionCheck() {
+ if (getFileSystem().needToResolveAgainstDefaultDirectory()) {
+ return new String(getByteArrayForSysCalls());
+ } else {
+ return toString();
+ }
+ }
+
+ // Checks that the given file is a UnixPath
+ private UnixPath checkPath(FileRef obj) {
+ if (obj == null)
+ throw new NullPointerException();
+ if (!(obj instanceof UnixPath))
+ throw new ProviderMismatchException();
+ return (UnixPath)obj;
+ }
+
+ // create offset list if not already created
+ private void initOffsets() {
+ if (offsets == null) {
+ int count, index;
+
+ // count names
+ count = 0;
+ index = 0;
+ while (index < path.length) {
+ byte c = path[index++];
+ if (c != '/') {
+ count++;
+ while (index < path.length && path[index] != '/')
+ index++;
+ }
+ }
+
+ // populate offsets
+ int[] result = new int[count];
+ count = 0;
+ index = 0;
+ while (index < path.length) {
+ byte c = path[index];
+ if (c == '/') {
+ index++;
+ } else {
+ result[count++] = index++;
+ while (index < path.length && path[index] != '/')
+ index++;
+ }
+ }
+ synchronized (this) {
+ if (offsets == null)
+ offsets = result;
+ }
+ }
+ }
+
+ @Override
+ public UnixFileSystem getFileSystem() {
+ return fs;
+ }
+
+ @Override
+ public UnixPath getRoot() {
+ if (path[0] == '/') {
+ return getFileSystem().rootDirectory();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public UnixPath getName() {
+ initOffsets();
+
+ int count = offsets.length;
+ if (count == 0)
+ return null; // no elements so no name
+
+ if (count == 1 && path[0] != '/')
+ return this;
+
+ int lastOffset = offsets[count-1];
+ int len = path.length - lastOffset;
+ byte[] result = new byte[len];
+ System.arraycopy(path, lastOffset, result, 0, len);
+ return new UnixPath(getFileSystem(), result);
+ }
+
+ @Override
+ public UnixPath getParent() {
+ initOffsets();
+
+ int count = offsets.length;
+ if (count == 0) {
+ // no elements so no parent
+ return null;
+ }
+ int len = offsets[count-1] - 1;
+ if (len <= 0) {
+ // parent is root only (may be null)
+ return getRoot();
+ }
+ byte[] result = new byte[len];
+ System.arraycopy(path, 0, result, 0, len);
+ return new UnixPath(getFileSystem(), result);
+ }
+
+ @Override
+ public int getNameCount() {
+ initOffsets();
+ return offsets.length;
+ }
+
+ @Override
+ public UnixPath getName(int index) {
+ initOffsets();
+ if (index < 0)
+ throw new IllegalArgumentException();
+ if (index >= offsets.length)
+ throw new IllegalArgumentException();
+
+ int begin = offsets[index];
+ int len;
+ if (index == (offsets.length-1)) {
+ len = path.length - begin;
+ } else {
+ len = offsets[index+1] - begin - 1;
+ }
+
+ // construct result
+ byte[] result = new byte[len];
+ System.arraycopy(path, begin, result, 0, len);
+ return new UnixPath(getFileSystem(), result);
+ }
+
+ @Override
+ public UnixPath subpath(int beginIndex, int endIndex) {
+ initOffsets();
+
+ if (beginIndex < 0)
+ throw new IllegalArgumentException();
+ if (beginIndex >= offsets.length)
+ throw new IllegalArgumentException();
+ if (endIndex > offsets.length)
+ throw new IllegalArgumentException();
+ if (beginIndex >= endIndex) {
+ throw new IllegalArgumentException();
+ }
+
+ // starting offset and length
+ int begin = offsets[beginIndex];
+ int len;
+ if (endIndex == offsets.length) {
+ len = path.length - begin;
+ } else {
+ len = offsets[endIndex] - begin - 1;
+ }
+
+ // construct result
+ byte[] result = new byte[len];
+ System.arraycopy(path, begin, result, 0, len);
+ return new UnixPath(getFileSystem(), result);
+ }
+
+ @Override
+ public boolean isAbsolute() {
+ return (path[0] == '/');
+ }
+
+ // Resolve child against given base
+ private static byte[] resolve(byte[] base, byte[] child) {
+ if (child[0] == '/')
+ return child;
+ byte[] result;
+ if (base.length == 1 && base[0] == '/') {
+ result = new byte[child.length + 1];
+ result[0] = '/';
+ System.arraycopy(child, 0, result, 1, child.length);
+ } else {
+ result = new byte[base.length + 1 + child.length];
+ System.arraycopy(base, 0, result, 0, base.length);
+ result[base.length] = '/';
+ System.arraycopy(child, 0, result, base.length+1, child.length);
+ }
+ return result;
+ }
+
+ @Override
+ public UnixPath resolve(Path obj) {
+ if (obj == null)
+ return this;
+ byte[] other = checkPath(obj).path;
+ if (other[0] == '/')
+ return ((UnixPath)obj);
+ byte[] result = resolve(path, other);
+ return new UnixPath(getFileSystem(), result);
+ }
+
+ @Override
+ public UnixPath resolve(String other) {
+ return resolve(new UnixPath(getFileSystem(), other));
+ }
+
+ UnixPath resolve(byte[] other) {
+ return resolve(new UnixPath(getFileSystem(), other));
+ }
+
+ @Override
+ public UnixPath relativize(Path obj) {
+ UnixPath other = checkPath(obj);
+ if (other.equals(this))
+ return null;
+
+ // can only relativize paths of the same type
+ if (this.isAbsolute() != other.isAbsolute())
+ throw new IllegalArgumentException("'other' is different type of Path");
+
+ int bn = this.getNameCount();
+ int cn = other.getNameCount();
+
+ // skip matching names
+ int n = (bn > cn) ? cn : bn;
+ int i = 0;
+ while (i < n) {
+ if (!this.getName(i).equals(other.getName(i)))
+ break;
+ i++;
+ }
+
+ int dotdots = bn - i;
+ if (i < cn) {
+ // remaining name components in other
+ UnixPath remainder = other.subpath(i, cn);
+ if (dotdots == 0)
+ return remainder;
+
+ // result is a "../" for each remaining name in base
+ // followed by the remaining names in other
+ byte[] result = new byte[dotdots*3 + remainder.path.length];
+ int pos = 0;
+ while (dotdots > 0) {
+ result[pos++] = (byte)'.';
+ result[pos++] = (byte)'.';
+ result[pos++] = (byte)'/';
+ dotdots--;
+ }
+ System.arraycopy(remainder.path, 0, result, pos, remainder.path.length);
+ return new UnixPath(getFileSystem(), result);
+ } else {
+ // no remaining names in other so result is simply a sequence of ".."
+ byte[] result = new byte[dotdots*3 - 1];
+ int pos = 0;
+ while (dotdots > 0) {
+ result[pos++] = (byte)'.';
+ result[pos++] = (byte)'.';
+ // no tailing slash at the end
+ if (dotdots > 1)
+ result[pos++] = (byte)'/';
+ dotdots--;
+ }
+ return new UnixPath(getFileSystem(), result);
+ }
+ }
+
+ @Override
+ public Path normalize() {
+ final int count = getNameCount();
+ if (count == 0)
+ return this;
+
+ boolean[] ignore = new boolean[count]; // true => ignore name
+ int[] size = new int[count]; // length of name
+ int remaining = count; // number of names remaining
+ boolean hasDotDot = false; // has at least one ..
+ boolean isAbsolute = path[0] == '/';
+
+ // first pass:
+ // 1. compute length of names
+ // 2. mark all occurences of "." to ignore
+ // 3. and look for any occurences of ".."
+ for (int i=0; i<count; i++) {
+ int begin = offsets[i];
+ int len;
+ if (i == (offsets.length-1)) {
+ len = path.length - begin;
+ } else {
+ len = offsets[i+1] - begin - 1;
+ }
+ size[i] = len;
+
+ if (path[begin] == '.') {
+ if (len == 1) {
+ ignore[i] = true; // ignore "."
+ remaining--;
+ }
+ else {
+ if (path[begin+1] == '.') // ".." found
+ hasDotDot = true;
+ }
+ }
+ }
+
+ // multiple passes to eliminate all occurences of name/..
+ if (hasDotDot) {
+ int prevRemaining;
+ do {
+ prevRemaining = remaining;
+ int prevName = -1;
+ for (int i=0; i<count; i++) {
+ if (ignore[i])
+ continue;
+
+ // not a ".."
+ if (size[i] != 2) {
+ prevName = i;
+ continue;
+ }
+
+ int begin = offsets[i];
+ if (path[begin] != '.' || path[begin+1] != '.') {
+ prevName = i;
+ continue;
+ }
+
+ // ".." found
+ if (prevName >= 0) {
+ // name/<ignored>/.. found so mark name and ".." to be
+ // ignored
+ ignore[prevName] = true;
+ ignore[i] = true;
+ remaining = remaining - 2;
+ prevName = -1;
+ } else {
+ // Case: /<ignored>/.. so mark ".." as ignored
+ if (isAbsolute) {
+ boolean hasPrevious = false;
+ for (int j=0; j<i; j++) {
+ if (!ignore[j]) {
+ hasPrevious = true;
+ break;
+ }
+ }
+ if (!hasPrevious) {
+ // all proceeding names are ignored
+ ignore[i] = true;
+ remaining--;
+ }
+ }
+ }
+ }
+ } while (prevRemaining > remaining);
+ }
+
+ // no redundant names
+ if (remaining == count)
+ return this;
+
+ // corner case - all names removed
+ if (remaining == 0) {
+ return isAbsolute ? getFileSystem().rootDirectory() : null;
+ }
+
+ // compute length of result
+ int len = remaining - 1;
+ if (isAbsolute)
+ len++;
+
+ for (int i=0; i<count; i++) {
+ if (!ignore[i])
+ len += size[i];
+ }
+ byte[] result = new byte[len];
+
+ // copy names into result
+ int pos = 0;
+ if (isAbsolute)
+ result[pos++] = '/';
+ for (int i=0; i<count; i++) {
+ if (!ignore[i]) {
+ System.arraycopy(path, offsets[i], result, pos, size[i]);
+ pos += size[i];
+ if (--remaining > 0) {
+ result[pos++] = '/';
+ }
+ }
+ }
+ return new UnixPath(getFileSystem(), result);
+ }
+
+ @Override
+ public boolean startsWith(Path other) {
+ UnixPath that = checkPath(other);
+
+ // other path is longer
+ if (that.path.length > path.length)
+ return false;
+
+ int thisOffsetCount = getNameCount();
+ int thatOffsetCount = that.getNameCount();
+
+ // other path has no name elements
+ if (thatOffsetCount == 0 && this.isAbsolute())
+ return true;
+
+ // given path has more elements that this path
+ if (thatOffsetCount > thisOffsetCount)
+ return false;
+
+ // same number of elements so must be exact match
+ if ((thatOffsetCount == thisOffsetCount) &&
+ (path.length != that.path.length)) {
+ return false;
+ }
+
+ // check offsets of elements match
+ for (int i=0; i<thatOffsetCount; i++) {
+ Integer o1 = offsets[i];
+ Integer o2 = that.offsets[i];
+ if (!o1.equals(o2))
+ return false;
+ }
+
+ // offsets match so need to compare bytes
+ int i=0;
+ while (i < that.path.length) {
+ if (this.path[i] != that.path[i])
+ return false;
+ i++;
+ }
+
+ // final check that match is on name boundary
+ if (i < path.length && this.path[i] != '/')
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public boolean endsWith(Path other) {
+ UnixPath that = checkPath(other);
+
+ // other path is longer
+ if (that.path.length > path.length)
+ return false;
+
+ // other path is absolute so this path must be absolute
+ if (that.isAbsolute() && !this.isAbsolute())
+ return false;
+
+ int thisOffsetCount = getNameCount();
+ int thatOffsetCount = that.getNameCount();
+
+ // given path has more elements that this path
+ if (thatOffsetCount > thisOffsetCount) {
+ return false;
+ } else {
+ // same number of elements
+ if (thatOffsetCount == thisOffsetCount) {
+ if (thisOffsetCount == 0)
+ return true;
+ int expectedLen = path.length;
+ if (this.isAbsolute() && !that.isAbsolute())
+ expectedLen--;
+ if (that.path.length != expectedLen)
+ return false;
+ } else {
+ // this path has more elements so given path must be relative
+ if (that.isAbsolute())
+ return false;
+ }
+ }
+
+ // compare bytes
+ int thisPos = offsets[thisOffsetCount - thatOffsetCount];
+ int thatPos = that.offsets[0];
+ while (thatPos < that.path.length) {
+ if (this.path[thisPos++] != that.path[thatPos++])
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int compareTo(Path other) {
+ int len1 = path.length;
+ int len2 = ((UnixPath) other).path.length;
+
+ int n = Math.min(len1, len2);
+ byte v1[] = path;
+ byte v2[] = ((UnixPath) other).path;
+
+ int k = 0;
+ while (k < n) {
+ int c1 = v1[k] & 0xff;
+ int c2 = v2[k] & 0xff;
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ k++;
+ }
+ return len1 - len2;
+ }
+
+ @Override
+ public boolean equals(Object ob) {
+ if ((ob != null) && (ob instanceof UnixPath)) {
+ return compareTo((Path)ob) == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ // OK if two or more threads compute hash
+ int h = hash;
+ if (h == 0) {
+ for (int i = 0; i< path.length; i++) {
+ h = 31*h + (path[i] & 0xff);
+ }
+ hash = h;
+ }
+ return h;
+ }
+
+ @Override
+ public String toString() {
+ // OK if two or more threads create a String
+ if (stringValue == null)
+ stringValue = new String(path); // platform encoding
+ return stringValue;
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ initOffsets();
+ return new Iterator<Path>() {
+ int i = 0;
+ @Override
+ public boolean hasNext() {
+ return (i < offsets.length);
+ }
+ @Override
+ public Path next() {
+ if (i < offsets.length) {
+ Path result = getName(i);
+ i++;
+ return result;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ // -- file operations --
+
+ // package-private
+ int openForAttributeAccess(boolean followLinks) throws IOException {
+ int flags = O_RDONLY;
+ if (!followLinks)
+ flags |= O_NOFOLLOW;
+ try {
+ return open(this, flags, 0);
+ } catch (UnixException x) {
+ // HACK: EINVAL instead of ELOOP on Solaris 10 prior to u4 (see 6460380)
+ if (getFileSystem().isSolaris() && x.errno() == EINVAL)
+ x.setError(ELOOP);
+
+ if (x.errno() == ELOOP)
+ throw new FileSystemException(getPathForExecptionMessage(), null,
+ x.getMessage() + " or unable to access attributes of symbolic link");
+
+ x.rethrowAsIOException(this);
+ return -1; // keep compile happy
+ }
+ }
+
+ // create file permissions used for read and write checks
+ private void checkReadOrWrite(boolean checkRead) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null)
+ return;
+ if (perms == null) {
+ synchronized (this) {
+ if (perms == null) {
+ FilePermission[] p = new FilePermission[2];
+ String pathForPermCheck = getPathForPermissionCheck();
+ p[0] = new FilePermission(pathForPermCheck,
+ SecurityConstants.FILE_READ_ACTION);
+ p[1] = new FilePermission(pathForPermCheck,
+ SecurityConstants.FILE_WRITE_ACTION);
+ perms = p;
+ }
+ }
+ }
+ if (checkRead) {
+ sm.checkPermission(perms[0]);
+ } else {
+ sm.checkPermission(perms[1]);
+ }
+ }
+
+ void checkRead() {
+ checkReadOrWrite(true);
+ }
+
+ void checkWrite() {
+ checkReadOrWrite(false);
+ }
+
+ void checkDelete() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // permission not cached
+ sm.checkDelete(getPathForPermissionCheck());
+ }
+ }
+
+ @Override
+ public FileStore getFileStore()
+ throws IOException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
+ checkRead();
+ }
+ return getFileSystem().getFileStore(this);
+ }
+
+ @Override
+ public void checkAccess(AccessMode... modes) throws IOException {
+ boolean e = false;
+ boolean r = false;
+ boolean w = false;
+ boolean x = false;
+
+ if (modes.length == 0) {
+ e = true;
+ } else {
+ for (AccessMode mode: modes) {
+ switch (mode) {
+ case READ : r = true; break;
+ case WRITE : w = true; break;
+ case EXECUTE : x = true; break;
+ default: throw new AssertionError("Should not get here");
+ }
+ }
+ }
+
+ int mode = 0;
+ if (e || r) {
+ checkRead();
+ mode |= (r) ? R_OK : F_OK;
+ }
+ if (w) {
+ checkWrite();
+ mode |= W_OK;
+ }
+ if (x) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // not cached
+ sm.checkExec(getPathForPermissionCheck());
+ }
+ mode |= X_OK;
+ }
+ try {
+ access(this, mode);
+ } catch (UnixException exc) {
+ exc.rethrowAsIOException(this);
+ }
+ }
+
+ @Override
+ public void delete(boolean failIfNotExists) throws IOException {
+ checkDelete();
+
+ // need file attributes to know if file is directory
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(this, false);
+ if (attrs.isDirectory()) {
+ rmdir(this);
+ } else {
+ unlink(this);
+ }
+ } catch (UnixException x) {
+ // no-op if file does not exist
+ if (!failIfNotExists && x.errno() == ENOENT)
+ return;
+
+ // DirectoryNotEmptyException if not empty
+ if (attrs != null && attrs.isDirectory() &&
+ (x.errno() == EEXIST || x.errno() == ENOTEMPTY))
+ throw new DirectoryNotEmptyException(getPathForExecptionMessage());
+
+ x.rethrowAsIOException(this);
+ }
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter)
+ throws IOException
+ {
+ if (filter == null)
+ throw new NullPointerException();
+ checkRead();
+
+ // can't return SecureDirectoryStream on older kernels.
+ if (!getFileSystem().supportsSecureDirectoryStreams()) {
+ try {
+ long ptr = opendir(this);
+ return new UnixDirectoryStream(this, ptr, filter);
+ } catch (UnixException x) {
+ if (x.errno() == UnixConstants.ENOTDIR)
+ throw new NotDirectoryException(getPathForExecptionMessage());
+ x.rethrowAsIOException(this);
+ }
+ }
+
+ // open directory and dup file descriptor for use by
+ // opendir/readdir/closedir
+ int dfd1 = -1;
+ int dfd2 = -1;
+ long dp = 0L;
+ try {
+ dfd1 = open(this, O_RDONLY, 0);
+ dfd2 = dup(dfd1);
+ dp = fdopendir(dfd1);
+ } catch (UnixException x) {
+ if (dfd1 != -1)
+ close(dfd1);
+ if (dfd2 != -1)
+ close(dfd2);
+ if (x.errno() == UnixConstants.ENOTDIR)
+ throw new NotDirectoryException(getPathForExecptionMessage());
+ x.rethrowAsIOException(this);
+ }
+ return new UnixSecureDirectoryStream(this, dp, dfd2, filter);
+ }
+
+ // invoked by AbstractPath#copyTo
+ @Override
+ public void implCopyTo(Path obj, CopyOption... options)
+ throws IOException
+ {
+ UnixPath target = (UnixPath)obj;
+ UnixCopyFile.copy(this, target, options);
+ }
+
+ @Override
+ public void implMoveTo(Path obj, CopyOption... options)
+ throws IOException
+ {
+ UnixPath target = (UnixPath)obj;
+ UnixCopyFile.move(this, target, options);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileAttributeView> V
+ getFileAttributeView(Class<V> type, LinkOption... options)
+ {
+ FileAttributeView view = getFileSystem()
+ .newFileAttributeView(type, this, options);
+ if (view == null)
+ return null;
+ return (V) view;
+ }
+
+ @Override
+ public FileAttributeView getFileAttributeView(String name, LinkOption... options) {
+ return getFileSystem().newFileAttributeView(name, this, options);
+ }
+
+ @Override
+ public Path createDirectory(FileAttribute<?>... attrs)
+ throws IOException
+ {
+ checkWrite();
+
+ int mode = UnixFileModeAttribute
+ .toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs);
+ try {
+ mkdir(this, mode);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ }
+ return this;
+ }
+
+ @Override
+ public InputStream newInputStream()throws IOException {
+ try {
+ Set<OpenOption> options = Collections.emptySet();
+ FileChannel fc = UnixChannelFactory.newFileChannel(this, options, 0);
+ return Channels.newInputStream(fc);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ }
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ int mode = UnixFileModeAttribute
+ .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
+ try {
+ return UnixChannelFactory.newFileChannel(this, options, mode);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ }
+ }
+
+ @Override
+ public OutputStream newOutputStream(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ // need to copy options to add WRITE
+ Set<OpenOption> opts = new HashSet<OpenOption>(options);
+ if (opts.contains(StandardOpenOption.READ))
+ throw new IllegalArgumentException("READ not allowed");
+ opts.add(StandardOpenOption.WRITE);
+
+ int mode = UnixFileModeAttribute
+ .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
+ try {
+ FileChannel fc = UnixChannelFactory.newFileChannel(this, opts, mode);
+ return Channels.newOutputStream(fc);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ }
+ }
+
+ @Override
+ public boolean isSameFile(FileRef obj) throws IOException {
+ if (this.equals(obj))
+ return true;
+ if (!(obj instanceof UnixPath)) // includes null check
+ return false;
+ UnixPath other = (UnixPath)obj;
+
+ // check security manager access to both files
+ this.checkRead();
+ other.checkRead();
+
+ UnixFileAttributes thisAttrs;
+ UnixFileAttributes otherAttrs;
+ try {
+ thisAttrs = UnixFileAttributes.get(this, true);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ return false; // keep compiler happy
+ }
+ try {
+ otherAttrs = UnixFileAttributes.get(other, true);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(other);
+ return false; // keep compiler happy
+ }
+ return thisAttrs.isSameFile(otherAttrs);
+ }
+
+ @Override
+ public Path createSymbolicLink(Path obj, FileAttribute<?>... attrs)
+ throws IOException
+ {
+ UnixPath target = checkPath(obj);
+
+ // no attributes supported when creating links
+ if (attrs.length > 0) {
+ UnixFileModeAttribute.toUnixMode(0, attrs); // may throw NPE or UOE
+ throw new UnsupportedOperationException("Initial file attributes" +
+ "not supported when creating symbolic link");
+ }
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new LinkPermission("symbolic"));
+ checkWrite();
+ }
+
+ // create link
+ try {
+ symlink(target.asByteArray(), this);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ }
+
+ return this;
+ }
+
+ @Override
+ public Path createLink(Path obj) throws IOException {
+ UnixPath existing = checkPath(obj);
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new LinkPermission("hard"));
+ this.checkWrite();
+ existing.checkWrite();
+ }
+ try {
+ link(existing, this);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this, existing);
+ }
+ return this;
+ }
+
+ @Override
+ public Path readSymbolicLink() throws IOException {
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ FilePermission perm = new FilePermission(getPathForPermissionCheck(),
+ SecurityConstants.FILE_READLINK_ACTION);
+ AccessController.checkPermission(perm);
+ }
+ try {
+ byte[] target = readlink(this);
+ return new UnixPath(getFileSystem(), target);
+ } catch (UnixException x) {
+ if (x.errno() == UnixConstants.EINVAL)
+ throw new NotLinkException(getPathForExecptionMessage());
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ }
+ }
+
+ @Override
+ public UnixPath toAbsolutePath() {
+ if (isAbsolute()) {
+ return this;
+ }
+ // The path is relative so need to resolve against default directory,
+ // taking care not to reveal the user.dir
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPropertyAccess("user.dir");
+ }
+ return new UnixPath(getFileSystem(),
+ resolve(getFileSystem().defaultDirectory(), path));
+ }
+
+ @Override
+ public UnixPath toRealPath(boolean resolveLinks) throws IOException {
+ checkRead();
+
+ UnixPath absolute = toAbsolutePath();
+
+ // if resolveLinks is true then use realpath
+ if (resolveLinks) {
+ try {
+ byte[] rp = realpath(absolute);
+ return new UnixPath(getFileSystem(), rp);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(this);
+ }
+ }
+
+ // if resolveLinks is false then eliminate "." and also ".."
+ // where the previous element is not a link.
+ UnixPath root = getFileSystem().rootDirectory();
+ UnixPath result = root;
+ for (int i=0; i<absolute.getNameCount(); i++) {
+ UnixPath element = absolute.getName(i);
+
+ // eliminate "."
+ if ((element.asByteArray().length == 1) && (element.asByteArray()[0] == '.'))
+ continue;
+
+ // cannot eliminate ".." if previous element is a link
+ if ((element.asByteArray().length == 2) && (element.asByteArray()[0] == '.') &&
+ (element.asByteArray()[1] == '.'))
+ {
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(result, false);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(result);
+ }
+ if (!attrs.isSymbolicLink()) {
+ result = result.getParent();
+ if (result == null) {
+ result = root;
+ }
+ continue;
+ }
+ }
+ result = result.resolve(element);
+ }
+
+ // finally check that file exists
+ try {
+ UnixFileAttributes.get(result, true);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(result);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean isHidden() {
+ checkRead();
+ UnixPath name = getName();
+ if (name == null)
+ return false;
+ return (name.asByteArray()[0] == '.');
+ }
+
+ @Override
+ public URI toUri() {
+ return UnixUriUtils.toUri(this);
+ }
+
+ @Override
+ public WatchKey register(WatchService watcher,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ if (watcher == null)
+ throw new NullPointerException();
+ if (!(watcher instanceof AbstractWatchService))
+ throw new ProviderMismatchException();
+ checkRead();
+ return ((AbstractWatchService)watcher).register(this, events, modifiers);
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java b/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java
new file mode 100644
index 000000000..e58948787
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.channels.SeekableByteChannel;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+
+import static sun.nio.fs.UnixNativeDispatcher.*;
+import static sun.nio.fs.UnixConstants.*;
+
+/**
+ * Unix implementation of SecureDirectoryStream.
+ */
+
+class UnixSecureDirectoryStream
+ extends SecureDirectoryStream
+{
+ private final UnixDirectoryStream ds;
+ private final int dfd;
+
+ UnixSecureDirectoryStream(UnixPath dir,
+ long dp,
+ int dfd,
+ DirectoryStream.Filter<? super Path> filter)
+ {
+ this.ds = new UnixDirectoryStream(dir, dp, filter);
+ this.dfd = dfd;
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ ds.writeLock().lock();
+ try {
+ if (ds.closeImpl()) {
+ UnixNativeDispatcher.close(dfd);
+ }
+ } finally {
+ ds.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ return ds.iterator(this);
+ }
+
+ private UnixPath getName(Path obj) {
+ if (obj == null)
+ throw new NullPointerException();
+ if (!(obj instanceof UnixPath))
+ throw new ProviderMismatchException();
+ return (UnixPath)obj;
+ }
+
+ /**
+ * Opens sub-directory in this directory
+ */
+ @Override
+ public SecureDirectoryStream newDirectoryStream(Path obj,
+ boolean followLinks,
+ DirectoryStream.Filter<? super Path> filter)
+ throws IOException
+ {
+ UnixPath file = getName(obj);
+ UnixPath child = ds.directory().resolve(file);
+
+ // permission check using name resolved against original path of directory
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ child.checkRead();
+ }
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ // open directory and create new secure directory stream
+ int newdfd1 = -1;
+ int newdfd2 = -1;
+ long ptr = 0L;
+ try {
+ int flags = O_RDONLY;
+ if (!followLinks)
+ flags |= O_NOFOLLOW;
+ newdfd1 = openat(dfd, file.asByteArray(), flags , 0);
+ newdfd2 = dup(newdfd1);
+ ptr = fdopendir(newdfd1);
+ } catch (UnixException x) {
+ if (newdfd1 != -1)
+ UnixNativeDispatcher.close(newdfd1);
+ if (newdfd2 != -1)
+ UnixNativeDispatcher.close(newdfd2);
+ if (x.errno() == UnixConstants.ENOTDIR)
+ throw new NotDirectoryException(file.toString());
+ x.rethrowAsIOException(file);
+ }
+ return new UnixSecureDirectoryStream(child, ptr, newdfd2, filter);
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ /**
+ * Opens file in this directory
+ */
+ @Override
+ public SeekableByteChannel newByteChannel(Path obj,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ UnixPath file = getName(obj);
+
+ int mode = UnixFileModeAttribute
+ .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs);
+
+ // path for permission check
+ String pathToCheck = ds.directory().resolve(file).getPathForPermissionCheck();
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+ try {
+ return UnixChannelFactory.newFileChannel(dfd, file, pathToCheck, options, mode);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ /**
+ * Deletes file/directory in this directory. Works in a race-free manner
+ * when invoked with flags.
+ */
+ void implDelete(Path obj, boolean haveFlags, int flags)
+ throws IOException
+ {
+ UnixPath file = getName(obj);
+
+ // permission check using name resolved against original path of directory
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ ds.directory().resolve(file).checkDelete();
+ }
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ if (!haveFlags) {
+ // need file attribute to know if file is directory. This creates
+ // a race in that the file may be replaced by a directory or a
+ // directory replaced by a file between the time we query the
+ // file type and unlink it.
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(dfd, file, false);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ flags = (attrs.isDirectory()) ? AT_REMOVEDIR : 0;
+ }
+
+ try {
+ unlinkat(dfd, file.asByteArray(), flags);
+ } catch (UnixException x) {
+ if ((flags & AT_REMOVEDIR) != 0) {
+ if (x.errno() == EEXIST || x.errno() == ENOTEMPTY) {
+ throw new DirectoryNotEmptyException(null);
+ }
+ }
+ x.rethrowAsIOException(file);
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ @Override
+ public void deleteFile(Path file) throws IOException {
+ implDelete(file, true, 0);
+ }
+
+ @Override
+ public void deleteDirectory(Path dir) throws IOException {
+ implDelete(dir, true, AT_REMOVEDIR);
+ }
+
+ /**
+ * Rename/move file in this directory to another (open) directory
+ */
+ @Override
+ public void move(Path fromObj, SecureDirectoryStream dir, Path toObj)
+ throws IOException
+ {
+ UnixPath from = getName(fromObj);
+ UnixPath to = getName(toObj);
+ if (dir == null)
+ throw new NullPointerException();
+ if (!(dir instanceof UnixSecureDirectoryStream))
+ throw new ProviderMismatchException();
+ UnixSecureDirectoryStream that = (UnixSecureDirectoryStream)dir;
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ this.ds.directory().resolve(from).checkWrite();
+ that.ds.directory().resolve(to).checkWrite();
+ }
+
+ // lock ordering doesn't matter
+ this.ds.readLock().lock();
+ try {
+ that.ds.readLock().lock();
+ try {
+ if (!this.ds.isOpen() || !that.ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+ try {
+ renameat(this.dfd, from.asByteArray(), that.dfd, to.asByteArray());
+ } catch (UnixException x) {
+ if (x.errno() == EXDEV) {
+ throw new AtomicMoveNotSupportedException(
+ from.toString(), to.toString(), x.errorString());
+ }
+ x.rethrowAsIOException(from, to);
+ }
+ } finally {
+ that.ds.readLock().unlock();
+ }
+ } finally {
+ this.ds.readLock().unlock();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <V extends FileAttributeView> V getFileAttributeViewImpl(UnixPath file,
+ Class<V> type,
+ boolean followLinks)
+ {
+ if (type == null)
+ throw new NullPointerException();
+ Class<?> c = type;
+ if (c == BasicFileAttributeView.class) {
+ return (V) new BasicFileAttributeViewImpl(file, followLinks);
+ }
+ if (c == PosixFileAttributeView.class || c == FileOwnerAttributeView.class) {
+ return (V) new PosixFileAttributeViewImpl(file, followLinks);
+ }
+ // TBD - should also support AclFileAttributeView
+ return (V) null;
+ }
+
+ /**
+ * Returns file attribute view bound to this directory
+ */
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) {
+ return getFileAttributeViewImpl(null, type, false);
+ }
+
+ /**
+ * Returns file attribute view bound to dfd/filename.
+ */
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(Path obj,
+ Class<V> type,
+ LinkOption... options)
+ {
+ UnixPath file = getName(obj);
+ boolean followLinks = file.getFileSystem().followLinks(options);
+ return getFileAttributeViewImpl(file, type, followLinks);
+ }
+
+ /**
+ * A BasicFileAttributeView implementation that using a dfd/name pair.
+ */
+ private class BasicFileAttributeViewImpl
+ extends AbstractBasicFileAttributeView
+ {
+ final UnixPath file;
+ final boolean followLinks;
+
+ // set to true when binding to another object
+ volatile boolean forwarding;
+
+ BasicFileAttributeViewImpl(UnixPath file, boolean followLinks)
+ {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ int open() throws IOException {
+ int oflags = O_RDONLY;
+ if (!followLinks)
+ oflags |= O_NOFOLLOW;
+ try {
+ return openat(dfd, file.asByteArray(), oflags, 0);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return -1; // keep compiler happy
+ }
+ }
+
+ private void checkWriteAccess() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ ds.directory().resolve(file).checkWrite();
+ }
+ }
+
+ @Override
+ public String name() {
+ return "basic";
+ }
+
+ @Override
+ public BasicFileAttributes readAttributes() throws IOException {
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (file == null) {
+ ds.directory().checkRead();
+ } else {
+ ds.directory().resolve(file).checkRead();
+ }
+ }
+ try {
+ UnixFileAttributes attrs = (file == null) ?
+ UnixFileAttributes.get(dfd) :
+ UnixFileAttributes.get(dfd, file, followLinks);
+
+ // SECURITY: must return as BasicFileAttribute
+ return attrs.asBasicFileAttributes();
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ @Override
+ public void setTimes(Long lastModifiedTime,
+ Long lastAccessTime,
+ Long createTime, // ignore
+ TimeUnit unit)
+ throws IOException
+ {
+ // no effect
+ if (lastModifiedTime == null && lastAccessTime == null) {
+ return;
+ }
+
+ checkWriteAccess();
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ int fd = (file == null) ? dfd : open();
+ try {
+ UnixFileAttributes attrs = null;
+
+ // if not changing both attributes then need existing attributes
+ if (lastModifiedTime == null || lastAccessTime == null) {
+ try {
+ attrs = UnixFileAttributes.get(fd);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ }
+
+ // modified time = existing, now, or new value
+ long modTime;
+ if (lastModifiedTime == null) {
+ modTime = attrs.lastModifiedTime();
+ } else {
+ if (lastModifiedTime >= 0L) {
+ modTime = TimeUnit.MILLISECONDS.convert(lastModifiedTime, unit);
+ } else {
+ if (lastModifiedTime != -1L)
+ throw new IllegalArgumentException();
+ modTime = System.currentTimeMillis();
+ }
+ }
+
+ // access time = existing, now, or new value
+ long accTime;
+ if (lastAccessTime == null) {
+ accTime = attrs.lastAccessTime();
+ } else {
+ if (lastAccessTime >= 0L) {
+ accTime = TimeUnit.MILLISECONDS.convert(lastAccessTime, unit);
+ } else {
+ if (lastAccessTime != -1L)
+ throw new IllegalArgumentException();
+ accTime = System.currentTimeMillis();
+ }
+ }
+
+ try {
+ futimes(fd, accTime, modTime);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ }
+ } finally {
+ if (file != null)
+ UnixNativeDispatcher.close(fd);
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+ }
+
+ /**
+ * A PosixFileAttributeView implementation that using a dfd/name pair.
+ */
+ private class PosixFileAttributeViewImpl
+ extends BasicFileAttributeViewImpl implements PosixFileAttributeView
+ {
+ private static final String PERMISSIONS_NAME = "permissions";
+ private static final String OWNER_NAME = "owner";
+ private static final String GROUP_NAME = "group";
+
+ PosixFileAttributeViewImpl(UnixPath file, boolean followLinks) {
+ super(file, followLinks);
+ }
+
+ private void checkWriteAndUserAccess() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ super.checkWriteAccess();
+ sm.checkPermission(new RuntimePermission("accessUserInformation"));
+ }
+ }
+
+ @Override
+ public String name() {
+ return "posix";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(PERMISSIONS_NAME))
+ return readAttributes().permissions();
+ if (attribute.equals(OWNER_NAME))
+ return readAttributes().owner();
+ if (attribute.equals(GROUP_NAME))
+ return readAttributes().group();
+ return super.getAttribute(attribute);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(PERMISSIONS_NAME)) {
+ setPermissions((Set<PosixFilePermission>)value);
+ return;
+ }
+ if (attribute.equals(OWNER_NAME)) {
+ setOwner((UserPrincipal)value);
+ return;
+ }
+ if (attribute.equals(GROUP_NAME)) {
+ setGroup((GroupPrincipal)value);
+ return;
+ }
+ super.setAttribute(attribute, value);
+ }
+
+ final void addPosixAttributesToBuilder(PosixFileAttributes attrs,
+ AttributesBuilder builder)
+ {
+ if (builder.match(PERMISSIONS_NAME))
+ builder.add(PERMISSIONS_NAME, attrs.permissions());
+ if (builder.match(OWNER_NAME))
+ builder.add(OWNER_NAME, attrs.owner());
+ if (builder.match(GROUP_NAME))
+ builder.add(GROUP_NAME, attrs.group());
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ AttributesBuilder builder = AttributesBuilder.create(first, rest);
+ PosixFileAttributes attrs = readAttributes();
+ addBasicAttributesToBuilder(attrs, builder);
+ addPosixAttributesToBuilder(attrs, builder);
+ return builder.unmodifiableMap();
+ }
+
+ @Override
+ public PosixFileAttributes readAttributes() throws IOException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (file == null)
+ ds.directory().checkRead();
+ else
+ ds.directory().resolve(file).checkRead();
+ sm.checkPermission(new RuntimePermission("accessUserInformation"));
+ }
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ try {
+ UnixFileAttributes attrs = (file == null) ?
+ UnixFileAttributes.get(dfd) :
+ UnixFileAttributes.get(dfd, file, followLinks);
+ return attrs;
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ @Override
+ public void setPermissions(Set<PosixFilePermission> perms)
+ throws IOException
+ {
+ // permission check
+ checkWriteAndUserAccess();
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ int fd = (file == null) ? dfd : open();
+ try {
+ fchmod(fd, UnixFileModeAttribute.toUnixMode(perms));
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ } finally {
+ if (file != null && fd >= 0)
+ UnixNativeDispatcher.close(fd);
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ private void setOwners(int uid, int gid) throws IOException {
+ // permission check
+ checkWriteAndUserAccess();
+
+ ds.readLock().lock();
+ try {
+ if (!ds.isOpen())
+ throw new ClosedDirectoryStreamException();
+
+ int fd = (file == null) ? dfd : open();
+ try {
+ fchown(fd, uid, gid);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file);
+ } finally {
+ if (file != null && fd >= 0)
+ UnixNativeDispatcher.close(fd);
+ }
+ } finally {
+ ds.readLock().unlock();
+ }
+ }
+
+ @Override
+ public UserPrincipal getOwner() throws IOException {
+ return readAttributes().owner();
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner)
+ throws IOException
+ {
+ if (!(owner instanceof UnixUserPrincipals.User))
+ throw new ProviderMismatchException();
+ if (owner instanceof UnixUserPrincipals.Group)
+ throw new IOException("'owner' parameter can't be a group");
+ int uid = ((UnixUserPrincipals.User)owner).uid();
+ setOwners(uid, -1);
+ }
+
+ @Override
+ public void setGroup(GroupPrincipal group)
+ throws IOException
+ {
+ if (!(group instanceof UnixUserPrincipals.Group))
+ throw new ProviderMismatchException();
+ int gid = ((UnixUserPrincipals.Group)group).gid();
+ setOwners(-1, gid);
+ }
+ }
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixUriUtils.java b/src/solaris/classes/sun/nio/fs/UnixUriUtils.java
new file mode 100644
index 000000000..bf3c78d63
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixUriUtils.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Unix specific Path <--> URI conversion
+ */
+
+class UnixUriUtils {
+ private UnixUriUtils() { }
+
+ /**
+ * Converts URI to Path
+ */
+ static UnixPath fromUri(UnixFileSystem fs, URI uri) {
+ if (!uri.isAbsolute())
+ throw new IllegalArgumentException("URI is not absolute");
+ if (uri.isOpaque())
+ throw new IllegalArgumentException("URI is not hierarchical");
+ String scheme = uri.getScheme();
+ if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
+ throw new IllegalArgumentException("URI scheme is not \"file\"");
+ if (uri.getAuthority() != null)
+ throw new IllegalArgumentException("URI has an authority component");
+ if (uri.getFragment() != null)
+ throw new IllegalArgumentException("URI has a fragment component");
+ if (uri.getQuery() != null)
+ throw new IllegalArgumentException("URI has a query component");
+
+ String path = uri.getPath();
+ if (path.equals(""))
+ throw new IllegalArgumentException("URI path component is empty");
+ if (path.endsWith("/") && (path.length() > 1)) {
+ // "/foo/" --> "/foo", but "/" --> "/"
+ path = path.substring(0, path.length() - 1);
+ }
+
+ // preserve bytes
+ byte[] result = new byte[path.length()];
+ for (int i=0; i<path.length(); i++) {
+ byte v = (byte)(path.charAt(i));
+ if (v == 0)
+ throw new IllegalArgumentException("Nul character not allowed");
+ result[i] = v;
+ }
+ return new UnixPath(fs, result);
+ }
+
+ /**
+ * Converts Path to URI
+ */
+ static URI toUri(UnixPath up) {
+ byte[] path = up.toAbsolutePath().asByteArray();
+ StringBuilder sb = new StringBuilder("file:///");
+ assert path[0] == '/';
+ for (int i=1; i<path.length; i++) {
+ char c = (char)(path[i] & 0xff);
+ if (match(c, L_PATH, H_PATH)) {
+ sb.append(c);
+ } else {
+ sb.append('%');
+ sb.append(hexDigits[(c >> 4) & 0x0f]);
+ sb.append(hexDigits[(c >> 0) & 0x0f]);
+ }
+ }
+
+ // trailing slash if directory
+ if (sb.charAt(sb.length()-1) != '/') {
+ try {
+ if (UnixFileAttributes.get(up, true).isDirectory())
+ sb.append('/');
+ } catch (UnixException x) {
+ // ignore
+ }
+ }
+
+ try {
+ return new URI(sb.toString());
+ } catch (URISyntaxException x) {
+ throw new AssertionError(x); // should not happen
+ }
+ }
+
+ // The following is copied from java.net.URI
+
+ // Compute the low-order mask for the characters in the given string
+ private static long lowMask(String chars) {
+ int n = chars.length();
+ long m = 0;
+ for (int i = 0; i < n; i++) {
+ char c = chars.charAt(i);
+ if (c < 64)
+ m |= (1L << c);
+ }
+ return m;
+ }
+
+ // Compute the high-order mask for the characters in the given string
+ private static long highMask(String chars) {
+ int n = chars.length();
+ long m = 0;
+ for (int i = 0; i < n; i++) {
+ char c = chars.charAt(i);
+ if ((c >= 64) && (c < 128))
+ m |= (1L << (c - 64));
+ }
+ return m;
+ }
+
+ // Compute a low-order mask for the characters
+ // between first and last, inclusive
+ private static long lowMask(char first, char last) {
+ long m = 0;
+ int f = Math.max(Math.min(first, 63), 0);
+ int l = Math.max(Math.min(last, 63), 0);
+ for (int i = f; i <= l; i++)
+ m |= 1L << i;
+ return m;
+ }
+
+ // Compute a high-order mask for the characters
+ // between first and last, inclusive
+ private static long highMask(char first, char last) {
+ long m = 0;
+ int f = Math.max(Math.min(first, 127), 64) - 64;
+ int l = Math.max(Math.min(last, 127), 64) - 64;
+ for (int i = f; i <= l; i++)
+ m |= 1L << i;
+ return m;
+ }
+
+ // Tell whether the given character is permitted by the given mask pair
+ private static boolean match(char c, long lowMask, long highMask) {
+ if (c < 64)
+ return ((1L << c) & lowMask) != 0;
+ if (c < 128)
+ return ((1L << (c - 64)) & highMask) != 0;
+ return false;
+ }
+
+ // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ // "8" | "9"
+ private static final long L_DIGIT = lowMask('0', '9');
+ private static final long H_DIGIT = 0L;
+
+ // upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ // "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ // "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ private static final long L_UPALPHA = 0L;
+ private static final long H_UPALPHA = highMask('A', 'Z');
+
+ // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
+ // "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
+ // "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
+ private static final long L_LOWALPHA = 0L;
+ private static final long H_LOWALPHA = highMask('a', 'z');
+
+ // alpha = lowalpha | upalpha
+ private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
+ private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
+
+ // alphanum = alpha | digit
+ private static final long L_ALPHANUM = L_DIGIT | L_ALPHA;
+ private static final long H_ALPHANUM = H_DIGIT | H_ALPHA;
+
+ // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ // "(" | ")"
+ private static final long L_MARK = lowMask("-_.!~*'()");
+ private static final long H_MARK = highMask("-_.!~*'()");
+
+ // unreserved = alphanum | mark
+ private static final long L_UNRESERVED = L_ALPHANUM | L_MARK;
+ private static final long H_UNRESERVED = H_ALPHANUM | H_MARK;
+
+ // pchar = unreserved | escaped |
+ // ":" | "@" | "&" | "=" | "+" | "$" | ","
+ private static final long L_PCHAR
+ = L_UNRESERVED | lowMask(":@&=+$,");
+ private static final long H_PCHAR
+ = H_UNRESERVED | highMask(":@&=+$,");
+
+ // All valid path characters
+ private static final long L_PATH = L_PCHAR | lowMask(";/");
+ private static final long H_PATH = H_PCHAR | highMask(";/");
+
+ private final static char[] hexDigits = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+}
diff --git a/src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java b/src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java
new file mode 100644
index 000000000..88dfe9c4d
--- /dev/null
+++ b/src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import static sun.nio.fs.UnixNativeDispatcher.*;
+
+/**
+ * Unix implementation of java.nio.file.attribute.UserPrincipal
+ */
+
+class UnixUserPrincipals {
+ private static User createSpecial(String name) { return new User(-1, name); }
+
+ static final User SPECIAL_OWNER = createSpecial("OWNER@");
+ static final User SPECIAL_GROUP = createSpecial("GROUP@");
+ static final User SPECIAL_EVERYONE = createSpecial("EVERYONE@");
+
+ static class User implements UserPrincipal {
+ private final int id; // uid or gid
+ private final boolean isGroup;
+ private final String name;
+
+ private User(int id, boolean isGroup, String name) {
+ this.id = id;
+ this.isGroup = isGroup;
+ this.name = name;
+ }
+
+ User(int id, String name) {
+ this(id, false, name);
+ }
+
+ int uid() {
+ if (isGroup)
+ throw new AssertionError();
+ return id;
+ }
+
+ int gid() {
+ if (isGroup)
+ return id;
+ throw new AssertionError();
+ }
+
+ boolean isSpecial() {
+ return id == -1;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof User))
+ return false;
+ User other = (User)obj;
+ if ((this.id != other.id) ||
+ (this.isGroup != other.isGroup)) {
+ return false;
+ }
+ // specials
+ if (this.id == -1 && other.id == -1)
+ return this.name.equals(other.name);
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (id != -1) ? id : name.hashCode();
+ }
+ }
+
+ static class Group extends User implements GroupPrincipal {
+ Group(int id, String name) {
+ super(id, true, name);
+ }
+ }
+
+ // return UserPrincipal representing given uid
+ static User fromUid(int uid) {
+ String name = null;
+ try {
+ name = new String(getpwuid(uid));
+ } catch (UnixException x) {
+ name = Integer.toString(uid);
+ }
+ return new User(uid, name);
+ }
+
+ // return GroupPrincipal representing given gid
+ static Group fromGid(int gid) {
+ String name = null;
+ try {
+ name = new String(getgrgid(gid));
+ } catch (UnixException x) {
+ name = Integer.toString(gid);
+ }
+ return new Group(gid, name);
+ }
+
+ // lookup user or group name
+ private static int lookupName(String name, boolean isGroup)
+ throws IOException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("lookupUserInformation"));
+ }
+ int id = -1;
+ try {
+ id = (isGroup) ? getgrnam(name) : getpwnam(name);
+ } catch (UnixException x) {
+ throw new IOException(name + ": " + x.errorString());
+ }
+ if (id == -1)
+ throw new UserPrincipalNotFoundException(name);
+ return id;
+
+ }
+
+ // lookup user name
+ static UserPrincipal lookupUser(String name) throws IOException {
+ if (name.equals(SPECIAL_OWNER.getName()))
+ return SPECIAL_OWNER;
+ if (name.equals(SPECIAL_GROUP.getName()))
+ return SPECIAL_GROUP;
+ if (name.equals(SPECIAL_EVERYONE.getName()))
+ return SPECIAL_EVERYONE;
+ int uid = lookupName(name, false);
+ return new User(uid, name);
+ }
+
+ // lookup group name
+ static GroupPrincipal lookupGroup(String group)
+ throws IOException
+ {
+ int gid = lookupName(group, true);
+ return new Group(gid, group);
+ }
+}
diff --git a/src/solaris/native/java/lang/UNIXProcess_md.c b/src/solaris/native/java/lang/UNIXProcess_md.c
index 8390a2baa..9cfe22a1f 100644
--- a/src/solaris/native/java/lang/UNIXProcess_md.c
+++ b/src/solaris/native/java/lang/UNIXProcess_md.c
@@ -260,6 +260,12 @@ Java_java_lang_UNIXProcess_waitForProcessExit(JNIEnv* env,
}
static int
+isAsciiDigit(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static int
closeDescriptors(void)
{
DIR *dp;
@@ -284,7 +290,7 @@ closeDescriptors(void)
*/
while ((dirp = readdir64(dp)) != NULL) {
int fd;
- if (isdigit(dirp->d_name[0]) &&
+ if (isAsciiDigit(dirp->d_name[0]) &&
(fd = strtol(dirp->d_name, NULL, 10)) >= from_fd + 2)
close(fd);
}
diff --git a/src/solaris/native/java/net/Inet4AddressImpl.c b/src/solaris/native/java/net/Inet4AddressImpl.c
index a41aa96e1..9e3cca486 100644
--- a/src/solaris/native/java/net/Inet4AddressImpl.c
+++ b/src/solaris/native/java/net/Inet4AddressImpl.c
@@ -165,16 +165,18 @@ Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
CHECK_NULL_RETURN(hostname, NULL);
+#ifdef __solaris__
/*
* Workaround for Solaris bug 4160367 - if a hostname contains a
* white space then 0.0.0.0 is returned
*/
- if (isspace(hostname[0])) {
+ if (isspace((unsigned char)hostname[0])) {
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
(char *)hostname);
JNU_ReleaseStringPlatformChars(env, host, hostname);
return NULL;
}
+#endif
/* Try once, with our static buffer. */
#ifdef __GLIBC__
@@ -325,7 +327,8 @@ static jboolean
ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
struct sockaddr_in* netif, jint ttl) {
jint size;
- jint n, len, hlen1, icmplen;
+ jint n, hlen1, icmplen;
+ socklen_t len;
char sendbuf[1500];
char recvbuf[1500];
struct icmp *icmp;
diff --git a/src/solaris/native/java/net/Inet6AddressImpl.c b/src/solaris/native/java/net/Inet6AddressImpl.c
index 181307fcb..5ecedbc6c 100644
--- a/src/solaris/native/java/net/Inet6AddressImpl.c
+++ b/src/solaris/native/java/net/Inet6AddressImpl.c
@@ -196,16 +196,18 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
+#ifdef __solaris__
/*
* Workaround for Solaris bug 4160367 - if a hostname contains a
* white space then 0.0.0.0 is returned
*/
- if (isspace(hostname[0])) {
+ if (isspace((unsigned char)hostname[0])) {
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
(char *)hostname);
JNU_ReleaseStringPlatformChars(env, host, hostname);
return NULL;
}
+#endif
error = (*getaddrinfo_ptr)(hostname, NULL, &hints, &res);
@@ -455,7 +457,8 @@ static jboolean
ping6(JNIEnv *env, jint fd, struct sockaddr_in6* him, jint timeout,
struct sockaddr_in6* netif, jint ttl) {
jint size;
- jint n, len;
+ jint n;
+ socklen_t len;
char sendbuf[1500];
unsigned char recvbuf[1500];
struct icmp6_hdr *icmp6;
diff --git a/src/solaris/native/java/net/NetworkInterface.c b/src/solaris/native/java/net/NetworkInterface.c
index 14273f5c1..a741f86ad 100644
--- a/src/solaris/native/java/net/NetworkInterface.c
+++ b/src/solaris/native/java/net/NetworkInterface.c
@@ -969,13 +969,39 @@ netif *addif(JNIEnv *env, netif *ifs, char *if_name, int index, int family,
// Got access to parent, so create it if necessary.
strcpy(vname, name);
*unit = '\0';
- }
- else {
+ } else {
+#if defined(__solaris__) && defined(AF_INET6)
+ struct lifreq lifr;
+ memset((char *) &lifr, 0, sizeof(lifr));
+ strcpy(lifr.lifr_name, vname);
+
+ /* Try with an IPv6 socket in case the interface has only IPv6
+ * addresses assigned to it */
+ close(sock);
+ sock = JVM_Socket(AF_INET6, SOCK_DGRAM, 0);
+
+ if (sock < 0) {
+ NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "Socket creation failed");
+ return ifs; /* return untouched list */
+ }
+
+ if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifr) >= 0) {
+ // Got access to parent, so create it if necessary.
+ strcpy(vname, name);
+ *unit = '\0';
+ } else {
+ // failed to access parent interface do not create parent.
+ // We are a virtual interface with no parent.
+ isVirtual = 1;
+ vname[0] = 0;
+ }
+#else
// failed to access parent interface do not create parent.
// We are a virtual interface with no parent.
isVirtual = 1;
-
vname[0] = 0;
+#endif
}
}
close(sock);
diff --git a/src/solaris/native/sun/awt/awt_Robot.c b/src/solaris/native/sun/awt/awt_Robot.c
index 07c053bce..6fce85dc7 100644
--- a/src/solaris/native/sun/awt/awt_Robot.c
+++ b/src/solaris/native/sun/awt/awt_Robot.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -54,6 +54,7 @@ extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;
// 2 would be more correct, however that's how Robot originally worked
// and tests start to fail if this value is changed
static int32_t num_buttons = 3;
+static jint * masks;
static int32_t isXTestAvailable() {
int32_t major_opcode, first_event, first_error;
@@ -208,6 +209,26 @@ JNIEXPORT void JNICALL
Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls) {
int32_t xtestAvailable;
+// this should be called from XRobotPeer constructor
+ jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
+ jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
+ jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
+ jsize len = (*env)->GetArrayLength(env, obj);
+ jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
+
+ masks = (jint *)malloc(sizeof(jint)*len);
+ if (masks == (jint *) NULL) {
+ JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL);
+ goto finally;
+ }
+
+ int i;
+ for (i = 0; i < len; i++) {
+ masks[i] = tmp[i];
+ }
+ (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
+ (*env)->DeleteLocalRef(env, obj);
+
DTRACE_PRINTLN("RobotPeer: setup()");
AWT_LOCK();
@@ -221,10 +242,17 @@ Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls) {
}
getNumButtons();
-
+ finally:
AWT_UNLOCK();
}
+JNIEXPORT jint JNICALL
+Java_sun_awt_X11_XRobotPeer_getNumberOfButtonsImpl(JNIEnv *env,
+ jclass cls) {
+ // At the moment this routine being called we already should have an initialized num_buttons variable.
+ return num_buttons;
+}
+
JNIEXPORT void JNICALL
Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env,
jclass cls,
@@ -348,52 +376,65 @@ Java_sun_awt_X11_XRobotPeer_mouseMoveImpl (JNIEnv *env,
AWT_UNLOCK();
}
-JNIEXPORT void JNICALL
-Java_sun_awt_X11_XRobotPeer_mousePressImpl (JNIEnv *env,
- jclass cls,
- jint buttonMask) {
+/*
+ * Function joining the code of mousePressImpl and mouseReleaseImpl
+ */
+void mouseAction(JNIEnv *env,
+ jclass cls,
+ jint buttonMask,
+ Bool isMousePress)
+{
AWT_LOCK();
- DTRACE_PRINTLN1("RobotPeer: mousePressImpl(%i)", buttonMask);
+ DTRACE_PRINTLN1("RobotPeer: mouseAction(%i)", buttonMask);
+ DTRACE_PRINTLN1("RobotPeer: mouseAction, press = %d", isMousePress);
- if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK) {
- XTestFakeButtonEvent(awt_display, 1, True, CurrentTime);
+ if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK )
+ {
+ XTestFakeButtonEvent(awt_display, 1, isMousePress, CurrentTime);
}
- if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK) &&
+ if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) &&
(num_buttons >= 2)) {
- XTestFakeButtonEvent(awt_display, 2, True, CurrentTime);
+ XTestFakeButtonEvent(awt_display, 2, isMousePress, CurrentTime);
}
- if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK) &&
+ if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) &&
(num_buttons >= 3)) {
- XTestFakeButtonEvent(awt_display, 3, True, CurrentTime);
+ XTestFakeButtonEvent(awt_display, 3, isMousePress, CurrentTime);
}
- XSync(awt_display, False);
+ if (num_buttons > 3){
+ int32_t i;
+ int32_t button = 0;
+ for (i = 3; i<num_buttons; i++){
+ if ((buttonMask & masks[i])) {
+ // arrays starts from zero index => +1
+ // users wants to affect 4 or 5 button but they are assigned
+ // to the wheel so => we have to shift it to the right by 2.
+ button = i + 3;
+ XTestFakeButtonEvent(awt_display, button, isMousePress, CurrentTime);
+ }
+ }
+ }
+
+ XSync(awt_display, False);
AWT_UNLOCK();
}
JNIEXPORT void JNICALL
+Java_sun_awt_X11_XRobotPeer_mousePressImpl (JNIEnv *env,
+ jclass cls,
+ jint buttonMask) {
+ mouseAction(env, cls, buttonMask, True);
+}
+
+JNIEXPORT void JNICALL
Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl (JNIEnv *env,
jclass cls,
jint buttonMask) {
- AWT_LOCK();
-
- DTRACE_PRINTLN1("RobotPeer: mouseReleaseImpl(%i)", buttonMask);
-
- if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK) {
- XTestFakeButtonEvent(awt_display, 1, False, CurrentTime);
- }
- if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK) &&
- (num_buttons >= 2)) {
- XTestFakeButtonEvent(awt_display, 2, False, CurrentTime);
- }
- if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK) &&
- (num_buttons >= 3)) {
- XTestFakeButtonEvent(awt_display, 3, False, CurrentTime);
- }
- XSync(awt_display, False);
-
- AWT_UNLOCK();
+ mouseAction(env, cls, buttonMask, False);
}
JNIEXPORT void JNICALL
diff --git a/src/solaris/native/sun/nio/ch/EPoll.c b/src/solaris/native/sun/nio/ch/EPoll.c
new file mode 100644
index 000000000..fc9cab72c
--- /dev/null
+++ b/src/solaris/native/sun/nio/ch/EPoll.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_EPoll.h"
+
+#include <dlfcn.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* epoll_wait(2) man page */
+
+typedef union epoll_data {
+ void *ptr;
+ int fd;
+ __uint32_t u32;
+ __uint64_t u64;
+} epoll_data_t;
+
+struct epoll_event {
+ __uint32_t events; /* Epoll events */
+ epoll_data_t data; /* User data variable */
+} __attribute__ ((__packed__));
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * epoll event notification is new in 2.6 kernel. As the offical build
+ * platform for the JDK is on a 2.4-based distribution then we must
+ * obtain the addresses of the epoll functions dynamically.
+ */
+typedef int (*epoll_create_t)(int size);
+typedef int (*epoll_ctl_t) (int epfd, int op, int fd, struct epoll_event *event);
+typedef int (*epoll_wait_t) (int epfd, struct epoll_event *events, int maxevents, int timeout);
+
+static epoll_create_t epoll_create_func;
+static epoll_ctl_t epoll_ctl_func;
+static epoll_wait_t epoll_wait_func;
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_EPoll_init(JNIEnv *env, jclass this)
+{
+ epoll_create_func = (epoll_create_t) dlsym(RTLD_DEFAULT, "epoll_create");
+ epoll_ctl_func = (epoll_ctl_t) dlsym(RTLD_DEFAULT, "epoll_ctl");
+ epoll_wait_func = (epoll_wait_t) dlsym(RTLD_DEFAULT, "epoll_wait");
+
+ if ((epoll_create_func == NULL) || (epoll_ctl_func == NULL) ||
+ (epoll_wait_func == NULL)) {
+ JNU_ThrowInternalError(env, "unable to get address of epoll functions, pre-2.6 kernel?");
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_EPoll_eventSize(JNIEnv* env, jclass this)
+{
+ return sizeof(struct epoll_event);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_EPoll_eventsOffset(JNIEnv* env, jclass this)
+{
+ return offsetof(struct epoll_event, events);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_EPoll_dataOffset(JNIEnv* env, jclass this)
+{
+ return offsetof(struct epoll_event, data);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_EPoll_epollCreate(JNIEnv *env, jclass c) {
+ /*
+ * epoll_create expects a size as a hint to the kernel about how to
+ * dimension internal structures. We can't predict the size in advance.
+ */
+ int epfd = (*epoll_create_func)(256);
+ if (epfd < 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed");
+ }
+ return epfd;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_EPoll_epollCtl(JNIEnv *env, jclass c, jint epfd,
+ jint opcode, jint fd, jint events)
+{
+ struct epoll_event event;
+ int res;
+
+ event.events = events;
+ event.data.fd = fd;
+
+ RESTARTABLE((*epoll_ctl_func)(epfd, (int)opcode, (int)fd, &event), res);
+
+ return (res == 0) ? 0 : errno;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_EPoll_epollWait(JNIEnv *env, jclass c,
+ jint epfd, jlong address, jint numfds)
+{
+ struct epoll_event *events = jlong_to_ptr(address);
+ int res;
+
+ RESTARTABLE((*epoll_wait_func)(epfd, events, numfds, -1), res);
+ if (res < 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed");
+ }
+ return res;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_EPoll_close0(JNIEnv *env, jclass c, jint epfd) {
+ int res;
+ RESTARTABLE(close(epfd), res);
+}
diff --git a/src/solaris/native/sun/nio/ch/EPollPort.c b/src/solaris/native/sun/nio/ch/EPollPort.c
new file mode 100644
index 000000000..8d34dd8dd
--- /dev/null
+++ b/src/solaris/native/sun/nio/ch/EPollPort.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_EPollPort.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_EPollPort_socketpair(JNIEnv* env, jclass clazz, jintArray sv) {
+ int sp[2];
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "socketpair failed");
+ } else {
+ jint res[2];
+ res[0] = (jint)sp[0];
+ res[1] = (jint)sp[1];
+ (*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_EPollPort_interrupt(JNIEnv *env, jclass c, jint fd) {
+ int res;
+ int buf[1];
+ buf[0] = 1;
+ RESTARTABLE(write(fd, buf, 1), res);
+ if (res < 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "write failed");
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_EPollPort_drain1(JNIEnv *env, jclass cl, jint fd) {
+ int res;
+ char buf[1];
+ RESTARTABLE(read(fd, buf, 1), res);
+ if (res < 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "drain1 failed");
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_EPollPort_close0(JNIEnv *env, jclass c, jint fd) {
+ int res;
+ RESTARTABLE(close(fd), res);
+}
diff --git a/src/solaris/native/sun/nio/ch/FileChannelImpl.c b/src/solaris/native/sun/nio/ch/FileChannelImpl.c
index b42c899e5..7b37e6168 100644
--- a/src/solaris/native/sun/nio/ch/FileChannelImpl.c
+++ b/src/solaris/native/sun/nio/ch/FileChannelImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -31,7 +31,6 @@
#include <sys/stat.h>
#include "sun_nio_ch_FileChannelImpl.h"
#include "java_lang_Integer.h"
-#include "java_lang_Long.h"
#include "nio.h"
#include "nio_util.h"
#include <dlfcn.h>
@@ -145,32 +144,6 @@ Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this,
}
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileChannelImpl_truncate0(JNIEnv *env, jobject this,
- jobject fdo, jlong size)
-{
- return handle(env,
- ftruncate64(fdval(env, fdo), size),
- "Truncation failed");
-}
-
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileChannelImpl_force0(JNIEnv *env, jobject this,
- jobject fdo, jboolean md)
-{
- jint fd = fdval(env, fdo);
- int result = 0;
-
- if (md == JNI_FALSE) {
- result = fdatasync(fd);
- } else {
- result = fsync(fd);
- }
- return handle(env, result, "Force failed");
-}
-
-
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this,
jobject fdo, jlong offset)
@@ -187,17 +160,6 @@ Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this,
}
-JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileChannelImpl_size0(JNIEnv *env, jobject this, jobject fdo)
-{
- struct stat64 fbuf;
-
- if (fstat64(fdval(env, fdo), &fbuf) < 0)
- return handle(env, -1, "Size failed");
- return fbuf.st_size;
-}
-
-
JNIEXPORT void JNICALL
Java_sun_nio_ch_FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo)
{
@@ -280,65 +242,3 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
}
#endif
}
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileChannelImpl_lock0(JNIEnv *env, jobject this, jobject fdo,
- jboolean block, jlong pos, jlong size,
- jboolean shared)
-{
- jint fd = fdval(env, fdo);
- jint lockResult = 0;
- int cmd = 0;
- struct flock64 fl;
-
- fl.l_whence = SEEK_SET;
- if (size == (jlong)java_lang_Long_MAX_VALUE) {
- fl.l_len = (off64_t)0;
- } else {
- fl.l_len = (off64_t)size;
- }
- fl.l_start = (off64_t)pos;
- if (shared == JNI_TRUE) {
- fl.l_type = F_RDLCK;
- } else {
- fl.l_type = F_WRLCK;
- }
- if (block == JNI_TRUE) {
- cmd = F_SETLKW64;
- } else {
- cmd = F_SETLK64;
- }
- lockResult = fcntl(fd, cmd, &fl);
- if (lockResult < 0) {
- if ((cmd == F_SETLK64) && (errno == EAGAIN))
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
- if (errno == EINTR)
- return sun_nio_ch_FileChannelImpl_INTERRUPTED;
- JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
- }
- return 0;
-}
-
-
-JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileChannelImpl_release0(JNIEnv *env, jobject this,
- jobject fdo, jlong pos, jlong size)
-{
- jint fd = fdval(env, fdo);
- jint lockResult = 0;
- struct flock64 fl;
- int cmd = F_SETLK64;
-
- fl.l_whence = SEEK_SET;
- if (size == (jlong)java_lang_Long_MAX_VALUE) {
- fl.l_len = (off64_t)0;
- } else {
- fl.l_len = (off64_t)size;
- }
- fl.l_start = (off64_t)pos;
- fl.l_type = F_UNLCK;
- lockResult = fcntl(fd, cmd, &fl);
- if (lockResult < 0) {
- JNU_ThrowIOExceptionWithLastError(env, "Release failed");
- }
-}
diff --git a/src/solaris/native/sun/nio/ch/FileDispatcher.c b/src/solaris/native/sun/nio/ch/FileDispatcherImpl.c
index fd3c0e3d6..ed6588933 100644
--- a/src/solaris/native/sun/nio/ch/FileDispatcher.c
+++ b/src/solaris/native/sun/nio/ch/FileDispatcherImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -27,11 +27,13 @@
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
-#include "sun_nio_ch_FileDispatcher.h"
+#include "sun_nio_ch_FileDispatcherImpl.h"
+#include "java_lang_Long.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/uio.h>
+#include "nio.h"
#include "nio_util.h"
@@ -40,7 +42,7 @@ static int preCloseFD = -1; /* File descriptor to which we dup other fd's
JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileDispatcher_init(JNIEnv *env, jclass cl)
+Java_sun_nio_ch_FileDispatcherImpl_init(JNIEnv *env, jclass cl)
{
int sp[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) {
@@ -52,7 +54,7 @@ Java_sun_nio_ch_FileDispatcher_init(JNIEnv *env, jclass cl)
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz,
+Java_sun_nio_ch_FileDispatcherImpl_read0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
@@ -62,7 +64,7 @@ Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz,
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len, jlong offset)
{
jint fd = fdval(env, fdo);
@@ -72,7 +74,7 @@ Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz,
+Java_sun_nio_ch_FileDispatcherImpl_readv0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
@@ -84,7 +86,7 @@ Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz,
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz,
+Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
@@ -94,7 +96,7 @@ Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz,
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_pwrite0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len, jlong offset)
{
jint fd = fdval(env, fdo);
@@ -104,7 +106,7 @@ Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz,
+Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
@@ -115,6 +117,113 @@ Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz,
return convertLongReturnVal(env, writev(fd, iov, len), JNI_FALSE);
}
+static jlong
+handle(JNIEnv *env, jlong rv, char *msg)
+{
+ if (rv >= 0)
+ return rv;
+ if (errno == EINTR)
+ return IOS_INTERRUPTED;
+ JNU_ThrowIOExceptionWithLastError(env, msg);
+ return IOS_THROWN;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_force0(JNIEnv *env, jobject this,
+ jobject fdo, jboolean md)
+{
+ jint fd = fdval(env, fdo);
+ int result = 0;
+
+ if (md == JNI_FALSE) {
+ result = fdatasync(fd);
+ } else {
+ result = fsync(fd);
+ }
+ return handle(env, result, "Force failed");
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_truncate0(JNIEnv *env, jobject this,
+ jobject fdo, jlong size)
+{
+ return handle(env,
+ ftruncate64(fdval(env, fdo), size),
+ "Truncation failed");
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_size0(JNIEnv *env, jobject this, jobject fdo)
+{
+ struct stat64 fbuf;
+
+ if (fstat64(fdval(env, fdo), &fbuf) < 0)
+ return handle(env, -1, "Size failed");
+ return fbuf.st_size;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo,
+ jboolean block, jlong pos, jlong size,
+ jboolean shared)
+{
+ jint fd = fdval(env, fdo);
+ jint lockResult = 0;
+ int cmd = 0;
+ struct flock64 fl;
+
+ fl.l_whence = SEEK_SET;
+ if (size == (jlong)java_lang_Long_MAX_VALUE) {
+ fl.l_len = (off64_t)0;
+ } else {
+ fl.l_len = (off64_t)size;
+ }
+ fl.l_start = (off64_t)pos;
+ if (shared == JNI_TRUE) {
+ fl.l_type = F_RDLCK;
+ } else {
+ fl.l_type = F_WRLCK;
+ }
+ if (block == JNI_TRUE) {
+ cmd = F_SETLKW64;
+ } else {
+ cmd = F_SETLK64;
+ }
+ lockResult = fcntl(fd, cmd, &fl);
+ if (lockResult < 0) {
+ if ((cmd == F_SETLK64) && (errno == EAGAIN))
+ return sun_nio_ch_FileDispatcherImpl_NO_LOCK;
+ if (errno == EINTR)
+ return sun_nio_ch_FileDispatcherImpl_INTERRUPTED;
+ JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
+ }
+ return 0;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_release0(JNIEnv *env, jobject this,
+ jobject fdo, jlong pos, jlong size)
+{
+ jint fd = fdval(env, fdo);
+ jint lockResult = 0;
+ struct flock64 fl;
+ int cmd = F_SETLK64;
+
+ fl.l_whence = SEEK_SET;
+ if (size == (jlong)java_lang_Long_MAX_VALUE) {
+ fl.l_len = (off64_t)0;
+ } else {
+ fl.l_len = (off64_t)size;
+ }
+ fl.l_start = (off64_t)pos;
+ fl.l_type = F_UNLCK;
+ lockResult = fcntl(fd, cmd, &fl);
+ if (lockResult < 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "Release failed");
+ }
+}
+
+
static void closeFileDescriptor(JNIEnv *env, int fd) {
if (fd != -1) {
int result = close(fd);
@@ -124,14 +233,14 @@ static void closeFileDescriptor(JNIEnv *env, int fd) {
}
JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileDispatcher_close0(JNIEnv *env, jclass clazz, jobject fdo)
+Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo)
{
jint fd = fdval(env, fdo);
closeFileDescriptor(env, fd);
}
JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileDispatcher_preClose0(JNIEnv *env, jclass clazz, jobject fdo)
+Java_sun_nio_ch_FileDispatcherImpl_preClose0(JNIEnv *env, jclass clazz, jobject fdo)
{
jint fd = fdval(env, fdo);
if (preCloseFD >= 0) {
@@ -141,7 +250,7 @@ Java_sun_nio_ch_FileDispatcher_preClose0(JNIEnv *env, jclass clazz, jobject fdo)
}
JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileDispatcher_closeIntFD(JNIEnv *env, jclass clazz, jint fd)
+Java_sun_nio_ch_FileDispatcherImpl_closeIntFD(JNIEnv *env, jclass clazz, jint fd)
{
closeFileDescriptor(env, fd);
}
diff --git a/src/solaris/native/sun/nio/ch/SocketDispatcher.c b/src/solaris/native/sun/nio/ch/SocketDispatcher.c
index 1524326b6..812ff4bed 100644
--- a/src/solaris/native/sun/nio/ch/SocketDispatcher.c
+++ b/src/solaris/native/sun/nio/ch/SocketDispatcher.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -27,7 +27,6 @@
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
-#include "sun_nio_ch_FileDispatcher.h"
/* this is a fake c file to make the build happy since there is no
real SocketDispatcher.c file on Solaris but there is on windows. */
diff --git a/src/solaris/native/sun/nio/ch/SolarisEventPort.c b/src/solaris/native/sun/nio/ch/SolarisEventPort.c
new file mode 100644
index 000000000..649475946
--- /dev/null
+++ b/src/solaris/native/sun/nio/ch/SolarisEventPort.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+#include "nio_util.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <port.h> // Solaris 10
+
+#include "sun_nio_ch_SolarisEventPort.h"
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_SolarisEventPort_init(JNIEnv *env, jclass clazz)
+{
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_SolarisEventPort_portCreate
+ (JNIEnv* env, jclass clazz)
+{
+ int port = port_create();
+ if (port == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "port_create");
+ }
+ return (jint)port;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_SolarisEventPort_portClose
+ (JNIEnv* env, jclass clazz, jint port)
+{
+ int res;
+ RESTARTABLE(close(port), res);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_SolarisEventPort_portAssociate
+ (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress, jint events)
+{
+ uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress);
+
+ if (port_associate((int)port, (int)source, object, (int)events, NULL) == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "port_associate");
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_SolarisEventPort_portDissociate
+ (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress)
+{
+ uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress);
+
+ if (port_dissociate((int)port, (int)source, object) == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "port_dissociate");
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_SolarisEventPort_portSend(JNIEnv* env, jclass clazz,
+ jint port, jint events)
+{
+ if (port_send((int)port, (int)events, NULL) == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "port_send");
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_SolarisEventPort_portGet(JNIEnv* env, jclass clazz,
+ jint port, jlong eventAddress)
+{
+ int res;
+ port_event_t* ev = (port_event_t*)jlong_to_ptr(eventAddress);
+
+ RESTARTABLE(port_get((int)port, ev, NULL), res);
+ if (res == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "port_get");
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_SolarisEventPort_portGetn(JNIEnv* env, jclass clazz,
+ jint port, jlong arrayAddress, jint max)
+{
+ int res;
+ uint_t n = 1;
+ port_event_t* list = (port_event_t*)jlong_to_ptr(arrayAddress);
+
+ RESTARTABLE(port_getn((int)port, list, (uint_t)max, &n, NULL), res);
+ if (res == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "port_getn");
+ }
+ return (jint)n;
+}
diff --git a/src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c b/src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c
new file mode 100644
index 000000000..9c92cc2ad
--- /dev/null
+++ b/src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "sun_nio_ch_UnixAsynchronousServerSocketChannelImpl.h"
+
+extern void Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv* env,
+ jclass c);
+
+extern jint Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv* env,
+ jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa);
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_initIDs(JNIEnv* env,
+ jclass c)
+{
+ Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(env, c);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_accept0(JNIEnv* env,
+ jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa)
+{
+ return Java_sun_nio_ch_ServerSocketChannelImpl_accept0(env, this,
+ ssfdo, newfdo, isaa);
+}
diff --git a/src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c b/src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c
new file mode 100644
index 000000000..461d97f40
--- /dev/null
+++ b/src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "net_util.h"
+#include "jlong.h"
+#include "sun_nio_ch_UnixAsynchronousSocketChannelImpl.h"
+#include "nio_util.h"
+#include "nio.h"
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_UnixAsynchronousSocketChannelImpl_checkConnect(JNIEnv *env,
+ jobject this, int fd)
+{
+ int error = 0;
+ int n = sizeof(error);
+ int result;
+
+ result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n);
+ if (result < 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "getsockopt");
+ } else {
+ if (error)
+ handleSocketError(env, error);
+ }
+}
diff --git a/src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c b/src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c
new file mode 100644
index 000000000..0a169a94d
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <link.h>
+
+#ifdef __solaris__
+#include <strings.h>
+#endif
+
+#ifdef __linux__
+#include <string.h>
+#endif
+
+/* Definitions for GIO */
+
+#define G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "standard::content-type"
+
+typedef void* gpointer;
+typedef struct _GFile GFile;
+typedef struct _GFileInfo GFileInfo;
+typedef struct _GCancellable GCancellable;
+typedef struct _GError GError;
+
+typedef enum {
+ G_FILE_QUERY_INFO_NONE = 0
+} GFileQueryInfoFlags;
+
+typedef void (*g_type_init_func)(void);
+typedef void (*g_object_unref_func)(gpointer object);
+typedef GFile* (*g_file_new_for_path_func)(const char* path);
+typedef GFileInfo* (*g_file_query_info_func)(GFile *file,
+ const char *attributes, GFileQueryInfoFlags flags,
+ GCancellable *cancellable, GError **error);
+typedef char* (*g_file_info_get_content_type_func)(GFileInfo *info);
+
+static g_type_init_func g_type_init;
+static g_object_unref_func g_object_unref;
+static g_file_new_for_path_func g_file_new_for_path;
+static g_file_query_info_func g_file_query_info;
+static g_file_info_get_content_type_func g_file_info_get_content_type;
+
+
+/* Definitions for GNOME VFS */
+
+typedef int gboolean;
+
+typedef gboolean (*gnome_vfs_init_function)(void);
+typedef const char* (*gnome_vfs_mime_type_from_name_function)
+ (const char* filename);
+
+static gnome_vfs_init_function gnome_vfs_init;
+static gnome_vfs_mime_type_from_name_function gnome_vfs_mime_type_from_name;
+
+
+#include "sun_nio_fs_GnomeFileTypeDetector.h"
+
+
+JNIEXPORT jboolean JNICALL
+Java_sun_nio_fs_GnomeFileTypeDetector_initializeGio
+ (JNIEnv* env, jclass this)
+{
+ void* gio_handle;
+
+ gio_handle = dlopen("libgio-2.0.so", RTLD_LAZY);
+ if (gio_handle == NULL) {
+ gio_handle = dlopen("libgio-2.0.so.0", RTLD_LAZY);
+ if (gio_handle == NULL) {
+ return JNI_FALSE;
+ }
+ }
+
+ g_type_init = (g_type_init_func)dlsym(gio_handle, "g_type_init");
+ (*g_type_init)();
+
+ g_object_unref = (g_object_unref_func)dlsym(gio_handle, "g_object_unref");
+
+ g_file_new_for_path =
+ (g_file_new_for_path_func)dlsym(gio_handle, "g_file_new_for_path");
+
+ g_file_query_info =
+ (g_file_query_info_func)dlsym(gio_handle, "g_file_query_info");
+
+ g_file_info_get_content_type = (g_file_info_get_content_type_func)
+ dlsym(gio_handle, "g_file_info_get_content_type");
+
+
+ if (g_type_init == NULL ||
+ g_object_unref == NULL ||
+ g_file_new_for_path == NULL ||
+ g_file_query_info == NULL ||
+ g_file_info_get_content_type == NULL)
+ {
+ dlclose(gio_handle);
+ return JNI_FALSE;
+ }
+
+ (*g_type_init)();
+ return JNI_TRUE;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGio
+ (JNIEnv* env, jclass this, jlong pathAddress)
+{
+ char* path = (char*)jlong_to_ptr(pathAddress);
+ GFile* gfile;
+ GFileInfo* gfileinfo;
+ jbyteArray result = NULL;
+
+ gfile = (*g_file_new_for_path)(path);
+ gfileinfo = (*g_file_query_info)(gfile, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (gfileinfo != NULL) {
+ const char* mime = (*g_file_info_get_content_type)(gfileinfo);
+ if (mime != NULL) {
+ jsize len = strlen(mime);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)mime);
+ }
+ }
+ (*g_object_unref)(gfileinfo);
+ }
+ (*g_object_unref)(gfile);
+
+ return result;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_sun_nio_fs_GnomeFileTypeDetector_initializeGnomeVfs
+ (JNIEnv* env, jclass this)
+{
+ void* vfs_handle;
+
+ vfs_handle = dlopen("libgnomevfs-2.so", RTLD_LAZY);
+ if (vfs_handle == NULL) {
+ vfs_handle = dlopen("libgnomevfs-2.so.0", RTLD_LAZY);
+ }
+ if (vfs_handle == NULL) {
+ return JNI_FALSE;
+ }
+
+ gnome_vfs_init = (gnome_vfs_init_function)dlsym(vfs_handle, "gnome_vfs_init");
+ gnome_vfs_mime_type_from_name = (gnome_vfs_mime_type_from_name_function)
+ dlsym(vfs_handle, "gnome_vfs_mime_type_from_name");
+
+ if (gnome_vfs_init == NULL ||
+ gnome_vfs_mime_type_from_name == NULL)
+ {
+ dlclose(vfs_handle);
+ return JNI_FALSE;
+ }
+
+ (*gnome_vfs_init)();
+ return JNI_TRUE;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGnomeVfs
+ (JNIEnv* env, jclass this, jlong pathAddress)
+{
+ char* path = (char*)jlong_to_ptr(pathAddress);
+ const char* mime = (*gnome_vfs_mime_type_from_name)(path);
+
+ if (mime == NULL) {
+ return NULL;
+ } else {
+ jbyteArray result;
+ jsize len = strlen(mime);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)mime);
+ }
+ return result;
+ }
+}
diff --git a/src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c b/src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c
new file mode 100644
index 000000000..8e4fa6696
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <mntent.h>
+
+#include "sun_nio_fs_LinuxNativeDispatcher.h"
+
+typedef size_t fgetxattr_func(int fd, const char* name, void* value, size_t size);
+typedef int fsetxattr_func(int fd, const char* name, void* value, size_t size, int flags);
+typedef int fremovexattr_func(int fd, const char* name);
+typedef int flistxattr_func(int fd, char* list, size_t size);
+
+fgetxattr_func* my_fgetxattr_func = NULL;
+fsetxattr_func* my_fsetxattr_func = NULL;
+fremovexattr_func* my_fremovexattr_func = NULL;
+flistxattr_func* my_flistxattr_func = NULL;
+
+static void throwUnixException(JNIEnv* env, int errnum) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
+ "(I)V", errnum);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
+{
+ my_fgetxattr_func = (fgetxattr_func*)dlsym(RTLD_DEFAULT, "fgetxattr");
+ my_fsetxattr_func = (fsetxattr_func*)dlsym(RTLD_DEFAULT, "fsetxattr");
+ my_fremovexattr_func = (fremovexattr_func*)dlsym(RTLD_DEFAULT, "fremovexattr");
+ my_flistxattr_func = (flistxattr_func*)dlsym(RTLD_DEFAULT, "flistxattr");
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
+ jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
+{
+ size_t res = -1;
+ const char* name = jlong_to_ptr(nameAddress);
+ void* value = jlong_to_ptr(valueAddress);
+
+ if (my_fgetxattr_func == NULL) {
+ errno = ENOTSUP;
+ } else {
+ /* EINTR not documented */
+ res = (*my_fgetxattr_func)(fd, name, value, valueLen);
+ }
+ if (res == (size_t)-1)
+ throwUnixException(env, errno);
+ return (jint)res;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
+ jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
+{
+ int res = -1;
+ const char* name = jlong_to_ptr(nameAddress);
+ void* value = jlong_to_ptr(valueAddress);
+
+ if (my_fsetxattr_func == NULL) {
+ errno = ENOTSUP;
+ } else {
+ /* EINTR not documented */
+ res = (*my_fsetxattr_func)(fd, name, value, valueLen, 0);
+ }
+ if (res == -1)
+ throwUnixException(env, errno);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
+ jint fd, jlong nameAddress)
+{
+ int res = -1;
+ const char* name = jlong_to_ptr(nameAddress);
+
+ if (my_fremovexattr_func == NULL) {
+ errno = ENOTSUP;
+ } else {
+ /* EINTR not documented */
+ res = (*my_fremovexattr_func)(fd, name);
+ }
+ if (res == -1)
+ throwUnixException(env, errno);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz,
+ jint fd, jlong listAddress, jint size)
+{
+ size_t res = -1;
+ char* list = jlong_to_ptr(listAddress);
+
+ if (my_flistxattr_func == NULL) {
+ errno = ENOTSUP;
+ } else {
+ /* EINTR not documented */
+ res = (*my_flistxattr_func)(fd, list, (size_t)size);
+ }
+ if (res == (size_t)-1)
+ throwUnixException(env, errno);
+ return (jint)res;
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress,
+ jlong modeAddress)
+{
+ FILE* fp = NULL;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+ const char* mode = (const char*)jlong_to_ptr(modeAddress);
+
+ do {
+ fp = setmntent(path, mode);
+ } while (fp == NULL && errno == EINTR);
+ if (fp == NULL) {
+ throwUnixException(env, errno);
+ }
+ return ptr_to_jlong(fp);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
+{
+ FILE* fp = jlong_to_ptr(stream);
+ /* FIXME - man page doesn't explain how errors are returned */
+ endmntent(fp);
+}
diff --git a/src/solaris/native/sun/nio/fs/LinuxWatchService.c b/src/solaris/native/sun/nio/fs/LinuxWatchService.c
new file mode 100644
index 000000000..74a154079
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/LinuxWatchService.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+
+#include "sun_nio_fs_LinuxWatchService.h"
+
+/* inotify.h may not be available at build time */
+#ifdef __cplusplus
+extern "C" {
+#endif
+struct inotify_event
+{
+ int wd;
+ uint32_t mask;
+ uint32_t cookie;
+ uint32_t len;
+ char name __flexarr;
+};
+#ifdef __cplusplus
+}
+#endif
+
+typedef int inotify_init_func(void);
+typedef int inotify_add_watch_func(int fd, const char* path, uint32_t mask);
+typedef int inotify_rm_watch_func(int fd, uint32_t wd);
+
+inotify_init_func* my_inotify_init_func = NULL;
+inotify_add_watch_func* my_inotify_add_watch_func = NULL;
+inotify_rm_watch_func* my_inotify_rm_watch_func = NULL;
+
+static void throwUnixException(JNIEnv* env, int errnum) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
+ "(I)V", errnum);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxWatchService_init(JNIEnv *env, jclass clazz)
+{
+ my_inotify_init_func = (inotify_init_func*)
+ dlsym(RTLD_DEFAULT, "inotify_init");
+ my_inotify_add_watch_func =
+ (inotify_add_watch_func*) dlsym(RTLD_DEFAULT, "inotify_add_watch");
+ my_inotify_rm_watch_func =
+ (inotify_rm_watch_func*) dlsym(RTLD_DEFAULT, "inotify_rm_watch");
+
+ if ((my_inotify_init_func == NULL) || (my_inotify_add_watch_func == NULL) ||
+ (my_inotify_rm_watch_func == NULL)) {
+ JNU_ThrowInternalError(env, "unable to get address of inotify functions");
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_LinuxWatchService_eventSize(JNIEnv *env, jclass clazz)
+{
+ return (jint)sizeof(struct inotify_event);
+}
+
+JNIEXPORT jintArray JNICALL
+Java_sun_nio_fs_LinuxWatchService_eventOffsets(JNIEnv *env, jclass clazz)
+{
+ jintArray result = (*env)->NewIntArray(env, 5);
+ if (result != NULL) {
+ jint arr[5];
+ arr[0] = (jint)offsetof(struct inotify_event, wd);
+ arr[1] = (jint)offsetof(struct inotify_event, mask);
+ arr[2] = (jint)offsetof(struct inotify_event, cookie);
+ arr[3] = (jint)offsetof(struct inotify_event, len);
+ arr[4] = (jint)offsetof(struct inotify_event, name);
+ (*env)->SetIntArrayRegion(env, result, 0, 5, arr);
+ }
+ return result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_LinuxWatchService_inotifyInit
+ (JNIEnv* env, jclass clazz)
+{
+ int ifd = (*my_inotify_init_func)();
+ if (ifd == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)ifd;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_LinuxWatchService_inotifyAddWatch
+ (JNIEnv* env, jclass clazz, jint fd, jlong address, jint mask)
+{
+ int wfd = -1;
+ const char* path = (const char*)jlong_to_ptr(address);
+
+ wfd = (*my_inotify_add_watch_func)((int)fd, path, mask);
+ if (wfd == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)wfd;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxWatchService_inotifyRmWatch
+ (JNIEnv* env, jclass clazz, jint fd, jint wd)
+{
+ int err = (*my_inotify_rm_watch_func)((int)fd, (int)wd);
+ if (err == -1)
+ throwUnixException(env, errno);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxWatchService_configureBlocking
+ (JNIEnv* env, jclass clazz, jint fd, jboolean blocking)
+{
+ int flags = fcntl(fd, F_GETFL);
+
+ if ((blocking == JNI_FALSE) && !(flags & O_NONBLOCK))
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ else if ((blocking == JNI_TRUE) && (flags & O_NONBLOCK))
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_LinuxWatchService_socketpair
+ (JNIEnv* env, jclass clazz, jintArray sv)
+{
+ int sp[2];
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
+ throwUnixException(env, errno);
+ } else {
+ jint res[2];
+ res[0] = (jint)sp[0];
+ res[1] = (jint)sp[1];
+ (*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
+ }
+
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_LinuxWatchService_poll
+ (JNIEnv* env, jclass clazz, jint fd1, jint fd2)
+{
+ struct pollfd ufds[2];
+ int n;
+
+ ufds[0].fd = fd1;
+ ufds[0].events = POLLIN;
+ ufds[1].fd = fd2;
+ ufds[1].events = POLLIN;
+
+ n = poll(&ufds[0], 2, -1);
+ if (n == -1) {
+ if (errno == EINTR) {
+ n = 0;
+ } else {
+ throwUnixException(env, errno);
+ }
+ }
+ return (jint)n;
+
+
+}
diff --git a/src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c b/src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c
new file mode 100644
index 000000000..a57009c67
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <sys/acl.h>
+
+#include "sun_nio_fs_SolarisNativeDispatcher.h"
+
+static void throwUnixException(JNIEnv* env, int errnum) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
+ "(I)V", errnum);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_SolarisNativeDispatcher_init(JNIEnv *env, jclass clazz) {
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_SolarisNativeDispatcher_facl(JNIEnv* env, jclass this, jint fd,
+ jint cmd, jint nentries, jlong address)
+{
+ void* aclbufp = jlong_to_ptr(address);
+ int n = -1;
+
+ n = facl((int)fd, (int)cmd, (int)nentries, aclbufp);
+ if (n == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)n;
+}
diff --git a/src/solaris/native/sun/nio/fs/SolarisWatchService.c b/src/solaris/native/sun/nio/fs/SolarisWatchService.c
new file mode 100644
index 000000000..776227f99
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/SolarisWatchService.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <port.h> // Solaris 10
+
+#include "sun_nio_fs_SolarisWatchService.h"
+
+static void throwUnixException(JNIEnv* env, int errnum) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
+ "(I)V", errnum);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_SolarisWatchService_init(JNIEnv *env, jclass clazz)
+{
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_SolarisWatchService_portCreate
+ (JNIEnv* env, jclass clazz)
+{
+ int port = port_create();
+ if (port == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)port;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_SolarisWatchService_portAssociate
+ (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress, jint events)
+{
+ uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress);
+
+ if (port_associate((int)port, (int)source, object, (int)events, NULL) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_SolarisWatchService_portDissociate
+ (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress)
+{
+ uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress);
+
+ if (port_dissociate((int)port, (int)source, object) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_SolarisWatchService_portSend(JNIEnv* env, jclass clazz,
+ jint port, jint events)
+{
+ if (port_send((int)port, (int)events, NULL) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_SolarisWatchService_portGetn(JNIEnv* env, jclass clazz,
+ jint port, jlong arrayAddress, jint max)
+{
+ uint_t n = 1;
+ port_event_t* list = (port_event_t*)jlong_to_ptr(arrayAddress);
+
+ if (port_getn((int)port, list, (uint_t)max, &n, NULL) == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)n;
+}
diff --git a/src/solaris/native/sun/nio/fs/UnixCopyFile.c b/src/solaris/native/sun/nio/fs/UnixCopyFile.c
new file mode 100644
index 000000000..526ccba96
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/UnixCopyFile.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "sun_nio_fs_UnixCopyFile.h"
+
+#define RESTARTABLE(_cmd, _result) do { \
+ do { \
+ _result = _cmd; \
+ } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+static void throwUnixException(JNIEnv* env, int errnum) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
+ "(I)V", errnum);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+/**
+ * Transfer all bytes from src to dst via user-space buffers
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixCopyFile_transfer
+ (JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress)
+{
+ char buf[8192];
+ volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
+
+ for (;;) {
+ ssize_t n, pos, len;
+ RESTARTABLE(read((int)src, &buf, sizeof(buf)), n);
+ if (n <= 0) {
+ if (n < 0)
+ throwUnixException(env, errno);
+ return;
+ }
+ if (cancel != NULL && *cancel != 0) {
+ throwUnixException(env, ECANCELED);
+ return;
+ }
+ pos = 0;
+ len = n;
+ do {
+ char* bufp = buf;
+ bufp += pos;
+ RESTARTABLE(write((int)dst, bufp, len), n);
+ if (n == -1) {
+ throwUnixException(env, errno);
+ return;
+ }
+ pos += n;
+ len -= n;
+ } while (len > 0);
+ }
+}
diff --git a/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c b/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c
new file mode 100644
index 000000000..13a1a3a21
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+
+#ifdef __solaris__
+#include <strings.h>
+#include <sys/mnttab.h>
+#include <sys/mkdev.h>
+#endif
+
+#ifdef __linux__
+#include <string.h>
+#include <mntent.h>
+#endif
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+
+#include "sun_nio_fs_UnixNativeDispatcher.h"
+
+#define RESTARTABLE(_cmd, _result) do { \
+ do { \
+ _result = _cmd; \
+ } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+static jfieldID attrs_st_mode;
+static jfieldID attrs_st_ino;
+static jfieldID attrs_st_dev;
+static jfieldID attrs_st_rdev;
+static jfieldID attrs_st_nlink;
+static jfieldID attrs_st_uid;
+static jfieldID attrs_st_gid;
+static jfieldID attrs_st_size;
+static jfieldID attrs_st_atime;
+static jfieldID attrs_st_mtime;
+static jfieldID attrs_st_ctime;
+
+static jfieldID attrs_f_frsize;
+static jfieldID attrs_f_blocks;
+static jfieldID attrs_f_bfree;
+static jfieldID attrs_f_bavail;
+
+static jfieldID entry_name;
+static jfieldID entry_dir;
+static jfieldID entry_fstype;
+static jfieldID entry_options;
+static jfieldID entry_dev;
+
+/**
+ * System calls that may not be available at build time.
+ */
+typedef int openat64_func(int, const char *, int, ...);
+typedef int fstatat64_func(int, const char *, struct stat64 *, int);
+typedef int unlinkat_func(int, const char*, int);
+typedef int renameat_func(int, const char*, int, const char*);
+typedef int futimesat_func(int, const char *, const struct timeval *);
+typedef DIR* fdopendir_func(int);
+
+static openat64_func* my_openat64_func = NULL;
+static fstatat64_func* my_fstatat64_func = NULL;
+static unlinkat_func* my_unlinkat_func = NULL;
+static renameat_func* my_renameat_func = NULL;
+static futimesat_func* my_futimesat_func = NULL;
+static fdopendir_func* my_fdopendir_func = NULL;
+
+/**
+ * fstatat missing from glibc on Linux. Temporary workaround
+ * for x86/x64.
+ */
+#if defined(__linux__) && defined(__i386)
+#define FSTATAT64_SYSCALL_AVAILABLE
+static int fstatat64_wrapper(int dfd, const char *path,
+ struct stat64 *statbuf, int flag)
+{
+ #ifndef __NR_fstatat64
+ #define __NR_fstatat64 300
+ #endif
+ return syscall(__NR_fstatat64, dfd, path, statbuf, flag);
+}
+#endif
+
+#if defined(__linux__) && defined(__x86_64__)
+#define FSTATAT64_SYSCALL_AVAILABLE
+static int fstatat64_wrapper(int dfd, const char *path,
+ struct stat64 *statbuf, int flag)
+{
+ #ifndef __NR_newfstatat
+ #define __NR_newfstatat 262
+ #endif
+ return syscall(__NR_newfstatat, dfd, path, statbuf, flag);
+}
+#endif
+
+/**
+ * Call this to throw an internal UnixException when a system/library
+ * call fails
+ */
+static void throwUnixException(JNIEnv* env, int errnum) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
+ "(I)V", errnum);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+/**
+ * Initialize jfieldIDs
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_initIDs(JNIEnv* env, jclass this)
+{
+ jclass clazz;
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileAttributes");
+ if (clazz == NULL) {
+ return;
+ }
+ attrs_st_mode = (*env)->GetFieldID(env, clazz, "st_mode", "I");
+ attrs_st_ino = (*env)->GetFieldID(env, clazz, "st_ino", "J");
+ attrs_st_dev = (*env)->GetFieldID(env, clazz, "st_dev", "J");
+ attrs_st_rdev = (*env)->GetFieldID(env, clazz, "st_rdev", "J");
+ attrs_st_nlink = (*env)->GetFieldID(env, clazz, "st_nlink", "I");
+ attrs_st_uid = (*env)->GetFieldID(env, clazz, "st_uid", "I");
+ attrs_st_gid = (*env)->GetFieldID(env, clazz, "st_gid", "I");
+ attrs_st_size = (*env)->GetFieldID(env, clazz, "st_size", "J");
+ attrs_st_atime = (*env)->GetFieldID(env, clazz, "st_atime", "J");
+ attrs_st_mtime = (*env)->GetFieldID(env, clazz, "st_mtime", "J");
+ attrs_st_ctime = (*env)->GetFieldID(env, clazz, "st_ctime", "J");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes");
+ if (clazz == NULL) {
+ return;
+ }
+ attrs_f_frsize = (*env)->GetFieldID(env, clazz, "f_frsize", "J");
+ attrs_f_blocks = (*env)->GetFieldID(env, clazz, "f_blocks", "J");
+ attrs_f_bfree = (*env)->GetFieldID(env, clazz, "f_bfree", "J");
+ attrs_f_bavail = (*env)->GetFieldID(env, clazz, "f_bavail", "J");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
+ if (clazz == NULL) {
+ return;
+ }
+ entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
+ entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
+ entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
+ entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
+ entry_dev = (*env)->GetFieldID(env, clazz, "dev", "J");
+
+ /* system calls that might not be available at build time */
+
+#if defined(__solaris__) && defined(_LP64)
+ /* Solaris 64-bit does not have openat64/fstatat64 */
+ my_openat64_func = (openat64_func*)dlsym(RTLD_DEFAULT, "openat");
+ my_fstatat64_func = (fstatat64_func*)dlsym(RTLD_DEFAULT, "fstatat");
+#else
+ my_openat64_func = (openat64_func*) dlsym(RTLD_DEFAULT, "openat64");
+ my_fstatat64_func = (fstatat64_func*) dlsym(RTLD_DEFAULT, "fstatat64");
+#endif
+ my_unlinkat_func = (unlinkat_func*) dlsym(RTLD_DEFAULT, "unlinkat");
+ my_renameat_func = (renameat_func*) dlsym(RTLD_DEFAULT, "renameat");
+ my_futimesat_func = (futimesat_func*) dlsym(RTLD_DEFAULT, "futimesat");
+ my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir");
+
+#if defined(FSTATAT64_SYSCALL_AVAILABLE)
+ /* fstatat64 missing from glibc */
+ if (my_fstatat64_func == NULL)
+ my_fstatat64_func = (fstatat64_func*)&fstatat64_wrapper;
+#endif
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_getcwd(JNIEnv* env, jclass this) {
+ jbyteArray result = NULL;
+ char buf[PATH_MAX+1];
+
+ /* EINTR not listed as a possible error */
+ char* cwd = getcwd(buf, sizeof(buf));
+ if (cwd == NULL) {
+ throwUnixException(env, errno);
+ } else {
+ jsize len = (jsize)strlen(buf);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)buf);
+ }
+ }
+ return result;
+}
+
+JNIEXPORT jbyteArray
+Java_sun_nio_fs_UnixNativeDispatcher_strerror(JNIEnv* env, jclass this, jint error)
+{
+ char* msg;
+ jsize len;
+ jbyteArray bytes;
+
+ msg = strerror((int)error);
+ len = strlen(msg);
+ bytes = (*env)->NewByteArray(env, len);
+ if (bytes != NULL) {
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)msg);
+ }
+ return bytes;
+}
+
+JNIEXPORT jint
+Java_sun_nio_fs_UnixNativeDispatcher_dup(JNIEnv* env, jclass this, jint fd) {
+
+ int res = -1;
+
+ RESTARTABLE(dup((int)fd), res);
+ if (fd == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)res;
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fopen0(JNIEnv* env, jclass this,
+ jlong pathAddress, jlong modeAddress)
+{
+ FILE* fp = NULL;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+ const char* mode = (const char*)jlong_to_ptr(modeAddress);
+
+ do {
+ fp = fopen(path, mode);
+ } while (fp == NULL && errno == EINTR);
+
+ if (fp == NULL) {
+ throwUnixException(env, errno);
+ }
+
+ return ptr_to_jlong(fp);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fclose(JNIEnv* env, jclass this, jlong stream)
+{
+ int res;
+ FILE* fp = jlong_to_ptr(stream);
+
+ do {
+ res = fclose(fp);
+ } while (res == EOF && errno == EINTR);
+ if (res == EOF) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_open0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint oflags, jint mode)
+{
+ jint fd;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(open64(path, (int)oflags, (mode_t)mode), fd);
+ if (fd == -1) {
+ throwUnixException(env, errno);
+ }
+ return fd;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_openat0(JNIEnv* env, jclass this, jint dfd,
+ jlong pathAddress, jint oflags, jint mode)
+{
+ jint fd;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ if (my_openat64_func == NULL) {
+ JNU_ThrowInternalError(env, "should not reach here");
+ return -1;
+ }
+
+ RESTARTABLE((*my_openat64_func)(dfd, path, (int)oflags, (mode_t)mode), fd);
+ if (fd == -1) {
+ throwUnixException(env, errno);
+ }
+ return fd;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_close(JNIEnv* env, jclass this, jint fd) {
+ int err;
+ /* TDB - need to decide if EIO and other errors should cause exception */
+ RESTARTABLE(close((int)fd), err);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_read(JNIEnv* env, jclass this, jint fd,
+ jlong address, jint nbytes)
+{
+ ssize_t n;
+ void* bufp = jlong_to_ptr(address);
+ RESTARTABLE(read((int)fd, bufp, (size_t)nbytes), n);
+ if (n == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)n;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_write(JNIEnv* env, jclass this, jint fd,
+ jlong address, jint nbytes)
+{
+ ssize_t n;
+ void* bufp = jlong_to_ptr(address);
+ RESTARTABLE(write((int)fd, bufp, (size_t)nbytes), n);
+ if (n == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jint)n;
+}
+
+/**
+ * Copy stat64 members into sun.nio.fs.UnixFileAttributes
+ */
+static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
+ (*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->st_mode);
+ (*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->st_ino);
+ (*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)buf->st_dev);
+ (*env)->SetLongField(env, attrs, attrs_st_rdev, (jlong)buf->st_rdev);
+ (*env)->SetIntField(env, attrs, attrs_st_nlink, (jint)buf->st_nlink);
+ (*env)->SetIntField(env, attrs, attrs_st_uid, (jint)buf->st_uid);
+ (*env)->SetIntField(env, attrs, attrs_st_gid, (jint)buf->st_gid);
+ (*env)->SetLongField(env, attrs, attrs_st_size, (jlong)buf->st_size);
+ (*env)->SetLongField(env, attrs, attrs_st_atime, (jlong)buf->st_atime * 1000);
+ (*env)->SetLongField(env, attrs, attrs_st_mtime, (jlong)buf->st_mtime * 1000);
+ (*env)->SetLongField(env, attrs, attrs_st_ctime, (jlong)buf->st_ctime * 1000);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this,
+ jlong pathAddress, jobject attrs)
+{
+ int err;
+ struct stat64 buf;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(stat64(path, &buf), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ } else {
+ prepAttributes(env, &buf, attrs);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_lstat0(JNIEnv* env, jclass this,
+ jlong pathAddress, jobject attrs)
+{
+ int err;
+ struct stat64 buf;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(lstat64(path, &buf), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ } else {
+ prepAttributes(env, &buf, attrs);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fstat(JNIEnv* env, jclass this, jint fd,
+ jobject attrs)
+{
+ int err;
+ struct stat64 buf;
+
+ RESTARTABLE(fstat64((int)fd, &buf), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ } else {
+ prepAttributes(env, &buf, attrs);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd,
+ jlong pathAddress, jint flag, jobject attrs)
+{
+ int err;
+ struct stat64 buf;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ if (my_fstatat64_func == NULL) {
+ JNU_ThrowInternalError(env, "should not reach here");
+ return;
+ }
+ RESTARTABLE((*my_fstatat64_func)((int)dfd, path, &buf, (int)flag), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ } else {
+ prepAttributes(env, &buf, attrs);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_chmod0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint mode)
+{
+ int err;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(chmod(path, (mode_t)mode), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fchmod(JNIEnv* env, jclass this, jint filedes,
+ jint mode)
+{
+ int err;
+
+ RESTARTABLE(fchmod((int)filedes, (mode_t)mode), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_chown0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint uid, jint gid)
+{
+ int err;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(chown(path, (uid_t)uid, (gid_t)gid), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_lchown0(JNIEnv* env, jclass this, jlong pathAddress, jint uid, jint gid)
+{
+ int err;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(lchown(path, (uid_t)uid, (gid_t)gid), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fchown(JNIEnv* env, jclass this, jint filedes, jint uid, jint gid)
+{
+ int err;
+
+ RESTARTABLE(fchown(filedes, (uid_t)uid, (gid_t)gid), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_utimes0(JNIEnv* env, jclass this,
+ jlong pathAddress, jlong accessTime, jlong modificationTime)
+{
+ int err;
+ struct timeval times[2];
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ times[0].tv_sec = accessTime / 1000;
+ times[0].tv_usec = (accessTime % 1000) * 1000;
+
+ times[1].tv_sec = modificationTime / 1000;
+ times[1].tv_usec = (modificationTime % 1000) * 1000;
+
+ RESTARTABLE(utimes(path, &times[0]), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_futimes(JNIEnv* env, jclass this, jint filedes,
+ jlong accessTime, jlong modificationTime)
+{
+ struct timeval times[2];
+ int err = 0;
+
+ times[0].tv_sec = accessTime / 1000;
+ times[0].tv_usec = (accessTime % 1000) * 1000;
+
+ times[1].tv_sec = modificationTime / 1000;
+ times[1].tv_usec = (modificationTime % 1000) * 1000;
+
+ if (my_futimesat_func != NULL) {
+ RESTARTABLE((*my_futimesat_func)(filedes, NULL, &times[0]), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+ }
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_opendir0(JNIEnv* env, jclass this,
+ jlong pathAddress)
+{
+ DIR* dir;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ /* EINTR not listed as a possible error */
+ dir = opendir(path);
+ if (dir == NULL) {
+ throwUnixException(env, errno);
+ }
+ return ptr_to_jlong(dir);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fdopendir(JNIEnv* env, jclass this, int dfd) {
+ DIR* dir;
+
+ if (my_fdopendir_func == NULL) {
+ JNU_ThrowInternalError(env, "should not reach here");
+ return (jlong)-1;
+ }
+
+ /* EINTR not listed as a possible error */
+ dir = (*my_fdopendir_func)((int)dfd);
+ if (dir == NULL) {
+ throwUnixException(env, errno);
+ }
+ return ptr_to_jlong(dir);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_closedir(JNIEnv* env, jclass this, jlong dir) {
+ int err;
+ DIR* dirp = jlong_to_ptr(dir);
+
+ RESTARTABLE(closedir(dirp), err);
+ if (errno == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_readdir(JNIEnv* env, jclass this, jlong value) {
+ char entry[sizeof(struct dirent64) + PATH_MAX + 1];
+ struct dirent64* ptr = (struct dirent64*)&entry;
+ struct dirent64* result;
+ int res;
+ DIR* dirp = jlong_to_ptr(value);
+
+ /* EINTR not listed as a possible error */
+ /* TDB: reentrant version probably not required here */
+ res = readdir64_r(dirp, ptr, &result);
+ if (res != 0) {
+ throwUnixException(env, res);
+ return NULL;
+ } else {
+ if (result == NULL) {
+ return NULL;
+ } else {
+ jsize len = strlen(ptr->d_name);
+ jbyteArray bytes = (*env)->NewByteArray(env, len);
+ if (bytes != NULL) {
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)(ptr->d_name));
+ }
+ return bytes;
+ }
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_mkdir0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint mode)
+{
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ /* EINTR not listed as a possible error */
+ if (mkdir(path, (mode_t)mode) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_rmdir0(JNIEnv* env, jclass this,
+ jlong pathAddress)
+{
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ /* EINTR not listed as a possible error */
+ if (rmdir(path) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_link0(JNIEnv* env, jclass this,
+ jlong existingAddress, jlong newAddress)
+{
+ int err;
+ const char* existing = (const char*)jlong_to_ptr(existingAddress);
+ const char* newname = (const char*)jlong_to_ptr(newAddress);
+
+ RESTARTABLE(link(existing, newname), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_unlink0(JNIEnv* env, jclass this,
+ jlong pathAddress)
+{
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ /* EINTR not listed as a possible error */
+ if (unlink(path) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_unlinkat0(JNIEnv* env, jclass this, jint dfd,
+ jlong pathAddress, jint flags)
+{
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ if (my_unlinkat_func == NULL) {
+ JNU_ThrowInternalError(env, "should not reach here");
+ return;
+ }
+
+ /* EINTR not listed as a possible error */
+ if ((*my_unlinkat_func)((int)dfd, path, (int)flags) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_rename0(JNIEnv* env, jclass this,
+ jlong fromAddress, jlong toAddress)
+{
+ const char* from = (const char*)jlong_to_ptr(fromAddress);
+ const char* to = (const char*)jlong_to_ptr(toAddress);
+
+ /* EINTR not listed as a possible error */
+ if (rename(from, to) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_renameat0(JNIEnv* env, jclass this,
+ jint fromfd, jlong fromAddress, jint tofd, jlong toAddress)
+{
+ const char* from = (const char*)jlong_to_ptr(fromAddress);
+ const char* to = (const char*)jlong_to_ptr(toAddress);
+
+ if (my_renameat_func == NULL) {
+ JNU_ThrowInternalError(env, "should not reach here");
+ return;
+ }
+
+ /* EINTR not listed as a possible error */
+ if ((*my_renameat_func)((int)fromfd, from, (int)tofd, to) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_symlink0(JNIEnv* env, jclass this,
+ jlong targetAddress, jlong linkAddress)
+{
+ const char* target = (const char*)jlong_to_ptr(targetAddress);
+ const char* link = (const char*)jlong_to_ptr(linkAddress);
+
+ /* EINTR not listed as a possible error */
+ if (symlink(target, link) == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_readlink0(JNIEnv* env, jclass this,
+ jlong pathAddress)
+{
+ jbyteArray result = NULL;
+ char target[PATH_MAX+1];
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ /* EINTR not listed as a possible error */
+ int n = readlink(path, target, sizeof(target));
+ if (n == -1) {
+ throwUnixException(env, errno);
+ } else {
+ jsize len;
+ if (n == sizeof(target)) {
+ n--;
+ }
+ target[n] = '\0';
+ len = (jsize)strlen(target);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)target);
+ }
+ }
+ return result;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_realpath0(JNIEnv* env, jclass this,
+ jlong pathAddress)
+{
+ jbyteArray result = NULL;
+ char resolved[PATH_MAX+1];
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ /* EINTR not listed as a possible error */
+ if (realpath(path, resolved) == NULL) {
+ throwUnixException(env, errno);
+ } else {
+ jsize len = (jsize)strlen(resolved);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)resolved);
+ }
+ }
+ return result;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_access0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint amode)
+{
+ int err;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(access(path, (int)amode), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_statvfs0(JNIEnv* env, jclass this,
+ jlong pathAddress, jobject attrs)
+{
+ int err;
+ struct statvfs64 buf;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+
+ RESTARTABLE(statvfs64(path, &buf), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ } else {
+ (*env)->SetLongField(env, attrs, attrs_f_frsize, long_to_jlong(buf.f_frsize));
+ (*env)->SetLongField(env, attrs, attrs_f_blocks, long_to_jlong(buf.f_blocks));
+ (*env)->SetLongField(env, attrs, attrs_f_bfree, long_to_jlong(buf.f_bfree));
+ (*env)->SetLongField(env, attrs, attrs_f_bavail, long_to_jlong(buf.f_bavail));
+ }
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_pathconf0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint name)
+{
+ long err;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ err = pathconf(path, (int)name);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jlong)err;
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fpathconf(JNIEnv* env, jclass this,
+ jint fd, jint name)
+{
+ long err;
+
+ err = fpathconf((int)fd, (int)name);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+ return (jlong)err;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_mknod0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint mode, jlong dev)
+{
+ int err;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ RESTARTABLE(mknod(path, (mode_t)mode, (dev_t)dev), err);
+ if (err == -1) {
+ throwUnixException(env, errno);
+ }
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_getpwuid(JNIEnv* env, jclass this, jint uid)
+{
+ jbyteArray result = NULL;
+ int buflen;
+
+ buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (buflen == -1) {
+ throwUnixException(env, errno);
+ } else {
+ char* pwbuf = (char*)malloc(buflen);
+ if (pwbuf == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "native heap");
+ } else {
+ struct passwd pwent;
+ struct passwd* p;
+ int res = 0;
+
+#ifdef __solaris__
+ p = getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen);
+#else
+ res = getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen, &p);
+#endif
+
+ if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') {
+ throwUnixException(env, errno);
+ } else {
+ jsize len = strlen(p->pw_name);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(p->pw_name));
+ }
+ }
+ free(pwbuf);
+ }
+ }
+ return result;
+}
+
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid)
+{
+ jbyteArray result = NULL;
+ int buflen;
+
+ buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (buflen == -1) {
+ throwUnixException(env, errno);
+ } else {
+ char* grbuf = (char*)malloc(buflen);
+ if (grbuf == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "native heap");
+ } else {
+ struct group grent;
+ struct group* g;
+ int res = 0;
+
+#ifdef __solaris__
+ g = getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen);
+#else
+ res = getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen, &g);
+#endif
+ if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') {
+ throwUnixException(env, errno);
+ } else {
+ jsize len = strlen(g->gr_name);
+ result = (*env)->NewByteArray(env, len);
+ if (result != NULL) {
+ (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(g->gr_name));
+ }
+ }
+ free(grbuf);
+ }
+ }
+ return result;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_getpwnam0(JNIEnv* env, jclass this,
+ jlong nameAddress)
+{
+ jint uid = -1;
+ int buflen;
+ char* pwbuf;
+ struct passwd pwent;
+ struct passwd* p;
+ int res = 0;
+ const char* name = (const char*)jlong_to_ptr(nameAddress);
+
+ buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (buflen == -1) {
+ throwUnixException(env, errno);
+ return -1;
+ }
+ pwbuf = (char*)malloc(buflen);
+ if (pwbuf == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "native heap");
+ return -1;
+ }
+
+#ifdef __solaris__
+ p = getpwnam_r(name, &pwent, pwbuf, (size_t)buflen);
+#else
+ res = getpwnam_r(name, &pwent, pwbuf, (size_t)buflen, &p);
+#endif
+
+ if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') {
+ /* not found or error */
+ } else {
+ uid = p->pw_uid;
+ }
+
+ free(pwbuf);
+
+ return uid;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0(JNIEnv* env, jclass this,
+ jlong nameAddress)
+{
+ jint gid = -1;
+ int buflen;
+ char* grbuf;
+ struct group grent;
+ struct group* g;
+ int res = 0;
+ const char* name = (const char*)jlong_to_ptr(nameAddress);
+
+ buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (buflen == -1) {
+ throwUnixException(env, errno);
+ return -1;
+ }
+ grbuf = (char*)malloc(buflen);
+ if (grbuf == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "native heap");
+ return -1;
+ }
+
+#ifdef __solaris__
+ g = getgrnam_r(name, &grent, grbuf, (size_t)buflen);
+#else
+ res = getgrnam_r(name, &grent, grbuf, (size_t)buflen, &g);
+#endif
+
+ if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') {
+ /* not found or error */
+ } else {
+ gid = g->gr_gid;
+ }
+ free(grbuf);
+
+ return gid;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_getextmntent(JNIEnv* env, jclass this,
+ jlong value, jobject entry)
+{
+#ifdef __solaris__
+ struct extmnttab ent;
+#else
+ struct mntent ent;
+ char buf[1024];
+ int buflen = sizeof(buf);
+ struct mntent* m;
+#endif
+ FILE* fp = jlong_to_ptr(value);
+ jsize len;
+ jbyteArray bytes;
+ char* name;
+ char* dir;
+ char* fstype;
+ char* options;
+ dev_t dev;
+
+#ifdef __solaris__
+ if (getextmntent(fp, &ent, 0))
+ return -1;
+ name = ent.mnt_special;
+ dir = ent.mnt_mountp;
+ fstype = ent.mnt_fstype;
+ options = ent.mnt_mntopts;
+ dev = makedev(ent.mnt_major, ent.mnt_minor);
+ if (dev == NODEV) {
+ /* possible bug on Solaris 8 and 9 */
+ throwUnixException(env, errno);
+ return -1;
+ }
+#else
+ m = getmntent_r(fp, &ent, (char*)&buf, buflen);
+ if (m == NULL)
+ return -1;
+ name = m->mnt_fsname;
+ dir = m->mnt_dir;
+ fstype = m->mnt_type;
+ options = m->mnt_opts;
+ dev = 0;
+#endif
+
+ len = strlen(name);
+ bytes = (*env)->NewByteArray(env, len);
+ if (bytes == NULL)
+ return -1;
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)name);
+ (*env)->SetObjectField(env, entry, entry_name, bytes);
+
+ len = strlen(dir);
+ bytes = (*env)->NewByteArray(env, len);
+ if (bytes == NULL)
+ return -1;
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)dir);
+ (*env)->SetObjectField(env, entry, entry_dir, bytes);
+
+ len = strlen(fstype);
+ bytes = (*env)->NewByteArray(env, len);
+ if (bytes == NULL)
+ return -1;
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype);
+ (*env)->SetObjectField(env, entry, entry_fstype, bytes);
+
+ len = strlen(options);
+ bytes = (*env)->NewByteArray(env, len);
+ if (bytes == NULL)
+ return -1;
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)options);
+ (*env)->SetObjectField(env, entry, entry_options, bytes);
+
+ if (dev != 0)
+ (*env)->SetLongField(env, entry, entry_dev, (jlong)dev);
+
+ return 0;
+}
diff --git a/src/solaris/native/sun/nio/fs/genSolarisConstants.c b/src/solaris/native/sun/nio/fs/genSolarisConstants.c
new file mode 100644
index 000000000..182449e4a
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/genSolarisConstants.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/acl.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+
+/**
+ * Generates sun.nio.fs.SolarisConstants
+ */
+
+static void out(char* s) {
+ printf("%s\n", s);
+}
+
+static void emit(char* name, int value) {
+ printf(" static final int %s = %d;\n", name, value);
+}
+
+static void emitX(char* name, int value) {
+ printf(" static final int %s = 0x%x;\n", name, value);
+}
+
+#define DEF(X) emit(#X, X);
+#define DEFX(X) emitX(#X, X);
+
+int main(int argc, const char* argv[]) {
+ out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT ");
+ out("package sun.nio.fs; ");
+ out("class SolarisConstants { ");
+ out(" private SolarisConstants() { } ");
+
+ // extended attributes
+ DEFX(O_XATTR);
+ DEF(_PC_XATTR_ENABLED);
+
+ // ACL configuration
+ DEF(_PC_ACL_ENABLED);
+ DEFX(_ACL_ACE_ENABLED);
+
+ // ACL commands
+ DEFX(ACE_GETACL);
+ DEFX(ACE_SETACL);
+
+ // ACL mask/flags/types
+ emitX("ACE_ACCESS_ALLOWED_ACE_TYPE", 0x0000);
+ emitX("ACE_ACCESS_DENIED_ACE_TYPE", 0x0001);
+ emitX("ACE_SYSTEM_AUDIT_ACE_TYPE", 0x0002);
+ emitX("ACE_SYSTEM_ALARM_ACE_TYPE", 0x0003);
+ emitX("ACE_READ_DATA", 0x00000001);
+ emitX("ACE_LIST_DIRECTORY", 0x00000001);
+ emitX("ACE_WRITE_DATA", 0x00000002);
+ emitX("ACE_ADD_FILE", 0x00000002);
+ emitX("ACE_APPEND_DATA", 0x00000004);
+ emitX("ACE_ADD_SUBDIRECTORY", 0x00000004);
+ emitX("ACE_READ_NAMED_ATTRS", 0x00000008);
+ emitX("ACE_WRITE_NAMED_ATTRS", 0x00000010);
+ emitX("ACE_EXECUTE", 0x00000020);
+ emitX("ACE_DELETE_CHILD", 0x00000040);
+ emitX("ACE_READ_ATTRIBUTES", 0x00000080);
+ emitX("ACE_WRITE_ATTRIBUTES", 0x00000100);
+ emitX("ACE_DELETE", 0x00010000);
+ emitX("ACE_READ_ACL", 0x00020000);
+ emitX("ACE_WRITE_ACL", 0x00040000);
+ emitX("ACE_WRITE_OWNER", 0x00080000);
+ emitX("ACE_SYNCHRONIZE", 0x00100000);
+ emitX("ACE_FILE_INHERIT_ACE", 0x0001);
+ emitX("ACE_DIRECTORY_INHERIT_ACE", 0x0002);
+ emitX("ACE_NO_PROPAGATE_INHERIT_ACE", 0x0004);
+ emitX("ACE_INHERIT_ONLY_ACE", 0x0008);
+ emitX("ACE_SUCCESSFUL_ACCESS_ACE_FLAG", 0x0010);
+ emitX("ACE_FAILED_ACCESS_ACE_FLAG", 0x0020);
+ emitX("ACE_IDENTIFIER_GROUP", 0x0040);
+ emitX("ACE_OWNER", 0x1000);
+ emitX("ACE_GROUP", 0x2000);
+ emitX("ACE_EVERYONE", 0x4000);
+
+ out("} ");
+ return 0;
+}
diff --git a/src/solaris/native/sun/nio/fs/genUnixConstants.c b/src/solaris/native/sun/nio/fs/genUnixConstants.c
new file mode 100644
index 000000000..c01f641a6
--- /dev/null
+++ b/src/solaris/native/sun/nio/fs/genUnixConstants.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+
+/**
+ * Generates sun.nio.fs.UnixConstants
+ */
+
+static void out(char* s) {
+ printf("%s\n", s);
+}
+
+static void emit(char* name, int value) {
+ printf(" static final int %s = %d;\n", name, value);
+}
+
+static void emitX(char* name, int value) {
+ printf(" static final int %s = 0x%x;\n", name, value);
+}
+
+#define DEF(X) emit(#X, X);
+#define DEFX(X) emitX(#X, X);
+
+int main(int argc, const char* argv[]) {
+ out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT ");
+ out("package sun.nio.fs; ");
+ out("class UnixConstants { ");
+ out(" private UnixConstants() { } ");
+
+ // open flags
+ DEF(O_RDONLY);
+ DEF(O_WRONLY);
+ DEF(O_RDWR);
+ DEFX(O_APPEND);
+ DEFX(O_CREAT);
+ DEFX(O_EXCL);
+ DEFX(O_TRUNC);
+ DEFX(O_SYNC);
+ DEFX(O_DSYNC);
+ DEFX(O_NOFOLLOW);
+
+ // flags used with openat/unlinkat/etc.
+#ifdef __solaris__
+ DEFX(AT_SYMLINK_NOFOLLOW);
+ DEFX(AT_REMOVEDIR);
+#endif
+#ifdef __linux__
+ emitX("AT_SYMLINK_NOFOLLOW", 0x100); // since 2.6.16
+ emitX("AT_REMOVEDIR", 0x200);
+#endif
+
+ // mode masks
+ emitX("S_IAMB",
+ (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH));
+ DEF(S_IRUSR);
+ DEF(S_IWUSR);
+ DEF(S_IXUSR);
+ DEF(S_IRGRP);
+ DEF(S_IWGRP);
+ DEF(S_IXGRP);
+ DEF(S_IROTH);
+ DEF(S_IWOTH);
+ DEF(S_IXOTH);
+ DEFX(S_IFMT);
+ DEFX(S_IFREG);
+ DEFX(S_IFDIR);
+ DEFX(S_IFLNK);
+ DEFX(S_IFCHR);
+ DEFX(S_IFBLK);
+ DEFX(S_IFIFO);
+
+ // access modes
+ DEF(R_OK);
+ DEF(W_OK);
+ DEF(X_OK);
+ DEF(F_OK);
+
+ // errors
+ DEF(ENOENT);
+ DEF(EACCES);
+ DEF(EEXIST);
+ DEF(ENOTDIR);
+ DEF(EINVAL);
+ DEF(EXDEV);
+ DEF(EISDIR);
+ DEF(ENOTEMPTY);
+ DEF(ENOSPC);
+ DEF(EAGAIN);
+ DEF(ENOSYS);
+ DEF(ELOOP);
+ DEF(EROFS);
+ DEF(ENODATA);
+ DEF(ERANGE);
+
+ out("} ");
+
+ return 0;
+}
diff --git a/src/solaris/native/sun/xawt/XlibWrapper.c b/src/solaris/native/sun/xawt/XlibWrapper.c
index 6801dd947..aee5cf9b0 100644
--- a/src/solaris/native/sun/xawt/XlibWrapper.c
+++ b/src/solaris/native/sun/xawt/XlibWrapper.c
@@ -1641,6 +1641,13 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11_XlibWrapper_XdbeSwapBuffers
AWT_CHECK_HAVE_LOCK();
return XdbeSwapBuffers((Display*) jlong_to_ptr(display), (XdbeSwapInfo *) jlong_to_ptr(swap_info), num_windows);
}
+JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XQueryKeymap
+(JNIEnv *env, jclass clazz, jlong display, jlong vector)
+{
+
+ AWT_CHECK_HAVE_LOCK();
+ XQueryKeymap( (Display *) jlong_to_ptr(display), (char *) jlong_to_ptr(vector));
+}
JNIEXPORT jlong JNICALL
Java_sun_awt_X11_XlibWrapper_XKeycodeToKeysym(JNIEnv *env, jclass clazz,
@@ -1911,19 +1918,30 @@ Java_sun_awt_X11_XlibWrapper_SetRectangularShape
jint x1, jint y1, jint x2, jint y2,
jobject region)
{
- XRectangle rects[256];
- XRectangle *pRect = rects;
- int numrects;
-
AWT_CHECK_HAVE_LOCK();
- numrects = RegionToYXBandedRectangles(env, x1, y1, x2, y2, region,
- &pRect, 256);
+ // If all the params are zeros, the shape must be simply reset.
+ // Otherwise, the shape may be not rectangular.
+ if (region || x1 || x2 || y1 || y2) {
+ XRectangle rects[256];
+ XRectangle *pRect = rects;
+
+ int numrects = RegionToYXBandedRectangles(env, x1, y1, x2, y2, region,
+ &pRect, 256);
- XShapeCombineRectangles((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window),
+ XShapeCombineRectangles((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window),
+ ShapeClip, 0, 0, pRect, numrects, ShapeSet, YXBanded);
+ XShapeCombineRectangles((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window),
ShapeBounding, 0, 0, pRect, numrects, ShapeSet, YXBanded);
- if (pRect != rects) {
- free(pRect);
+ if (pRect != rects) {
+ free(pRect);
+ }
+ } else {
+ // Reset the shape to a rectangular form.
+ XShapeCombineMask((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window),
+ ShapeClip, 0, 0, None, ShapeSet);
+ XShapeCombineMask((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window),
+ ShapeBounding, 0, 0, None, ShapeSet);
}
}
diff --git a/src/windows/classes/sun/awt/windows/WComponentPeer.java b/src/windows/classes/sun/awt/windows/WComponentPeer.java
index 0f71eda29..afbd170cc 100644
--- a/src/windows/classes/sun/awt/windows/WComponentPeer.java
+++ b/src/windows/classes/sun/awt/windows/WComponentPeer.java
@@ -68,13 +68,6 @@ public abstract class WComponentPeer extends WObjectPeer
private static final Logger log = Logger.getLogger("sun.awt.windows.WComponentPeer");
private static final Logger shapeLog = Logger.getLogger("sun.awt.windows.shape.WComponentPeer");
- static {
- wheelInit();
- }
-
- // Only actually does stuff if running on 95
- native static void wheelInit();
-
// ComponentPeer implementation
SurfaceData surfaceData;
@@ -964,8 +957,12 @@ public abstract class WComponentPeer extends WObjectPeer
+ "; SHAPE: " + shape);
}
- setRectangularShape(shape.getLoX(), shape.getLoY(), shape.getHiX(), shape.getHiY(),
- (shape.isRectangular() ? null : shape));
+ if (shape != null) {
+ setRectangularShape(shape.getLoX(), shape.getLoY(), shape.getHiX(), shape.getHiY(),
+ (shape.isRectangular() ? null : shape));
+ } else {
+ setRectangularShape(0, 0, 0, 0, null);
+ }
}
}
diff --git a/src/windows/classes/sun/awt/windows/WInputMethod.java b/src/windows/classes/sun/awt/windows/WInputMethod.java
index 33be7f8b4..6cec225db 100644
--- a/src/windows/classes/sun/awt/windows/WInputMethod.java
+++ b/src/windows/classes/sun/awt/windows/WInputMethod.java
@@ -548,11 +548,15 @@ public class WInputMethod extends InputMethodAdapter
public void inquireCandidatePosition()
{
+ Component source = getClientComponent();
+ if (source == null) {
+ return;
+ }
// This call should return immediately just to cause
// InputMethodRequests.getTextLocation be called within
// AWT Event thread. Otherwise, a potential deadlock
// could happen.
- java.awt.EventQueue.invokeLater(new Runnable() {
+ Runnable r = new Runnable() {
public void run() {
int x = 0;
int y = 0;
@@ -573,7 +577,9 @@ public class WInputMethod extends InputMethodAdapter
openCandidateWindow(awtFocussedComponentPeer, x, y);
}
- });
+ };
+ WToolkit.postEvent(WToolkit.targetToAppContext(source),
+ new InvocationEvent(source, r));
}
// java.awt.Toolkit#getNativeContainer() is not available
diff --git a/src/windows/classes/sun/awt/windows/WRobotPeer.java b/src/windows/classes/sun/awt/windows/WRobotPeer.java
index 1fb8ce2b5..4cd3d2d3c 100644
--- a/src/windows/classes/sun/awt/windows/WRobotPeer.java
+++ b/src/windows/classes/sun/awt/windows/WRobotPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2008 Sun Microsystems, Inc. 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
@@ -60,6 +60,8 @@ class WRobotPeer extends WObjectPeer implements RobotPeer
}
public native int getRGBPixelImpl(int x, int y);
+ public native int getNumberOfButtons();
+
public int [] getRGBPixels(Rectangle bounds) {
int pixelArray[] = new int[bounds.width*bounds.height];
getRGBPixels(bounds.x, bounds.y, bounds.width, bounds.height, pixelArray);
diff --git a/src/windows/classes/sun/awt/windows/WToolkit.java b/src/windows/classes/sun/awt/windows/WToolkit.java
index 5ea8ca3ac..55c1dde09 100644
--- a/src/windows/classes/sun/awt/windows/WToolkit.java
+++ b/src/windows/classes/sun/awt/windows/WToolkit.java
@@ -80,6 +80,10 @@ public class WToolkit extends SunToolkit implements Runnable {
// Dynamic Layout Resize client code setting
protected boolean dynamicLayoutSetting = false;
+ //Is it allowed to generate events assigned to extra mouse buttons.
+ //Set to true by default.
+ private static boolean areExtraMouseButtonsEnabled = true;
+
/**
* Initialize JNI field and method IDs
*/
@@ -249,6 +253,11 @@ public class WToolkit extends SunToolkit implements Runnable {
// Enabled "live resizing" by default. It remains controlled
// by the native system though.
setDynamicLayout(true);
+
+ areExtraMouseButtonsEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.enableExtraMouseButtons", "true"));
+ //set system property if not yet assigned
+ System.setProperty("sun.awt.enableExtraMouseButtons", ""+areExtraMouseButtonsEnabled);
+ setExtraMouseButtonsEnabledNative(areExtraMouseButtonsEnabled);
}
public void run() {
@@ -961,4 +970,9 @@ public class WToolkit extends SunToolkit implements Runnable {
return new WDesktopPeer();
}
+ public static native void setExtraMouseButtonsEnabledNative(boolean enable);
+
+ public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
+ return areExtraMouseButtonsEnabled;
+ }
}
diff --git a/src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
new file mode 100644
index 000000000..6187aa318
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+
+/**
+ * Creates this platform's default asynchronous channel provider
+ */
+
+public class DefaultAsynchronousChannelProvider {
+ private DefaultAsynchronousChannelProvider() { }
+
+ /**
+ * Returns the default AsynchronousChannelProvider.
+ */
+ public static AsynchronousChannelProvider create() {
+ return new WindowsAsynchronousChannelProvider();
+ }
+}
diff --git a/src/windows/classes/sun/nio/ch/FileDispatcher.java b/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java
index 692844966..30390f7a6 100644
--- a/src/windows/classes/sun/nio/ch/FileDispatcher.java
+++ b/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -27,13 +27,7 @@ package sun.nio.ch;
import java.io.*;
-
-/**
- * Allows different platforms to call different native methods
- * for read and write operations.
- */
-
-class FileDispatcher extends NativeDispatcher
+class FileDispatcherImpl extends FileDispatcher
{
static {
@@ -74,6 +68,28 @@ class FileDispatcher extends NativeDispatcher
return writev0(fd, address, len);
}
+ int force(FileDescriptor fd, boolean metaData) throws IOException {
+ return force0(fd, metaData);
+ }
+
+ int truncate(FileDescriptor fd, long size) throws IOException {
+ return truncate0(fd, size);
+ }
+
+ long size(FileDescriptor fd) throws IOException {
+ return size0(fd);
+ }
+
+ int lock(FileDescriptor fd, boolean blocking, long pos, long size,
+ boolean shared) throws IOException
+ {
+ return lock0(fd, blocking, pos, size, shared);
+ }
+
+ void release(FileDescriptor fd, long pos, long size) throws IOException {
+ release0(fd, pos, size);
+ }
+
void close(FileDescriptor fd) throws IOException {
close0(fd);
}
@@ -98,6 +114,20 @@ class FileDispatcher extends NativeDispatcher
static native long writev0(FileDescriptor fd, long address, int len)
throws IOException;
+ static native int force0(FileDescriptor fd, boolean metaData)
+ throws IOException;
+
+ static native int truncate0(FileDescriptor fd, long size)
+ throws IOException;
+
+ static native long size0(FileDescriptor fd) throws IOException;
+
+ static native int lock0(FileDescriptor fd, boolean blocking, long pos,
+ long size, boolean shared) throws IOException;
+
+ static native void release0(FileDescriptor fd, long pos, long size)
+ throws IOException;
+
static native void close0(FileDescriptor fd) throws IOException;
static native void closeByHandle(long fd) throws IOException;
diff --git a/src/windows/classes/sun/nio/ch/Iocp.java b/src/windows/classes/sun/nio/ch/Iocp.java
new file mode 100644
index 000000000..b4c2cdef6
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/Iocp.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import sun.misc.Unsafe;
+
+/**
+ * Windows implementation of AsynchronousChannelGroup encapsulating an I/O
+ * completion port.
+ */
+
+class Iocp extends AsynchronousChannelGroupImpl {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static final long INVALID_HANDLE_VALUE = -1L;
+
+ // maps completion key to channel
+ private final ReadWriteLock keyToChannelLock = new ReentrantReadWriteLock();
+ private final Map<Integer,OverlappedChannel> keyToChannel =
+ new HashMap<Integer,OverlappedChannel>();
+ private int nextCompletionKey;
+
+ // handle to completion port
+ private final long port;
+
+ // true if port has been closed
+ private boolean closed;
+
+ // the set of "stale" OVERLAPPED structures. These OVERLAPPED structures
+ // relate to I/O operations where the completion notification was not
+ // received in a timely manner after the channel is closed.
+ private final Set<Long> staleIoSet = new HashSet<Long>();
+
+ Iocp(AsynchronousChannelProvider provider, ThreadPool pool)
+ throws IOException
+ {
+ super(provider, pool);
+ this.port =
+ createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, fixedThreadCount());
+ this.nextCompletionKey = 1;
+ }
+
+ Iocp start() {
+ startThreads(new EventHandlerTask());
+ return this;
+ }
+
+ /*
+ * Channels implements this interface support overlapped I/O and can be
+ * associated with a completion port.
+ */
+ static interface OverlappedChannel extends Closeable {
+ /**
+ * Returns a reference to the pending I/O result.
+ */
+ <V,A> PendingFuture<V,A> getByOverlapped(long overlapped);
+ }
+
+ // release all resources
+ void implClose() {
+ synchronized (this) {
+ if (closed)
+ return;
+ closed = true;
+ }
+ close0(port);
+ synchronized (staleIoSet) {
+ for (Long ov: staleIoSet) {
+ unsafe.freeMemory(ov);
+ }
+ staleIoSet.clear();
+ }
+ }
+
+ @Override
+ boolean isEmpty() {
+ keyToChannelLock.writeLock().lock();
+ try {
+ return keyToChannel.isEmpty();
+ } finally {
+ keyToChannelLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ final Object attachForeignChannel(final Channel channel, FileDescriptor fdObj)
+ throws IOException
+ {
+ int key = associate(new OverlappedChannel() {
+ public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
+ return null;
+ }
+ public void close() throws IOException {
+ channel.close();
+ }
+ }, 0L);
+ return Integer.valueOf(key);
+ }
+
+ @Override
+ final void detachForeignChannel(Object key) {
+ disassociate((Integer)key);
+ }
+
+ @Override
+ void closeAllChannels() {
+ /**
+ * On Windows the close operation will close the socket/file handle
+ * and then wait until all outstanding I/O operations have aborted.
+ * This is necessary as each channel's cache of OVERLAPPED structures
+ * can only be freed once all I/O operations have completed. As I/O
+ * completion requires a lookup of the keyToChannel then we must close
+ * the channels when not holding the write lock.
+ */
+ final int MAX_BATCH_SIZE = 32;
+ OverlappedChannel channels[] = new OverlappedChannel[MAX_BATCH_SIZE];
+ int count;
+ do {
+ // grab a batch of up to 32 channels
+ keyToChannelLock.writeLock().lock();
+ count = 0;
+ try {
+ for (Integer key: keyToChannel.keySet()) {
+ channels[count++] = keyToChannel.get(key);
+ if (count >= MAX_BATCH_SIZE)
+ break;
+ }
+ } finally {
+ keyToChannelLock.writeLock().unlock();
+ }
+
+ // close them
+ for (int i=0; i<count; i++) {
+ try {
+ channels[i].close();
+ } catch (IOException ignore) { }
+ }
+ } while (count > 0);
+ }
+
+ private void wakeup() {
+ try {
+ postQueuedCompletionStatus(port, 0);
+ } catch (IOException e) {
+ // should not happen
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ void executeOnHandlerTask(Runnable task) {
+ synchronized (this) {
+ if (closed)
+ throw new RejectedExecutionException();
+ offerTask(task);
+ wakeup();
+ }
+
+ }
+
+ @Override
+ void shutdownHandlerTasks() {
+ // shutdown all handler threads
+ int nThreads = threadCount();
+ while (nThreads-- > 0) {
+ wakeup();
+ }
+ }
+
+ /**
+ * Associate the given handle with this group
+ */
+ int associate(OverlappedChannel ch, long handle) throws IOException {
+ keyToChannelLock.writeLock().lock();
+
+ // generate a completion key (if not shutdown)
+ int key;
+ try {
+ if (isShutdown())
+ throw new ShutdownChannelGroupException();
+
+ // generate unique key
+ do {
+ key = nextCompletionKey++;
+ } while ((key == 0) || keyToChannel.containsKey(key));
+
+ // associate with I/O completion port
+ if (handle != 0L)
+ createIoCompletionPort(handle, port, key, 0);
+
+ // setup mapping
+ keyToChannel.put(key, ch);
+ } finally {
+ keyToChannelLock.writeLock().unlock();
+ }
+ return key;
+ }
+
+ /**
+ * Disassociate channel from the group.
+ */
+ void disassociate(int key) {
+ boolean checkForShutdown = false;
+
+ keyToChannelLock.writeLock().lock();
+ try {
+ keyToChannel.remove(key);
+
+ // last key to be removed so check if group is shutdown
+ if (keyToChannel.isEmpty())
+ checkForShutdown = true;
+
+ } finally {
+ keyToChannelLock.writeLock().unlock();
+ }
+
+ // continue shutdown
+ if (checkForShutdown && isShutdown()) {
+ try {
+ shutdownNow();
+ } catch (IOException ignore) { }
+ }
+ }
+
+ /**
+ * Invoked when a channel associated with this port is closed before
+ * notifications for all outstanding I/O operations have been received.
+ */
+ void makeStale(Long overlapped) {
+ synchronized (staleIoSet) {
+ staleIoSet.add(overlapped);
+ }
+ }
+
+ /**
+ * Checks if the given OVERLAPPED is stale and if so, releases it.
+ */
+ private void checkIfStale(long ov) {
+ synchronized (staleIoSet) {
+ boolean removed = staleIoSet.remove(ov);
+ if (removed) {
+ unsafe.freeMemory(ov);
+ }
+ }
+ }
+
+ /**
+ * The handler for consuming the result of an asynchronous I/O operation.
+ */
+ static interface ResultHandler {
+ /**
+ * Invoked if the I/O operation completes successfully.
+ */
+ public void completed(int bytesTransferred);
+
+ /**
+ * Invoked if the I/O operation fails.
+ */
+ public void failed(int error, IOException ioe);
+ }
+
+ // Creates IOException for the given I/O error.
+ private static IOException translateErrorToIOException(int error) {
+ String msg = getErrorMessage(error);
+ if (msg == null)
+ msg = "Unknown error: 0x0" + Integer.toHexString(error);
+ return new IOException(msg);
+ }
+
+ /**
+ * Long-running task servicing system-wide or per-file completion port
+ */
+ private class EventHandlerTask implements Runnable {
+ public void run() {
+ Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+ Invoker.getGroupAndInvokeCount();
+ CompletionStatus ioResult = new CompletionStatus();
+ boolean replaceMe = false;
+
+ try {
+ for (;;) {
+ // reset invoke count
+ if (myGroupAndInvokeCount != null)
+ myGroupAndInvokeCount.resetInvokeCount();
+
+ // wait for I/O completion event
+ // A error here is fatal (thread will not be replaced)
+ replaceMe = false;
+ try {
+ getQueuedCompletionStatus(port, ioResult);
+ } catch (IOException x) {
+ // should not happen
+ x.printStackTrace();
+ return;
+ }
+
+ // handle wakeup to execute task or shutdown
+ if (ioResult.completionKey() == 0 &&
+ ioResult.overlapped() == 0L)
+ {
+ Runnable task = pollTask();
+ if (task == null) {
+ // shutdown request
+ return;
+ }
+
+ // run task
+ // (if error/exception then replace thread)
+ replaceMe = true;
+ task.run();
+ continue;
+ }
+
+ // map key to channel
+ OverlappedChannel ch = null;
+ keyToChannelLock.readLock().lock();
+ try {
+ ch = keyToChannel.get(ioResult.completionKey());
+ if (ch == null) {
+ checkIfStale(ioResult.overlapped());
+ continue;
+ }
+ } finally {
+ keyToChannelLock.readLock().unlock();
+ }
+
+ // lookup I/O request
+ PendingFuture<?,?> result = ch.getByOverlapped(ioResult.overlapped());
+ if (result == null) {
+ // we get here if the OVERLAPPED structure is associated
+ // with an I/O operation on a channel that was closed
+ // but the I/O operation event wasn't read in a timely
+ // manner. Alternatively, it may be related to a
+ // tryLock operation as the OVERLAPPED structures for
+ // these operations are not in the I/O cache.
+ checkIfStale(ioResult.overlapped());
+ continue;
+ }
+
+ // synchronize on result in case I/O completed immediately
+ // and was handled by initiator
+ synchronized (result) {
+ if (result.isDone()) {
+ continue;
+ }
+ // not handled by initiator
+ }
+
+ // invoke I/O result handler
+ int error = ioResult.error();
+ ResultHandler rh = (ResultHandler)result.getContext();
+ replaceMe = true; // (if error/exception then replace thread)
+ if (error == 0) {
+ rh.completed(ioResult.bytesTransferred());
+ } else {
+ rh.failed(error, translateErrorToIOException(error));
+ }
+ }
+ } finally {
+ // last thread to exit when shutdown releases resources
+ int remaining = threadExit(this, replaceMe);
+ if (remaining == 0 && isShutdown()) {
+ implClose();
+ }
+ }
+ }
+ }
+
+ /**
+ * Container for data returned by GetQueuedCompletionStatus
+ */
+ private static class CompletionStatus {
+ private int error;
+ private int bytesTransferred;
+ private int completionKey;
+ private long overlapped;
+
+ private CompletionStatus() { }
+ int error() { return error; }
+ int bytesTransferred() { return bytesTransferred; }
+ int completionKey() { return completionKey; }
+ long overlapped() { return overlapped; }
+ }
+
+ // -- native methods --
+
+ private static native void initIDs();
+
+ private static native long createIoCompletionPort(long handle,
+ long existingPort, int completionKey, int concurrency) throws IOException;
+
+ private static native void close0(long handle);
+
+ private static native void getQueuedCompletionStatus(long completionPort,
+ CompletionStatus status) throws IOException;
+
+ private static native void postQueuedCompletionStatus(long completionPort,
+ int completionKey) throws IOException;
+
+ private static native String getErrorMessage(int error);
+
+ static {
+ Util.load();
+ initIDs();
+ }
+}
diff --git a/src/windows/classes/sun/nio/ch/PendingIoCache.java b/src/windows/classes/sun/nio/ch/PendingIoCache.java
new file mode 100644
index 000000000..2e3d50385
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/PendingIoCache.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.*;
+import sun.misc.Unsafe;
+
+/**
+ * Maintains a mapping of pending I/O requests (identified by the address of
+ * an OVERLAPPED structure) to Futures.
+ */
+
+class PendingIoCache {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static final int addressSize = unsafe.addressSize();
+
+ private static int dependsArch(int value32, int value64) {
+ return (addressSize == 4) ? value32 : value64;
+ }
+
+ /*
+ * typedef struct _OVERLAPPED {
+ * DWORD Internal;
+ * DWORD InternalHigh;
+ * DWORD Offset;
+ * DWORD OffsetHigh;
+ * HANDLE hEvent;
+ * } OVERLAPPED;
+ */
+ private static final int SIZEOF_OVERLAPPED = dependsArch(20, 32);
+
+ // set to true when closed
+ private boolean closed;
+
+ // set to true when thread is waiting for all I/O operations to complete
+ private boolean closePending;
+
+ // maps OVERLAPPED to PendingFuture
+ private final Map<Long,PendingFuture> pendingIoMap =
+ new HashMap<Long,PendingFuture>();
+
+ // per-channel cache of OVERLAPPED structures
+ private long[] overlappedCache = new long[4];
+ private int overlappedCacheCount = 0;
+
+ PendingIoCache() {
+ }
+
+ long add(PendingFuture<?,?> result) {
+ synchronized (this) {
+ if (closed)
+ throw new AssertionError("Should not get here");
+ long ov;
+ if (overlappedCacheCount > 0) {
+ ov = overlappedCache[--overlappedCacheCount];
+ } else {
+ ov = unsafe.allocateMemory(SIZEOF_OVERLAPPED);
+ }
+ pendingIoMap.put(ov, result);
+ return ov;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ <V,A> PendingFuture<V,A> remove(long overlapped) {
+ synchronized (this) {
+ PendingFuture<V,A> res = pendingIoMap.remove(overlapped);
+ if (res != null) {
+ if (overlappedCacheCount < overlappedCache.length) {
+ overlappedCache[overlappedCacheCount++] = overlapped;
+ } else {
+ // cache full or channel closing
+ unsafe.freeMemory(overlapped);
+ }
+ // notify closing thread.
+ if (closePending) {
+ this.notifyAll();
+ }
+ }
+ return res;
+ }
+ }
+
+ void close() {
+ synchronized (this) {
+ if (closed)
+ return;
+
+ // handle the case that where there are I/O operations that have
+ // not completed.
+ if (!pendingIoMap.isEmpty())
+ clearPendingIoMap();
+
+ // release memory for any cached OVERLAPPED structures
+ while (overlappedCacheCount > 0) {
+ unsafe.freeMemory( overlappedCache[--overlappedCacheCount] );
+ }
+
+ // done
+ closed = true;
+ }
+ }
+
+ private void clearPendingIoMap() {
+ assert Thread.holdsLock(this);
+
+ // wait up to 50ms for the I/O operations to complete
+ closePending = true;
+ try {
+ this.wait(50);
+ } catch (InterruptedException x) { }
+ closePending = false;
+ if (pendingIoMap.isEmpty())
+ return;
+
+ // cause all pending I/O operations to fail
+ // simulate the failure of all pending I/O operations.
+ for (Long ov: pendingIoMap.keySet()) {
+ PendingFuture<?,?> result = pendingIoMap.get(ov);
+ assert !result.isDone();
+
+ // make I/O port aware of the stale OVERLAPPED structure
+ Iocp iocp = (Iocp)((Groupable)result.channel()).group();
+ iocp.makeStale(ov);
+
+ // execute a task that invokes the result handler's failed method
+ final Iocp.ResultHandler rh = (Iocp.ResultHandler)result.getContext();
+ Runnable task = new Runnable() {
+ public void run() {
+ rh.failed(-1, new AsynchronousCloseException());
+ }
+ };
+ iocp.executeOnPooledThread(task);
+ }
+ pendingIoMap.clear();
+ }
+}
diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java
new file mode 100644
index 000000000..435b5297b
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.net.ProtocolFamily;
+import java.io.IOException;
+
+public class WindowsAsynchronousChannelProvider
+ extends AsynchronousChannelProvider
+{
+ private static volatile Iocp defaultIocp;
+
+ public WindowsAsynchronousChannelProvider() {
+ // nothing to do
+ }
+
+ private Iocp defaultIocp() throws IOException {
+ if (defaultIocp == null) {
+ synchronized (WindowsAsynchronousChannelProvider.class) {
+ if (defaultIocp == null) {
+ // default thread pool may be shared with AsynchronousFileChannels
+ defaultIocp = new Iocp(this, ThreadPool.getDefault()).start();
+ }
+ }
+ }
+ return defaultIocp;
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
+ throws IOException
+ {
+ return new Iocp(this, ThreadPool.create(nThreads, factory)).start();
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
+ throws IOException
+ {
+ return new Iocp(this, ThreadPool.wrap(executor, initialSize)).start();
+ }
+
+ private Iocp toIocp(AsynchronousChannelGroup group) throws IOException {
+ if (group == null) {
+ return defaultIocp();
+ } else {
+ if (!(group instanceof Iocp))
+ throw new IllegalChannelGroupException();
+ return (Iocp)group;
+ }
+ }
+
+ @Override
+ public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new WindowsAsynchronousServerSocketChannelImpl(toIocp(group));
+ }
+
+ @Override
+ public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new WindowsAsynchronousSocketChannelImpl(toIocp(group));
+ }
+
+ @Override
+ public AsynchronousDatagramChannel openAsynchronousDatagramChannel(ProtocolFamily family,
+ AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new SimpleAsynchronousDatagramChannelImpl(family, toIocp(group));
+ }
+}
diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java
new file mode 100644
index 000000000..ef668648d
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java
@@ -0,0 +1,741 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.nio.ByteBuffer;
+import java.nio.BufferOverflowException;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import sun.misc.SharedSecrets;
+import sun.misc.JavaIOFileDescriptorAccess;
+
+/**
+ * Windows implementation of AsynchronousFileChannel using overlapped I/O.
+ */
+
+public class WindowsAsynchronousFileChannelImpl
+ extends AsynchronousFileChannelImpl
+ implements Iocp.OverlappedChannel, Groupable
+{
+ private static final JavaIOFileDescriptorAccess fdAccess =
+ SharedSecrets.getJavaIOFileDescriptorAccess();
+
+ // error when EOF is detected asynchronously.
+ private static final int ERROR_HANDLE_EOF = 38;
+
+ // Lazy initialization of default I/O completion port
+ private static class DefaultIocpHolder {
+ static final Iocp defaultIocp = defaultIocp();
+ private static Iocp defaultIocp() {
+ try {
+ return new Iocp(null, ThreadPool.createDefault()).start();
+ } catch (IOException ioe) {
+ InternalError e = new InternalError();
+ e.initCause(ioe);
+ throw e;
+ }
+ }
+ }
+
+ // Used for force/truncate/size methods
+ private static final FileDispatcher nd = new FileDispatcherImpl();
+
+ // The handle is extracted for use in native methods invoked from this class.
+ private final long handle;
+
+ // The key that identifies the channel's association with the I/O port
+ private final int completionKey;
+
+ // I/O completion port (group)
+ private final Iocp iocp;
+
+ private final boolean isDefaultIocp;
+
+ // Caches OVERLAPPED structure for each outstanding I/O operation
+ private final PendingIoCache ioCache;
+
+
+ private WindowsAsynchronousFileChannelImpl(FileDescriptor fdObj,
+ boolean reading,
+ boolean writing,
+ Iocp iocp,
+ boolean isDefaultIocp)
+ throws IOException
+ {
+ super(fdObj, reading, writing, iocp.executor());
+ this.handle = fdAccess.getHandle(fdObj);
+ this.iocp = iocp;
+ this.isDefaultIocp = isDefaultIocp;
+ this.ioCache = new PendingIoCache();
+ this.completionKey = iocp.associate(this, handle);
+ }
+
+ public static AsynchronousFileChannel open(FileDescriptor fdo,
+ boolean reading,
+ boolean writing,
+ ThreadPool pool)
+ throws IOException
+ {
+ Iocp iocp;
+ boolean isDefaultIocp;
+ if (pool == null) {
+ iocp = DefaultIocpHolder.defaultIocp;
+ isDefaultIocp = true;
+ } else {
+ iocp = new Iocp(null, pool).start();
+ isDefaultIocp = false;
+ }
+ try {
+ return new
+ WindowsAsynchronousFileChannelImpl(fdo, reading, writing, iocp, isDefaultIocp);
+ } catch (IOException x) {
+ // error binding to port so need to close it (if created for this channel)
+ if (!isDefaultIocp)
+ iocp.implClose();
+ throw x;
+ }
+ }
+
+ @Override
+ public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
+ return ioCache.remove(overlapped);
+ }
+
+ @Override
+ public void close() throws IOException {
+ closeLock.writeLock().lock();
+ try {
+ if (closed)
+ return; // already closed
+ closed = true;
+ } finally {
+ closeLock.writeLock().unlock();
+ }
+
+ // invalidate all locks held for this channel
+ invalidateAllLocks();
+
+ // close the file
+ close0(handle);
+
+ // waits until all I/O operations have completed
+ ioCache.close();
+
+ // disassociate from port and shutdown thread pool if not default
+ iocp.disassociate(completionKey);
+ if (!isDefaultIocp)
+ iocp.shutdown();
+ }
+
+ @Override
+ public AsynchronousChannelGroupImpl group() {
+ return iocp;
+ }
+
+ /**
+ * Translates Throwable to IOException
+ */
+ private static IOException toIOException(Throwable x) {
+ if (x instanceof IOException) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ return (IOException)x;
+ }
+ return new IOException(x);
+ }
+
+ @Override
+ public long size() throws IOException {
+ try {
+ begin();
+ return nd.size(fdObj);
+ } finally {
+ end();
+ }
+ }
+
+ @Override
+ public AsynchronousFileChannel truncate(long size) throws IOException {
+ if (size < 0)
+ throw new IllegalArgumentException("Negative size");
+ if (!writing)
+ throw new NonWritableChannelException();
+ try {
+ begin();
+ if (size > nd.size(fdObj))
+ return this;
+ nd.truncate(fdObj, size);
+ } finally {
+ end();
+ }
+ return this;
+ }
+
+ @Override
+ public void force(boolean metaData) throws IOException {
+ try {
+ begin();
+ nd.force(fdObj, metaData);
+ } finally {
+ end();
+ }
+ }
+
+ // -- file locking --
+
+ /**
+ * Task that initiates locking operation and handles completion result.
+ */
+ private class LockTask<A> implements Runnable, Iocp.ResultHandler {
+ private final long position;
+ private final FileLockImpl fli;
+ private final PendingFuture<FileLock,A> result;
+
+ LockTask(long position,
+ FileLockImpl fli,
+ PendingFuture<FileLock,A> result)
+ {
+ this.position = position;
+ this.fli = fli;
+ this.result = result;
+ }
+
+ @Override
+ public void run() {
+ long overlapped = 0L;
+ try {
+ begin();
+
+ // allocate OVERLAPPED structure
+ overlapped = ioCache.add(result);
+
+ // synchronize on result to avoid race with handler thread
+ // when lock is acquired immediately.
+ synchronized (result) {
+ int n = lockFile(handle, position, fli.size(), fli.isShared(),
+ overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ return;
+ }
+ // acquired lock immediately
+ result.setResult(fli);
+ }
+
+ } catch (Throwable x) {
+ // lock failed or channel closed
+ removeFromFileLockTable(fli);
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ result.setFailure(toIOException(x));
+ } finally {
+ end();
+ }
+
+ // invoke completion handler
+ Invoker.invoke(result.handler(), result);
+ }
+
+ @Override
+ public void completed(int bytesTransferred) {
+ // release waiters and invoke completion handler
+ result.setResult(fli);
+ Invoker.invoke(result.handler(), result);
+ }
+
+ @Override
+ public void failed(int error, IOException x) {
+ // lock not acquired so remove from lock table
+ removeFromFileLockTable(fli);
+
+ // release waiters
+ if (isOpen()) {
+ result.setFailure(x);
+ } else {
+ result.setFailure(new AsynchronousCloseException());
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+ }
+
+ @Override
+ public <A> Future<FileLock> lock(long position,
+ long size,
+ boolean shared,
+ A attachment,
+ CompletionHandler<FileLock,? super A> handler)
+ {
+ if (shared && !reading)
+ throw new NonReadableChannelException();
+ if (!shared && !writing)
+ throw new NonWritableChannelException();
+
+ // add to lock table
+ FileLockImpl fli = addToFileLockTable(position, size, shared);
+ if (fli == null) {
+ CompletedFuture<FileLock,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ // create Future and task that will be invoked to acquire lock
+ PendingFuture<FileLock,A> result =
+ new PendingFuture<FileLock,A>(this, handler, attachment);
+ LockTask lockTask = new LockTask<A>(position, fli, result);
+ result.setContext(lockTask);
+
+ // initiate I/O (can only be done from thread in thread pool)
+ try {
+ Invoker.invokeOnThreadInThreadPool(this, lockTask);
+ } catch (ShutdownChannelGroupException e) {
+ // rollback
+ removeFromFileLockTable(fli);
+ throw e;
+ }
+ return result;
+ }
+
+ static final int NO_LOCK = -1; // Failed to lock
+ static final int LOCKED = 0; // Obtained requested lock
+
+ @Override
+ public FileLock tryLock(long position, long size, boolean shared)
+ throws IOException
+ {
+ if (shared && !reading)
+ throw new NonReadableChannelException();
+ if (!shared && !writing)
+ throw new NonWritableChannelException();
+
+ // add to lock table
+ final FileLockImpl fli = addToFileLockTable(position, size, shared);
+ if (fli == null)
+ throw new ClosedChannelException();
+
+ boolean gotLock = false;
+ try {
+ begin();
+ // try to acquire the lock
+ int res = nd.lock(fdObj, false, position, size, shared);
+ if (res == NO_LOCK)
+ return null;
+ gotLock = true;
+ return fli;
+ } finally {
+ if (!gotLock)
+ removeFromFileLockTable(fli);
+ end();
+ }
+ }
+
+ // invoke by FileFileImpl to release lock
+ @Override
+ void release(FileLockImpl fli) throws IOException {
+ try {
+ begin();
+ nd.release(fdObj, fli.position(), fli.size());
+ removeFromFileLockTable(fli);
+ } finally {
+ end();
+ }
+ }
+
+ /**
+ * Task that initiates read operation and handles completion result.
+ */
+ private class ReadTask<A> implements Runnable, Iocp.ResultHandler {
+ private final ByteBuffer dst;
+ private final int pos, rem; // buffer position/remaining
+ private final long position; // file position
+ private final PendingFuture<Integer,A> result;
+
+ // set to dst if direct; otherwise set to substituted direct buffer
+ private volatile ByteBuffer buf;
+
+ ReadTask(ByteBuffer dst,
+ int pos,
+ int rem,
+ long position,
+ PendingFuture<Integer,A> result)
+ {
+ this.dst = dst;
+ this.pos = pos;
+ this.rem = rem;
+ this.position = position;
+ this.result = result;
+ }
+
+ void releaseBufferIfSubstituted() {
+ if (buf != dst)
+ Util.releaseTemporaryDirectBuffer(buf);
+ }
+
+ void updatePosition(int bytesTransferred) {
+ // if the I/O succeeded then adjust buffer position
+ if (bytesTransferred > 0) {
+ if (buf == dst) {
+ try {
+ dst.position(pos + bytesTransferred);
+ } catch (IllegalArgumentException x) {
+ // someone has changed the position; ignore
+ }
+ } else {
+ // had to substitute direct buffer
+ buf.position(bytesTransferred).flip();
+ try {
+ dst.put(buf);
+ } catch (BufferOverflowException x) {
+ // someone has changed the position; ignore
+ }
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ int n = -1;
+ long overlapped = 0L;
+ long address;
+
+ // Substitute a native buffer if not direct
+ if (dst instanceof DirectBuffer) {
+ buf = dst;
+ address = ((DirectBuffer)dst).address() + pos;
+ } else {
+ buf = Util.getTemporaryDirectBuffer(rem);
+ address = ((DirectBuffer)buf).address();
+ }
+
+ try {
+ begin();
+
+ // allocate OVERLAPPED
+ overlapped = ioCache.add(result);
+
+ // synchronize on result to allow this thread handle the case
+ // where the read completes immediately.
+ synchronized (result) {
+ n = readFile(handle, address, rem, position, overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ return;
+ }
+ // read completed immediately:
+ // 1. update buffer position
+ // 2. release waiters
+ updatePosition(n);
+ result.setResult(n);
+ }
+ } catch (Throwable x) {
+ // failed to initiate read
+ result.setFailure(toIOException(x));
+ } finally {
+ end();
+ }
+
+ // read failed or EOF so completion port will not be notified
+ if (n < 0 && overlapped != 0L) {
+ ioCache.remove(overlapped);
+ }
+
+ // return direct buffer to cache if substituted
+ releaseBufferIfSubstituted();
+
+ // invoke completion handler
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Executed when the I/O has completed
+ */
+ @Override
+ public void completed(int bytesTransferred) {
+ updatePosition(bytesTransferred);
+
+ // return direct buffer to cache if substituted
+ releaseBufferIfSubstituted();
+
+ // release waiters and invoke completion handler
+ result.setResult(bytesTransferred);
+ Invoker.invoke(result.handler(), result);
+ }
+
+ @Override
+ public void failed(int error, IOException x) {
+ // if EOF detected asynchronously then it is reported as error
+ if (error == ERROR_HANDLE_EOF) {
+ completed(-1);
+ } else {
+ // return direct buffer to cache if substituted
+ releaseBufferIfSubstituted();
+
+ // release waiters
+ if (isOpen()) {
+ result.setFailure(x);
+ } else {
+ result.setFailure(new AsynchronousCloseException());
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+ }
+ }
+
+ @Override
+ public <A> Future<Integer> read(ByteBuffer dst,
+ long position,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ if (!reading)
+ throw new NonReadableChannelException();
+ if (position < 0)
+ throw new IllegalArgumentException("Negative position");
+ if (dst.isReadOnly())
+ throw new IllegalArgumentException("Read-only buffer");
+
+ // check if channel is closed
+ if (!isOpen()) {
+ CompletedFuture<Integer,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ int pos = dst.position();
+ int lim = dst.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ // no space remaining
+ if (rem == 0) {
+ CompletedFuture<Integer,A> result =
+ CompletedFuture.withResult(this, 0, attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ // create Future and task that initiates read
+ PendingFuture<Integer,A> result =
+ new PendingFuture<Integer,A>(this, handler, attachment);
+ ReadTask readTask = new ReadTask<A>(dst, pos, rem, position, result);
+ result.setContext(readTask);
+
+ // initiate I/O (can only be done from thread in thread pool)
+ Invoker.invokeOnThreadInThreadPool(this, readTask);
+ return result;
+ }
+
+ /**
+ * Task that initiates write operation and handles completion result.
+ */
+ private class WriteTask<A> implements Runnable, Iocp.ResultHandler {
+ private final ByteBuffer src;
+ private final int pos, rem; // buffer position/remaining
+ private final long position; // file position
+ private final PendingFuture<Integer,A> result;
+
+ // set to src if direct; otherwise set to substituted direct buffer
+ private volatile ByteBuffer buf;
+
+ WriteTask(ByteBuffer src,
+ int pos,
+ int rem,
+ long position,
+ PendingFuture<Integer,A> result)
+ {
+ this.src = src;
+ this.pos = pos;
+ this.rem = rem;
+ this.position = position;
+ this.result = result;
+ }
+
+ void releaseBufferIfSubstituted() {
+ if (buf != src)
+ Util.releaseTemporaryDirectBuffer(buf);
+ }
+
+ void updatePosition(int bytesTransferred) {
+ // if the I/O succeeded then adjust buffer position
+ if (bytesTransferred > 0) {
+ try {
+ src.position(pos + bytesTransferred);
+ } catch (IllegalArgumentException x) {
+ // someone has changed the position
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ int n = -1;
+ long overlapped = 0L;
+ long address;
+
+ // Substitute a native buffer if not direct
+ if (src instanceof DirectBuffer) {
+ buf = src;
+ address = ((DirectBuffer)src).address() + pos;
+ } else {
+ buf = Util.getTemporaryDirectBuffer(rem);
+ buf.put(src);
+ buf.flip();
+ // temporarily restore position as we don't know how many bytes
+ // will be written
+ src.position(pos);
+ address = ((DirectBuffer)buf).address();
+ }
+
+ try {
+ begin();
+
+ // allocate an OVERLAPPED structure
+ overlapped = ioCache.add(result);
+
+ // synchronize on result to allow this thread handle the case
+ // where the read completes immediately.
+ synchronized (result) {
+ n = writeFile(handle, address, rem, position, overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ return;
+ }
+ // read completed immediately:
+ // 1. update buffer position
+ // 2. release waiters
+ updatePosition(n);
+ result.setResult(n);
+ }
+ } catch (Throwable x) {
+ // failed to initiate read:
+ result.setFailure(toIOException(x));
+
+ // release resources
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ releaseBufferIfSubstituted();
+
+ } finally {
+ end();
+ }
+
+ // invoke completion handler
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Executed when the I/O has completed
+ */
+ @Override
+ public void completed(int bytesTransferred) {
+ updatePosition(bytesTransferred);
+
+ // return direct buffer to cache if substituted
+ releaseBufferIfSubstituted();
+
+ // release waiters and invoke completion handler
+ result.setResult(bytesTransferred);
+ Invoker.invoke(result.handler(), result);
+ }
+
+ @Override
+ public void failed(int error, IOException x) {
+ // return direct buffer to cache if substituted
+ releaseBufferIfSubstituted();
+
+ // release waiters and invoker completion handler
+ if (isOpen()) {
+ result.setFailure(x);
+ } else {
+ result.setFailure(new AsynchronousCloseException());
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+ }
+
+ @Override
+ public <A> Future<Integer> write(ByteBuffer src,
+ long position,
+ A attachment,
+ CompletionHandler<Integer,? super A> handler)
+ {
+ if (!writing)
+ throw new NonWritableChannelException();
+ if (position < 0)
+ throw new IllegalArgumentException("Negative position");
+
+ // check if channel is closed
+ if (!isOpen()) {
+ CompletedFuture<Integer,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ int pos = src.position();
+ int lim = src.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+
+ // nothing to write
+ if (rem == 0) {
+ CompletedFuture<Integer,A> result =
+ CompletedFuture.withResult(this, 0, attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ // create Future and task to initiate write
+ PendingFuture<Integer,A> result =
+ new PendingFuture<Integer,A>(this, handler, attachment);
+ WriteTask writeTask = new WriteTask<A>(src, pos, rem, position, result);
+ result.setContext(writeTask);
+
+ // initiate I/O (can only be done from thread in thread pool)
+ Invoker.invokeOnThreadInThreadPool(this, writeTask);
+ return result;
+ }
+
+ // -- Native methods --
+
+ private static native int readFile(long handle, long address, int len,
+ long offset, long overlapped) throws IOException;
+
+ private static native int writeFile(long handle, long address, int len,
+ long offset, long overlapped) throws IOException;
+
+ private static native int lockFile(long handle, long position, long size,
+ boolean shared, long overlapped) throws IOException;
+
+ private static native void close0(long handle);
+
+ static {
+ Util.load();
+ }
+}
diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java
new file mode 100644
index 000000000..8efb10d75
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.misc.Unsafe;
+
+/**
+ * Windows implementation of AsynchronousServerSocketChannel using overlapped I/O.
+ */
+
+class WindowsAsynchronousServerSocketChannelImpl
+ extends AsynchronousServerSocketChannelImpl implements Iocp.OverlappedChannel
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // 2 * (sizeof(SOCKET_ADDRESS) + 16)
+ private static final int DATA_BUFFER_SIZE = 88;
+
+ private final long handle;
+ private final int completionKey;
+ private final Iocp iocp;
+
+ // typically there will be zero, or one I/O operations pending. In rare
+ // cases there may be more. These rare cases arise when a sequence of accept
+ // operations complete immediately and handled by the initiating thread.
+ // The corresponding OVERLAPPED cannot be reused/released until the completion
+ // event has been posted.
+ private final PendingIoCache ioCache;
+
+ // the data buffer to receive the local/remote socket address
+ private final long dataBuffer;
+
+ // flag to indicate that an accept operation is outstanding
+ private AtomicBoolean accepting = new AtomicBoolean();
+
+
+ WindowsAsynchronousServerSocketChannelImpl(Iocp iocp) throws IOException {
+ super(iocp);
+
+ // associate socket with given completion port
+ long h = IOUtil.fdVal(fd);
+ int key;
+ try {
+ key = iocp.associate(this, h);
+ } catch (IOException x) {
+ closesocket0(h); // prevent leak
+ throw x;
+ }
+
+ this.handle = h;
+ this.completionKey = key;
+ this.iocp = iocp;
+ this.ioCache = new PendingIoCache();
+ this.dataBuffer = unsafe.allocateMemory(DATA_BUFFER_SIZE);
+ }
+
+ @Override
+ public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
+ return ioCache.remove(overlapped);
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // close socket (which may cause outstanding accept to be aborted).
+ closesocket0(handle);
+
+ // waits until the accept operations have completed
+ ioCache.close();
+
+ // finally disassociate from the completion port
+ iocp.disassociate(completionKey);
+
+ // release other resources
+ unsafe.freeMemory(dataBuffer);
+ }
+
+ @Override
+ public AsynchronousChannelGroupImpl group() {
+ return iocp;
+ }
+
+ /**
+ * Task to initiate accept operation and to handle result.
+ */
+ private class AcceptTask<A> implements Runnable, Iocp.ResultHandler {
+ private final WindowsAsynchronousSocketChannelImpl channel;
+ private final AccessControlContext acc;
+ private final PendingFuture<AsynchronousSocketChannel,A> result;
+
+ AcceptTask(WindowsAsynchronousSocketChannelImpl channel,
+ AccessControlContext acc,
+ PendingFuture<AsynchronousSocketChannel,A> result)
+ {
+ this.channel = channel;
+ this.acc = acc;
+ this.result = result;
+ }
+
+ void enableAccept() {
+ accepting.set(false);
+ }
+
+ void closeChildChannel() {
+ try {
+ channel.close();
+ } catch (IOException ignore) { }
+ }
+
+ // caller must have acquired read lock for the listener and child channel.
+ void finishAccept() throws IOException {
+ /**
+ * Set local/remote addresses. This is currently very inefficient
+ * in that it requires 2 calls to getsockname and 2 calls to getpeername.
+ * (should change this to use GetAcceptExSockaddrs)
+ */
+ updateAcceptContext(handle, channel.handle());
+
+ InetSocketAddress local = Net.localAddress(channel.fd);
+ final InetSocketAddress remote = Net.remoteAddress(channel.fd);
+ channel.setConnected(local, remote);
+
+ // permission check (in context of initiating thread)
+ if (acc != null) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ SecurityManager sm = System.getSecurityManager();
+ sm.checkAccept(remote.getAddress().getHostAddress(),
+ remote.getPort());
+ return null;
+ }
+ }, acc);
+ }
+ }
+
+ /**
+ * Initiates the accept operation.
+ */
+ @Override
+ public void run() {
+ long overlapped = 0L;
+
+ try {
+ // begin usage of listener socket
+ begin();
+ try {
+ // begin usage of child socket (as it is registered with
+ // completion port and so may be closed in the event that
+ // the group is forcefully closed).
+ channel.begin();
+
+ synchronized (result) {
+ overlapped = ioCache.add(result);
+
+ int n = accept0(handle, channel.handle(), overlapped, dataBuffer);
+ if (n == IOStatus.UNAVAILABLE) {
+ return;
+ }
+
+ // connection accepted immediately
+ finishAccept();
+
+ // allow another accept before the result is set
+ enableAccept();
+ result.setResult(channel);
+ }
+ } finally {
+ // end usage on child socket
+ channel.end();
+ }
+ } catch (Throwable x) {
+ // failed to initiate accept so release resources
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ closeChildChannel();
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ if (!(x instanceof IOException) && !(x instanceof SecurityException))
+ x = new IOException(x);
+ enableAccept();
+ result.setFailure(x);
+ } finally {
+ // end of usage of listener socket
+ end();
+ }
+
+ // accept completed immediately but may not have executed on
+ // initiating thread in which case the operation may have been
+ // cancelled.
+ if (result.isCancelled()) {
+ closeChildChannel();
+ }
+
+ // invoke completion handler
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+
+ /**
+ * Executed when the I/O has completed
+ */
+ @Override
+ public void completed(int bytesTransferred) {
+ try {
+ // connection accept after group has shutdown
+ if (iocp.isShutdown()) {
+ throw new IOException(new ShutdownChannelGroupException());
+ }
+
+ // finish the accept
+ try {
+ begin();
+ try {
+ channel.begin();
+ finishAccept();
+ } finally {
+ channel.end();
+ }
+ } finally {
+ end();
+ }
+
+ // allow another accept before the result is set
+ enableAccept();
+ result.setResult(channel);
+ } catch (Throwable x) {
+ enableAccept();
+ closeChildChannel();
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ if (!(x instanceof IOException) && !(x instanceof SecurityException))
+ x = new IOException(x);
+ result.setFailure(x);
+ }
+
+ // if an async cancel has already cancelled the operation then
+ // close the new channel so as to free resources
+ if (result.isCancelled()) {
+ closeChildChannel();
+ }
+
+ // invoke handler (but not directly)
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+
+ @Override
+ public void failed(int error, IOException x) {
+ enableAccept();
+ closeChildChannel();
+
+ // release waiters
+ if (isOpen()) {
+ result.setFailure(x);
+ } else {
+ result.setFailure(new AsynchronousCloseException());
+ }
+ Invoker.invokeIndirectly(result.handler(), result);
+ }
+ }
+
+ @Override
+ public <A> Future<AsynchronousSocketChannel> accept(A attachment,
+ final CompletionHandler<AsynchronousSocketChannel,? super A> handler)
+ {
+ if (!isOpen()) {
+ CompletedFuture<AsynchronousSocketChannel,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invokeIndirectly(handler, result);
+ return result;
+ }
+ if (isAcceptKilled())
+ throw new RuntimeException("Accept not allowed due to cancellation");
+
+ // ensure channel is bound to local address
+ if (localAddress == null)
+ throw new NotYetBoundException();
+
+ // create the socket that will be accepted. The creation of the socket
+ // is enclosed by a begin/end for the listener socket to ensure that
+ // we check that the listener is open and also to prevent the I/O
+ // port from being closed as the new socket is registered.
+ WindowsAsynchronousSocketChannelImpl ch = null;
+ IOException ioe = null;
+ try {
+ begin();
+ ch = new WindowsAsynchronousSocketChannelImpl(iocp, false);
+ } catch (IOException x) {
+ ioe = x;
+ } finally {
+ end();
+ }
+ if (ioe != null) {
+ CompletedFuture<AsynchronousSocketChannel,A> result =
+ CompletedFuture.withFailure(this, ioe, attachment);
+ Invoker.invokeIndirectly(handler, result);
+ return result;
+ }
+
+ // need calling context when there is security manager as
+ // permission check may be done in a different thread without
+ // any application call frames on the stack
+ AccessControlContext acc = (System.getSecurityManager() == null) ?
+ null : AccessController.getContext();
+
+ PendingFuture<AsynchronousSocketChannel,A> result =
+ new PendingFuture<AsynchronousSocketChannel,A>(this, handler, attachment);
+ AcceptTask task = new AcceptTask<A>(ch, acc, result);
+ result.setContext(task);
+
+ // check and set flag to prevent concurrent accepting
+ if (!accepting.compareAndSet(false, true))
+ throw new AcceptPendingException();
+
+ // initiate accept. As I/O operations are tied to the initiating thread
+ // then it will only be invoked direcly if this thread is in the thread
+ // pool. If this thread is not in the thread pool when a task is
+ // submitted to initiate the accept.
+ Invoker.invokeOnThreadInThreadPool(this, task);
+ return result;
+ }
+
+ // -- Native methods --
+
+ private static native void initIDs();
+
+ private static native int accept0(long listenSocket, long acceptSocket,
+ long overlapped, long dataBuffer) throws IOException;
+
+ private static native void updateAcceptContext(long listenSocket,
+ long acceptSocket) throws IOException;
+
+ private static native void closesocket0(long socket) throws IOException;
+
+ static {
+ Util.load();
+ initIDs();
+ }
+}
diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java
new file mode 100644
index 000000000..fe9920c15
--- /dev/null
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 conne02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.ByteBuffer;
+import java.nio.BufferOverflowException;
+import java.net.*;
+import java.util.concurrent.*;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+/**
+ * Windows implementation of AsynchronousSocketChannel using overlapped I/O.
+ */
+
+class WindowsAsynchronousSocketChannelImpl
+ extends AsynchronousSocketChannelImpl implements Iocp.OverlappedChannel
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static int addressSize = unsafe.addressSize();
+
+ private static int dependsArch(int value32, int value64) {
+ return (addressSize == 4) ? value32 : value64;
+ }
+
+ /*
+ * typedef struct _WSABUF {
+ * u_long len;
+ * char FAR * buf;
+ * } WSABUF;
+ */
+ private static final int SIZEOF_WSABUF = dependsArch(8, 16);
+ private static final int OFFSETOF_LEN = 0;
+ private static final int OFFSETOF_BUF = dependsArch(4, 8);
+
+ // maximum vector size for scatter/gather I/O
+ private static final int MAX_WSABUF = 16;
+
+ private static final int SIZEOF_WSABUFARRAY = MAX_WSABUF * SIZEOF_WSABUF;
+
+
+ // socket handle. Use begin()/end() around each usage of this handle.
+ final long handle;
+
+ // I/O completion port that the socket is associated with
+ private final Iocp iocp;
+
+ // completion key to identify channel when I/O completes
+ private final int completionKey;
+
+ // Pending I/O operations are tied to an OVERLAPPED structure that can only
+ // be released when the I/O completion event is posted to the completion
+ // port. Where I/O operations complete immediately then it is possible
+ // there may be more than two OVERLAPPED structures in use.
+ private final PendingIoCache ioCache;
+
+ // per-channel arrays of WSABUF structures
+ private final long readBufferArray;
+ private final long writeBufferArray;
+
+
+ WindowsAsynchronousSocketChannelImpl(Iocp iocp, boolean failIfGroupShutdown)
+ throws IOException
+ {
+ super(iocp);
+
+ // associate socket with default completion port
+ long h = IOUtil.fdVal(fd);
+ int key = 0;
+ try {
+ key = iocp.associate(this, h);
+ } catch (ShutdownChannelGroupException x) {
+ if (failIfGroupShutdown) {
+ closesocket0(h);
+ throw x;
+ }
+ } catch (IOException x) {
+ closesocket0(h);
+ throw x;
+ }
+
+ this.handle = h;
+ this.iocp = iocp;
+ this.completionKey = key;
+ this.ioCache = new PendingIoCache();
+
+ // allocate WSABUF arrays
+ this.readBufferArray = unsafe.allocateMemory(SIZEOF_WSABUFARRAY);
+ this.writeBufferArray = unsafe.allocateMemory(SIZEOF_WSABUFARRAY);
+ }
+
+ WindowsAsynchronousSocketChannelImpl(Iocp iocp) throws IOException {
+ this(iocp, true);
+ }
+
+ @Override
+ public AsynchronousChannelGroupImpl group() {
+ return iocp;
+ }
+
+ /**
+ * Invoked by Iocp when an I/O operation competes.
+ */
+ @Override
+ public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
+ return ioCache.remove(overlapped);
+ }
+
+ // invoked by WindowsAsynchronousServerSocketChannelImpl
+ long handle() {
+ return handle;
+ }
+
+ // invoked by WindowsAsynchronousServerSocketChannelImpl when new connection
+ // accept
+ void setConnected(SocketAddress localAddress, SocketAddress remoteAddress) {
+ synchronized (stateLock) {
+ state = ST_CONNECTED;
+ this.localAddress = localAddress;
+ this.remoteAddress = remoteAddress;
+ }
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // close socket (may cause outstanding async I/O operations to fail).
+ closesocket0(handle);
+
+ // waits until all I/O operations have completed
+ ioCache.close();
+
+ // release arrays of WSABUF structures
+ unsafe.freeMemory(readBufferArray);
+ unsafe.freeMemory(writeBufferArray);
+
+ // finally disassociate from the completion port (key can be 0 if
+ // channel created when group is shutdown)
+ if (completionKey != 0)
+ iocp.disassociate(completionKey);
+ }
+
+ @Override
+ public void onCancel(PendingFuture<?,?> task) {
+ if (task.getContext() instanceof ConnectTask)
+ killConnect();
+ if (task.getContext() instanceof ReadTask)
+ killReading();
+ if (task.getContext() instanceof WriteTask)
+ killWriting();
+ }
+
+ /**
+ * Implements the task to initiate a connection and the handler to
+ * consume the result when the connection is established (or fails).
+ */
+ private class ConnectTask<A> implements Runnable, Iocp.ResultHandler {
+ private final InetSocketAddress remote;
+ private final PendingFuture<Void,A> result;
+
+ ConnectTask(InetSocketAddress remote, PendingFuture<Void,A> result) {
+ this.remote = remote;
+ this.result = result;
+ }
+
+ private void closeChannel() {
+ try {
+ close();
+ } catch (IOException ignore) { }
+ }
+
+ private IOException toIOException(Throwable x) {
+ if (x instanceof IOException) {
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ return (IOException)x;
+ }
+ return new IOException(x);
+ }
+
+ /**
+ * Invoke after a connection is successfully established.
+ */
+ private void afterConnect() throws IOException {
+ updateConnectContext(handle);
+ synchronized (stateLock) {
+ state = ST_CONNECTED;
+ remoteAddress = remote;
+ }
+ }
+
+ /**
+ * Task to initiate a connection.
+ */
+ @Override
+ public void run() {
+ long overlapped = 0L;
+ Throwable exc = null;
+ try {
+ begin();
+
+ // synchronize on result to allow this thread handle the case
+ // where the connection is established immediately.
+ synchronized (result) {
+ overlapped = ioCache.add(result);
+ // initiate the connection
+ int n = connect0(handle, Net.isIPv6Available(), remote.getAddress(),
+ remote.getPort(), overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // connection is pending
+ return;
+ }
+
+ // connection established immediately
+ afterConnect();
+ result.setResult(null);
+ }
+ } catch (Throwable x) {
+ exc = x;
+ } finally {
+ end();
+ }
+
+ if (exc != null) {
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ closeChannel();
+ result.setFailure(toIOException(exc));
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Invoked by handler thread when connection established.
+ */
+ @Override
+ public void completed(int bytesTransferred) {
+ Throwable exc = null;
+ try {
+ begin();
+ afterConnect();
+ result.setResult(null);
+ } catch (Throwable x) {
+ // channel is closed or unable to finish connect
+ exc = x;
+ } finally {
+ end();
+ }
+
+ // can't close channel while in begin/end block
+ if (exc != null) {
+ closeChannel();
+ result.setFailure(toIOException(exc));
+ }
+
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Invoked by handler thread when failed to establish connection.
+ */
+ @Override
+ public void failed(int error, IOException x) {
+ if (isOpen()) {
+ closeChannel();
+ result.setFailure(x);
+ } else {
+ result.setFailure(new AsynchronousCloseException());
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+ }
+
+ @Override
+ public <A> Future<Void> connect(SocketAddress remote,
+ A attachment,
+ CompletionHandler<Void,? super A> handler)
+ {
+ if (!isOpen()) {
+ CompletedFuture<Void,A> result = CompletedFuture
+ .withFailure(this, new ClosedChannelException(), attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ InetSocketAddress isa = Net.checkAddress(remote);
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
+
+ // check and update state
+ // ConnectEx requires the socket to be bound to a local address
+ IOException bindException = null;
+ synchronized (stateLock) {
+ if (state == ST_CONNECTED)
+ throw new AlreadyConnectedException();
+ if (state == ST_PENDING)
+ throw new ConnectionPendingException();
+ if (localAddress == null) {
+ try {
+ bind(new InetSocketAddress(0));
+ } catch (IOException x) {
+ bindException = x;
+ }
+ }
+ if (bindException == null)
+ state = ST_PENDING;
+ }
+
+ // handle bind failure
+ if (bindException != null) {
+ try {
+ close();
+ } catch (IOException ignore) { }
+ CompletedFuture<Void,A> result = CompletedFuture
+ .withFailure(this, bindException, attachment);
+ Invoker.invoke(handler, result);
+ return result;
+ }
+
+ // setup task
+ PendingFuture<Void,A> result =
+ new PendingFuture<Void,A>(this, handler, attachment);
+ ConnectTask task = new ConnectTask<A>(isa, result);
+ result.setContext(task);
+
+ // initiate I/O (can only be done from thread in thread pool)
+ Invoker.invokeOnThreadInThreadPool(this, task);
+ return result;
+ }
+
+ /**
+ * Implements the task to initiate a read and the handler to consume the
+ * result when the read completes.
+ */
+ private class ReadTask<V,A> implements Runnable, Iocp.ResultHandler {
+ private final ByteBuffer[] bufs;
+ private final int numBufs;
+ private final boolean scatteringRead;
+ private final PendingFuture<V,A> result;
+
+ // set by run method
+ private ByteBuffer[] shadow;
+
+ ReadTask(ByteBuffer[] bufs,
+ boolean scatteringRead,
+ PendingFuture<V,A> result)
+ {
+ this.bufs = bufs;
+ this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length;
+ this.scatteringRead = scatteringRead;
+ this.result = result;
+ }
+
+ /**
+ * Invoked prior to read to prepare the WSABUF array. Where necessary,
+ * it substitutes non-direct buffers with direct buffers.
+ */
+ void prepareBuffers() {
+ shadow = new ByteBuffer[numBufs];
+ long address = readBufferArray;
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer dst = bufs[i];
+ int pos = dst.position();
+ int lim = dst.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+ long a;
+ if (!(dst instanceof DirectBuffer)) {
+ // substitute with direct buffer
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+ shadow[i] = bb;
+ a = ((DirectBuffer)bb).address();
+ } else {
+ shadow[i] = dst;
+ a = ((DirectBuffer)dst).address() + pos;
+ }
+ unsafe.putAddress(address + OFFSETOF_BUF, a);
+ unsafe.putInt(address + OFFSETOF_LEN, rem);
+ address += SIZEOF_WSABUF;
+ }
+ }
+
+ /**
+ * Invoked after a read has completed to update the buffer positions
+ * and release any substituted buffers.
+ */
+ void updateBuffers(int bytesRead) {
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer nextBuffer = shadow[i];
+ int pos = nextBuffer.position();
+ int len = nextBuffer.remaining();
+ if (bytesRead >= len) {
+ bytesRead -= len;
+ int newPosition = pos + len;
+ try {
+ nextBuffer.position(newPosition);
+ } catch (IllegalArgumentException x) {
+ // position changed by another
+ }
+ } else { // Buffers not completely filled
+ if (bytesRead > 0) {
+ assert(pos + bytesRead < (long)Integer.MAX_VALUE);
+ int newPosition = pos + bytesRead;
+ try {
+ nextBuffer.position(newPosition);
+ } catch (IllegalArgumentException x) {
+ // position changed by another
+ }
+ }
+ break;
+ }
+ }
+
+ // Put results from shadow into the slow buffers
+ for (int i=0; i<numBufs; i++) {
+ if (!(bufs[i] instanceof DirectBuffer)) {
+ shadow[i].flip();
+ try {
+ bufs[i].put(shadow[i]);
+ } catch (BufferOverflowException x) {
+ // position changed by another
+ }
+ }
+ }
+ }
+
+ void releaseBuffers() {
+ for (int i=0; i<numBufs; i++) {
+ if (!(bufs[i] instanceof DirectBuffer)) {
+ Util.releaseTemporaryDirectBuffer(shadow[i]);
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void run() {
+ long overlapped = 0L;
+ boolean prepared = false;
+ boolean pending = false;
+
+ try {
+ begin();
+
+ // substitute non-direct buffers
+ prepareBuffers();
+ prepared = true;
+
+ // get an OVERLAPPED structure (from the cache or allocate)
+ overlapped = ioCache.add(result);
+
+ // synchronize on result to allow this thread handle the case
+ // where the read completes immediately.
+ synchronized (result) {
+ int n = read0(handle, numBufs, readBufferArray, overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ pending = true;
+ return;
+ }
+ // read completed immediately:
+ // 1. update buffer position
+ // 2. reset read flag
+ // 3. release waiters
+ if (n == 0) {
+ n = -1;
+ } else {
+ updateBuffers(n);
+ }
+ enableReading();
+
+ if (scatteringRead) {
+ result.setResult((V)Long.valueOf(n));
+ } else {
+ result.setResult((V)Integer.valueOf(n));
+ }
+ }
+ } catch (Throwable x) {
+ // failed to initiate read:
+ // 1. reset read flag
+ // 2. free resources
+ // 3. release waiters
+ enableReading();
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ if (x instanceof ClosedChannelException)
+ x = new AsynchronousCloseException();
+ if (!(x instanceof IOException))
+ x = new IOException(x);
+ result.setFailure(x);
+ } finally {
+ if (prepared && !pending) {
+ // return direct buffer(s) to cache if substituted
+ releaseBuffers();
+ }
+ end();
+ }
+
+ // invoke completion handler
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Executed when the I/O has completed
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public void completed(int bytesTransferred) {
+ if (bytesTransferred == 0) {
+ bytesTransferred = -1; // EOF
+ } else {
+ updateBuffers(bytesTransferred);
+ }
+
+ // return direct buffer to cache if substituted
+ releaseBuffers();
+
+ // release waiters if not already released by timeout
+ synchronized (result) {
+ if (result.isDone())
+ return;
+ enableReading();
+ if (scatteringRead) {
+ result.setResult((V)Long.valueOf(bytesTransferred));
+ } else {
+ result.setResult((V)Integer.valueOf(bytesTransferred));
+ }
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+
+ @Override
+ public void failed(int error, IOException x) {
+ // return direct buffer to cache if substituted
+ releaseBuffers();
+
+ // release waiters if not already released by timeout
+ if (!isOpen())
+ x = new AsynchronousCloseException();
+
+ synchronized (result) {
+ if (result.isDone())
+ return;
+ enableReading();
+ result.setFailure(x);
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Invoked if timeout expires before it is cancelled
+ */
+ void timeout() {
+ // synchronize on result as the I/O could complete/fail
+ synchronized (result) {
+ if (result.isDone())
+ return;
+
+ // kill further reading before releasing waiters
+ enableReading(true);
+ result.setFailure(new InterruptedByTimeoutException());
+ }
+
+ // invoke handler without any locks
+ Invoker.invoke(result.handler(), result);
+ }
+ }
+
+ @Override
+ <V extends Number,A> Future<V> readImpl(ByteBuffer[] bufs,
+ boolean scatteringRead,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler)
+ {
+ // setup task
+ PendingFuture<V,A> result =
+ new PendingFuture<V,A>(this, handler, attachment);
+ final ReadTask readTask = new ReadTask<V,A>(bufs, scatteringRead, result);
+ result.setContext(readTask);
+
+ // schedule timeout
+ if (timeout > 0L) {
+ Future<?> timeoutTask = iocp.schedule(new Runnable() {
+ public void run() {
+ readTask.timeout();
+ }
+ }, timeout, unit);
+ result.setTimeoutTask(timeoutTask);
+ }
+
+ // initiate I/O (can only be done from thread in thread pool)
+ Invoker.invokeOnThreadInThreadPool(this, readTask);
+ return result;
+ }
+
+ /**
+ * Implements the task to initiate a write and the handler to consume the
+ * result when the write completes.
+ */
+ private class WriteTask<V,A> implements Runnable, Iocp.ResultHandler {
+ private final ByteBuffer[] bufs;
+ private final int numBufs;
+ private final boolean gatheringWrite;
+ private final PendingFuture<V,A> result;
+
+ // set by run method
+ private ByteBuffer[] shadow;
+
+ WriteTask(ByteBuffer[] bufs,
+ boolean gatheringWrite,
+ PendingFuture<V,A> result)
+ {
+ this.bufs = bufs;
+ this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length;
+ this.gatheringWrite = gatheringWrite;
+ this.result = result;
+ }
+
+ /**
+ * Invoked prior to write to prepare the WSABUF array. Where necessary,
+ * it substitutes non-direct buffers with direct buffers.
+ */
+ void prepareBuffers() {
+ shadow = new ByteBuffer[numBufs];
+ long address = writeBufferArray;
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer src = bufs[i];
+ int pos = src.position();
+ int lim = src.limit();
+ assert (pos <= lim);
+ int rem = (pos <= lim ? lim - pos : 0);
+ long a;
+ if (!(src instanceof DirectBuffer)) {
+ // substitute with direct buffer
+ ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+ bb.put(src);
+ bb.flip();
+ src.position(pos); // leave heap buffer untouched for now
+ shadow[i] = bb;
+ a = ((DirectBuffer)bb).address();
+ } else {
+ shadow[i] = src;
+ a = ((DirectBuffer)src).address() + pos;
+ }
+ unsafe.putAddress(address + OFFSETOF_BUF, a);
+ unsafe.putInt(address + OFFSETOF_LEN, rem);
+ address += SIZEOF_WSABUF;
+ }
+ }
+
+ /**
+ * Invoked after a write has completed to update the buffer positions
+ * and release any substituted buffers.
+ */
+ void updateBuffers(int bytesWritten) {
+ // Notify the buffers how many bytes were taken
+ for (int i=0; i<numBufs; i++) {
+ ByteBuffer nextBuffer = bufs[i];
+ int pos = nextBuffer.position();
+ int lim = nextBuffer.limit();
+ int len = (pos <= lim ? lim - pos : lim);
+ if (bytesWritten >= len) {
+ bytesWritten -= len;
+ int newPosition = pos + len;
+ try {
+ nextBuffer.position(newPosition);
+ } catch (IllegalArgumentException x) {
+ // position changed by someone else
+ }
+ } else { // Buffers not completely filled
+ if (bytesWritten > 0) {
+ assert(pos + bytesWritten < (long)Integer.MAX_VALUE);
+ int newPosition = pos + bytesWritten;
+ try {
+ nextBuffer.position(newPosition);
+ } catch (IllegalArgumentException x) {
+ // position changed by someone else
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ void releaseBuffers() {
+ for (int i=0; i<numBufs; i++) {
+ if (!(bufs[i] instanceof DirectBuffer)) {
+ Util.releaseTemporaryDirectBuffer(shadow[i]);
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void run() {
+ int n = -1;
+ long overlapped = 0L;
+ boolean prepared = false;
+ boolean pending = false;
+ boolean shutdown = false;
+
+ try {
+ begin();
+
+ // substitute non-direct buffers
+ prepareBuffers();
+ prepared = true;
+
+ // get an OVERLAPPED structure (from the cache or allocate)
+ overlapped = ioCache.add(result);
+
+ // synchronize on result to allow this thread handle the case
+ // where the read completes immediately.
+ synchronized (result) {
+ n = write0(handle, numBufs, writeBufferArray, overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ pending = true;
+ return;
+ }
+
+ enableWriting();
+
+ if (n == IOStatus.EOF) {
+ // special case for shutdown output
+ shutdown = true;
+ throw new ClosedChannelException();
+ }
+
+ // write completed immediately:
+ // 1. enable writing
+ // 2. update buffer position
+ // 3. release waiters
+ updateBuffers(n);
+
+ // result is a Long or Integer
+ if (gatheringWrite) {
+ result.setResult((V)Long.valueOf(n));
+ } else {
+ result.setResult((V)Integer.valueOf(n));
+ }
+ }
+ } catch (Throwable x) {
+ enableWriting();
+
+ // failed to initiate read:
+ if (!shutdown && (x instanceof ClosedChannelException))
+ x = new AsynchronousCloseException();
+ if (!(x instanceof IOException))
+ x = new IOException(x);
+ result.setFailure(x);
+
+ // release resources
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+
+ } finally {
+ if (prepared && !pending) {
+ // return direct buffer(s) to cache if substituted
+ releaseBuffers();
+ }
+ end();
+ }
+
+ // invoke completion handler
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Executed when the I/O has completed
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public void completed(int bytesTransferred) {
+ updateBuffers(bytesTransferred);
+
+ // return direct buffer to cache if substituted
+ releaseBuffers();
+
+ // release waiters if not already released by timeout
+ synchronized (result) {
+ if (result.isDone())
+ return;
+ enableWriting();
+ if (gatheringWrite) {
+ result.setResult((V)Long.valueOf(bytesTransferred));
+ } else {
+ result.setResult((V)Integer.valueOf(bytesTransferred));
+ }
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+
+ @Override
+ public void failed(int error, IOException x) {
+ // return direct buffer to cache if substituted
+ releaseBuffers();
+
+ // release waiters if not already released by timeout
+ if (!isOpen())
+ x = new AsynchronousCloseException();
+
+ synchronized (result) {
+ if (result.isDone())
+ return;
+ enableWriting();
+ result.setFailure(x);
+ }
+ Invoker.invoke(result.handler(), result);
+ }
+
+ /**
+ * Invoked if timeout expires before it is cancelled
+ */
+ void timeout() {
+ // synchronize on result as the I/O could complete/fail
+ synchronized (result) {
+ if (result.isDone())
+ return;
+
+ // kill further writing before releasing waiters
+ enableWriting(true);
+ result.setFailure(new InterruptedByTimeoutException());
+ }
+
+ // invoke handler without any locks
+ Invoker.invoke(result.handler(), result);
+ }
+ }
+
+ @Override
+ <V extends Number,A> Future<V> writeImpl(ByteBuffer[] bufs,
+ boolean gatheringWrite,
+ long timeout,
+ TimeUnit unit,
+ A attachment,
+ CompletionHandler<V,? super A> handler)
+ {
+ // setup task
+ PendingFuture<V,A> result =
+ new PendingFuture<V,A>(this, handler, attachment);
+ final WriteTask writeTask = new WriteTask<V,A>(bufs, gatheringWrite, result);
+ result.setContext(writeTask);
+
+ // schedule timeout
+ if (timeout > 0L) {
+ Future<?> timeoutTask = iocp.schedule(new Runnable() {
+ public void run() {
+ writeTask.timeout();
+ }
+ }, timeout, unit);
+ result.setTimeoutTask(timeoutTask);
+ }
+
+ // initiate I/O (can only be done from thread in thread pool)
+ Invoker.invokeOnThreadInThreadPool(this, writeTask);
+ return result;
+ }
+
+ // -- Native methods --
+
+ private static native void initIDs();
+
+ private static native int connect0(long socket, boolean preferIPv6,
+ InetAddress remote, int remotePort, long overlapped) throws IOException;
+
+ private static native void updateConnectContext(long socket) throws IOException;
+
+ private static native int read0(long socket, int count, long addres, long overlapped)
+ throws IOException;
+
+ private static native int write0(long socket, int count, long address,
+ long overlapped) throws IOException;
+
+ private static native void shutdown0(long socket, int how) throws IOException;
+
+ private static native void closesocket0(long socket) throws IOException;
+
+ static {
+ Util.load();
+ initIDs();
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java
new file mode 100644
index 000000000..93923103e
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.spi.FileSystemProvider;
+
+/**
+ * Creates default provider on Windows
+ */
+public class DefaultFileSystemProvider {
+ private DefaultFileSystemProvider() { }
+ public static FileSystemProvider create() {
+ return new WindowsFileSystemProvider();
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java b/src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java
new file mode 100644
index 000000000..1e775aee8
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.spi.FileTypeDetector;
+
+public class DefaultFileTypeDetector {
+ private DefaultFileTypeDetector() { }
+
+ public static FileTypeDetector create() {
+ return new RegistryFileTypeDetector();
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java b/src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java
new file mode 100644
index 000000000..dc4b9c0e2
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * File type detector that does lookup of file extension using Windows Registry.
+ */
+
+public class RegistryFileTypeDetector
+ extends AbstractFileTypeDetector
+{
+ public RegistryFileTypeDetector() {
+ super();
+ }
+
+ @Override
+ public String implProbeContentType(FileRef file) throws IOException {
+ if (!(file instanceof Path))
+ return null;
+
+ // get file extension
+ Path name = ((Path)file).getName();
+ if (name == null)
+ return null;
+ String filename = name.toString();
+ int dot = filename.lastIndexOf('.');
+ if ((dot < 0) || (dot == (filename.length()-1)))
+ return null;
+
+ // query HKEY_CLASSES_ROOT\<ext>
+ String key = filename.substring(dot);
+ NativeBuffer keyBuffer = WindowsNativeDispatcher.asNativeBuffer(key);
+ NativeBuffer nameBuffer = WindowsNativeDispatcher.asNativeBuffer("Content Type");
+ try {
+ return queryStringValue(keyBuffer.address(), nameBuffer.address());
+ } finally {
+ nameBuffer.release();
+ keyBuffer.release();
+ }
+ }
+
+ private static native String queryStringValue(long subKey, long name);
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ // nio.dll has dependency on net.dll
+ System.loadLibrary("net");
+ System.loadLibrary("nio");
+ return null;
+ }});
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java b/src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java
new file mode 100644
index 000000000..937aedd85
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.ProviderMismatchException;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Windows implementation of AclFileAttributeView.
+ */
+
+class WindowsAclFileAttributeView
+ extends AbstractAclFileAttributeView
+{
+ /**
+ * typedef struct _SECURITY_DESCRIPTOR {
+ * BYTE Revision;
+ * BYTE Sbz1;
+ * SECURITY_DESCRIPTOR_CONTROL Control;
+ * PSID Owner;
+ * PSID Group;
+ * PACL Sacl;
+ * PACL Dacl;
+ * } SECURITY_DESCRIPTOR;
+ */
+ private static final short SIZEOF_SECURITY_DESCRIPTOR = 20;
+
+ private final WindowsPath file;
+ private final boolean followLinks;
+
+ WindowsAclFileAttributeView(WindowsPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ // permision check
+ private void checkAccess(WindowsPath file,
+ boolean checkRead,
+ boolean checkWrite)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (checkRead)
+ sm.checkRead(file.getPathForPermissionCheck());
+ if (checkWrite)
+ sm.checkWrite(file.getPathForPermissionCheck());
+ sm.checkPermission(new RuntimePermission("accessUserInformation"));
+ }
+ }
+
+ // invokes GetFileSecurity to get requested security information
+ static NativeBuffer getFileSecurity(String path, int request)
+ throws IOException
+ {
+ // invoke get to buffer size
+ int size = 0;
+ try {
+ size = GetFileSecurity(path, request, 0L, 0);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(path);
+ }
+ assert size > 0;
+
+ // allocate buffer and re-invoke to get security information
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ for (;;) {
+ int newSize = GetFileSecurity(path, request, buffer.address(), size);
+ if (newSize <= size)
+ return buffer;
+
+ // buffer was insufficient
+ buffer.release();
+ buffer = NativeBuffers.getNativeBuffer(newSize);
+ size = newSize;
+ }
+ } catch (WindowsException x) {
+ buffer.release();
+ x.rethrowAsIOException(path);
+ return null;
+ }
+ }
+
+ @Override
+ public UserPrincipal getOwner()
+ throws IOException
+ {
+ checkAccess(file, true, false);
+
+ // GetFileSecurity does not follow links so when following links we
+ // need the final target
+ String path = WindowsLinkSupport.getFinalPath(file, followLinks);
+ NativeBuffer buffer = getFileSecurity(path, OWNER_SECURITY_INFORMATION);
+ try {
+ // get the address of the SID
+ long sidAddress = GetSecurityDescriptorOwner(buffer.address());
+ if (sidAddress == 0L)
+ throw new IOException("no owner");
+ return WindowsUserPrincipals.fromSid(sidAddress);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ return null;
+ } finally {
+ buffer.release();
+ }
+ }
+
+ @Override
+ public List<AclEntry> getAcl()
+ throws IOException
+ {
+ checkAccess(file, true, false);
+
+ // GetFileSecurity does not follow links so when following links we
+ // need the final target
+ String path = WindowsLinkSupport.getFinalPath(file, followLinks);
+
+ // ALLOW and DENY entries in DACL;
+ // AUDIT entries in SACL (ignore for now as it requires privileges)
+ NativeBuffer buffer = getFileSecurity(path, DACL_SECURITY_INFORMATION);
+ try {
+ return WindowsSecurityDescriptor.getAcl(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+
+ @Override
+ public void setOwner(UserPrincipal obj)
+ throws IOException
+ {
+ if (obj == null)
+ throw new NullPointerException("'owner' is null");
+ if (!(obj instanceof WindowsUserPrincipals.User))
+ throw new ProviderMismatchException();
+ WindowsUserPrincipals.User owner = (WindowsUserPrincipals.User)obj;
+
+ // permission check
+ checkAccess(file, false, true);
+
+ // SetFileSecurity does not follow links so when following links we
+ // need the final target
+ String path = WindowsLinkSupport.getFinalPath(file, followLinks);
+
+ // ConvertStringSidToSid allocates memory for SID so must invoke
+ // LocalFree to free it when we are done
+ long pOwner = 0L;
+ try {
+ pOwner = ConvertStringSidToSid(owner.sidString());
+ } catch (WindowsException x) {
+ throw new IOException("Failed to get SID for " + owner.getName()
+ + ": " + x.errorString());
+ }
+
+ // Allocate buffer for security descriptor, initialize it, set
+ // owner information and update the file.
+ try {
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR);
+ try {
+ InitializeSecurityDescriptor(buffer.address());
+ SetSecurityDescriptorOwner(buffer.address(), pOwner);
+ // may need SeRestorePrivilege to set the owner
+ WindowsSecurity.Privilege priv =
+ WindowsSecurity.enablePrivilege("SeRestorePrivilege");
+ try {
+ SetFileSecurity(path,
+ OWNER_SECURITY_INFORMATION,
+ buffer.address());
+ } finally {
+ priv.drop();
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ } finally {
+ buffer.release();
+ }
+ } finally {
+ LocalFree(pOwner);
+ }
+ }
+
+ @Override
+ public void setAcl(List<AclEntry> acl) throws IOException {
+ checkAccess(file, false, true);
+
+ // SetFileSecurity does not follow links so when following links we
+ // need the final target
+ String path = WindowsLinkSupport.getFinalPath(file, followLinks);
+ WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.create(acl);
+ try {
+ SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd.address());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ } finally {
+ sd.release();
+ }
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java b/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java
new file mode 100644
index 000000000..f559166bb
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.channels.*;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.*;
+
+import com.sun.nio.file.ExtendedOpenOption;
+
+import sun.nio.ch.FileChannelImpl;
+import sun.nio.ch.ThreadPool;
+import sun.nio.ch.WindowsAsynchronousFileChannelImpl;
+import sun.misc.SharedSecrets;
+import sun.misc.JavaIOFileDescriptorAccess;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Factory to create FileChannels and AsynchronousFileChannels.
+ */
+
+class WindowsChannelFactory {
+ private static final JavaIOFileDescriptorAccess fdAccess =
+ SharedSecrets.getJavaIOFileDescriptorAccess();
+
+ private WindowsChannelFactory() { }
+
+ /**
+ * Do not follow reparse points when opening an existing file. Do not fail
+ * if the file is a reparse point.
+ */
+ static final OpenOption OPEN_REPARSE_POINT = new OpenOption() { };
+
+ /**
+ * Represents the flags from a user-supplied set of open options.
+ */
+ private static class Flags {
+ boolean read;
+ boolean write;
+ boolean append;
+ boolean truncateExisting;
+ boolean create;
+ boolean createNew;
+ boolean deleteOnClose;
+ boolean sparse;
+ boolean overlapped;
+ boolean sync;
+ boolean dsync;
+
+ // non-standard
+ boolean shareRead = true;
+ boolean shareWrite = true;
+ boolean shareDelete = true;
+ boolean noFollowLinks;
+ boolean openReparsePoint;
+
+ static Flags toFlags(Set<? extends OpenOption> options) {
+ Flags flags = new Flags();
+ for (OpenOption option: options) {
+ if (option instanceof StandardOpenOption) {
+ switch ((StandardOpenOption)option) {
+ case READ : flags.read = true; break;
+ case WRITE : flags.write = true; break;
+ case APPEND : flags.append = true; break;
+ case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
+ case CREATE : flags.create = true; break;
+ case CREATE_NEW : flags.createNew = true; break;
+ case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
+ case SPARSE : flags.sparse = true; break;
+ case SYNC : flags.sync = true; break;
+ case DSYNC : flags.dsync = true; break;
+ default: throw new UnsupportedOperationException();
+ }
+ continue;
+ }
+ if (option instanceof ExtendedOpenOption) {
+ switch ((ExtendedOpenOption)option) {
+ case NOSHARE_READ : flags.shareRead = false; break;
+ case NOSHARE_WRITE : flags.shareWrite = false; break;
+ case NOSHARE_DELETE : flags.shareDelete = false; break;
+ default: throw new UnsupportedOperationException();
+ }
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ flags.noFollowLinks = true;
+ continue;
+ }
+ if (option == OPEN_REPARSE_POINT) {
+ flags.openReparsePoint = true;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException();
+ }
+ return flags;
+ }
+ }
+
+ /**
+ * Open/creates file, returning FileChannel to access the file
+ *
+ * @param pathForWindows
+ * The path of the file to open/create
+ * @param pathToCheck
+ * The path used for permission checks (if security manager)
+ */
+ static FileChannel newFileChannel(String pathForWindows,
+ String pathToCheck,
+ Set<? extends OpenOption> options,
+ long pSecurityDescriptor)
+ throws WindowsException
+ {
+ Flags flags = Flags.toFlags(options);
+
+ // default is reading; append => writing
+ if (!flags.read && !flags.write) {
+ if (flags.append) {
+ flags.write = true;
+ } else {
+ flags.read = true;
+ }
+ }
+
+ // validation
+ if (flags.read && flags.append)
+ throw new IllegalArgumentException("READ + APPEND not allowed");
+ if (flags.append && flags.truncateExisting)
+ throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
+
+ FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
+ return FileChannelImpl.open(fdObj, flags.read, flags.write, null);
+ }
+
+ /**
+ * Open/creates file, returning AsynchronousFileChannel to access the file
+ *
+ * @param pathForWindows
+ * The path of the file to open/create
+ * @param pathToCheck
+ * The path used for permission checks (if security manager)
+ * @param pool
+ * The thread pool that the channel is associated with
+ */
+ static AsynchronousFileChannel newAsynchronousFileChannel(String pathForWindows,
+ String pathToCheck,
+ Set<? extends OpenOption> options,
+ long pSecurityDescriptor,
+ ThreadPool pool)
+ throws IOException
+ {
+ Flags flags = Flags.toFlags(options);
+
+ // Overlapped I/O required
+ flags.overlapped = true;
+
+ // default is reading
+ if (!flags.read && !flags.write) {
+ flags.read = true;
+ }
+
+ // validation
+ if (flags.append)
+ throw new UnsupportedOperationException("APPEND not allowed");
+
+ // open file for overlapped I/O
+ FileDescriptor fdObj;
+ try {
+ fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(pathForWindows);
+ return null;
+ }
+
+ // create the AsynchronousFileChannel
+ try {
+ return WindowsAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool);
+ } catch (IOException x) {
+ // IOException is thrown if the file handle cannot be associated
+ // with the completion port. All we can do is close the file.
+ long handle = fdAccess.getHandle(fdObj);
+ CloseHandle(handle);
+ throw x;
+ }
+ }
+
+ /**
+ * Opens file based on parameters and options, returning a FileDescriptor
+ * encapsulating the handle to the open file.
+ */
+ private static FileDescriptor open(String pathForWindows,
+ String pathToCheck,
+ Flags flags,
+ long pSecurityDescriptor)
+ throws WindowsException
+ {
+ // set to true if file must be truncated after open
+ boolean truncateAfterOpen = false;
+
+ // map options
+ int dwDesiredAccess = 0;
+ if (flags.read)
+ dwDesiredAccess |= GENERIC_READ;
+ if (flags.write)
+ dwDesiredAccess |= (flags.append) ? FILE_APPEND_DATA : GENERIC_WRITE;
+
+ int dwShareMode = 0;
+ if (flags.shareRead)
+ dwShareMode |= FILE_SHARE_READ;
+ if (flags.shareWrite)
+ dwShareMode |= FILE_SHARE_WRITE;
+ if (flags.shareDelete)
+ dwShareMode |= FILE_SHARE_DELETE;
+
+ int dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ int dwCreationDisposition = OPEN_EXISTING;
+ if (flags.write) {
+ if (flags.createNew) {
+ dwCreationDisposition = CREATE_NEW;
+ // force create to fail if file is orphaned reparse point
+ dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
+ } else {
+ if (flags.create)
+ dwCreationDisposition = OPEN_ALWAYS;
+ if (flags.truncateExisting) {
+ // Windows doesn't have a creation disposition that exactly
+ // corresponds to CREATE + TRUNCATE_EXISTING so we use
+ // the OPEN_ALWAYS mode and then truncate the file.
+ if (dwCreationDisposition == OPEN_ALWAYS) {
+ truncateAfterOpen = true;
+ } else {
+ dwCreationDisposition = TRUNCATE_EXISTING;
+ }
+ }
+ }
+ }
+
+ if (flags.dsync || flags.sync)
+ dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
+ if (flags.overlapped)
+ dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
+ if (flags.deleteOnClose)
+ dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
+
+ // NOFOLLOW_LINKS and NOFOLLOW_REPARSEPOINT mean open reparse point
+ boolean okayToFollowLinks = true;
+ if (dwCreationDisposition != CREATE_NEW &&
+ (flags.noFollowLinks ||
+ flags.openReparsePoint ||
+ flags.deleteOnClose))
+ {
+ if (flags.noFollowLinks || flags.deleteOnClose)
+ okayToFollowLinks = false;
+ dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+
+ // permission check
+ if (pathToCheck != null) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (flags.read)
+ sm.checkRead(pathToCheck);
+ if (flags.write)
+ sm.checkWrite(pathToCheck);
+ if (flags.deleteOnClose)
+ sm.checkDelete(pathToCheck);
+ }
+ }
+
+ // open file
+ long handle = CreateFile(pathForWindows,
+ dwDesiredAccess,
+ dwShareMode,
+ pSecurityDescriptor,
+ dwCreationDisposition,
+ dwFlagsAndAttributes);
+
+ // make sure this isn't a symbolic link.
+ if (!okayToFollowLinks) {
+ try {
+ if (WindowsFileAttributes.readAttributes(handle).isSymbolicLink())
+ throw new WindowsException("File is symbolic link");
+ } catch (WindowsException x) {
+ CloseHandle(handle);
+ throw x;
+ }
+ }
+
+ // truncate file (for CREATE + TRUNCATE_EXISTING case)
+ if (truncateAfterOpen) {
+ try {
+ SetEndOfFile(handle);
+ } catch (WindowsException x) {
+ CloseHandle(handle);
+ throw x;
+ }
+ }
+
+ // make the file sparse if needed
+ if (dwCreationDisposition == CREATE_NEW && flags.sparse) {
+ try {
+ DeviceIoControlSetSparse(handle);
+ } catch (WindowsException x) {
+ // ignore as sparse option is hint
+ }
+ }
+
+ // create FileDescriptor and return
+ FileDescriptor fdObj = new FileDescriptor();
+ fdAccess.setHandle(fdObj, handle);
+ return fdObj;
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsConstants.java b/src/windows/classes/sun/nio/fs/WindowsConstants.java
new file mode 100644
index 000000000..f2619ac80
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsConstants.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Win32 APIs constants.
+ */
+
+class WindowsConstants {
+ private WindowsConstants() { }
+
+ // general
+ public static final long INVALID_HANDLE_VALUE = -1L;
+
+ // generic rights
+ public static final int GENERIC_READ = 0x80000000;
+ public static final int GENERIC_WRITE = 0x40000000;
+
+ // share modes
+ public static final int FILE_SHARE_READ = 0x00000001;
+ public static final int FILE_SHARE_WRITE = 0x00000002;
+ public static final int FILE_SHARE_DELETE = 0x00000004;
+
+ // creation modes
+ public static final int CREATE_NEW = 1;
+ public static final int CREATE_ALWAYS = 2;
+ public static final int OPEN_EXISTING = 3;
+ public static final int OPEN_ALWAYS = 4;
+ public static final int TRUNCATE_EXISTING = 5;
+
+ // attributes and flags
+ public static final int FILE_ATTRIBUTE_READONLY = 0x00000001;
+ public static final int FILE_ATTRIBUTE_HIDDEN = 0x00000002;
+ public static final int FILE_ATTRIBUTE_SYSTEM = 0x00000004;
+ public static final int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
+ public static final int FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
+ public static final int FILE_ATTRIBUTE_DEVICE = 0x00000040;
+ public static final int FILE_ATTRIBUTE_NORMAL = 0x00000080;
+ public static final int FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
+ public static final int FILE_FLAG_NO_BUFFERING = 0x20000000;
+ public static final int FILE_FLAG_OVERLAPPED = 0x40000000;
+ public static final int FILE_FLAG_WRITE_THROUGH = 0x80000000;
+ public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+ public static final int FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
+ public static final int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
+
+ // stream ids
+ public static final int BACKUP_ALTERNATE_DATA = 0x00000004;
+ public static final int BACKUP_SPARSE_BLOCK = 0x00000009;
+
+ // reparse point/symbolic link related constants
+ public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C;
+ public static final int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
+ public static final int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1;
+
+ // volume flags
+ public static final int FILE_CASE_SENSITIVE_SEARCH = 0x00000001;
+ public static final int FILE_CASE_PRESERVED_NAMES = 0x00000002;
+ public static final int FILE_PERSISTENT_ACLS = 0x00000008;
+ public static final int FILE_VOLUME_IS_COMPRESSED = 0x00008000;
+ public static final int FILE_NAMED_STREAMS = 0x00040000;
+ public static final int FILE_READ_ONLY_VOLUME = 0x00080000;
+
+ // error codes
+ public static final int ERROR_FILE_NOT_FOUND = 2;
+ public static final int ERROR_PATH_NOT_FOUND = 3;
+ public static final int ERROR_ACCESS_DENIED = 5;
+ public static final int ERROR_INVALID_HANDLE = 6;
+ public static final int ERROR_INVALID_DATA = 13;
+ public static final int ERROR_NOT_SAME_DEVICE = 17;
+ public static final int ERROR_NOT_READY = 21;
+ public static final int ERROR_FILE_EXISTS = 80;
+ public static final int ERROR_DISK_FULL = 112;
+ public static final int ERROR_INSUFFICIENT_BUFFER = 122;
+ public static final int ERROR_INVALID_LEVEL = 124;
+ public static final int ERROR_DIR_NOT_EMPTY = 145;
+ public static final int ERROR_ALREADY_EXISTS = 183;
+ public static final int ERROR_DIRECTORY = 267;
+ public static final int ERROR_NOTIFY_ENUM_DIR = 1022;
+ public static final int ERROR_NONE_MAPPED = 1332;
+ public static final int ERROR_NOT_A_REPARSE_POINT = 4390;
+ public static final int ERROR_INVALID_REPARSE_DATA = 4392;
+
+ // notify filters
+ public static final int FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001;
+ public static final int FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002;
+ public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004;
+ public static final int FILE_NOTIFY_CHANGE_SIZE = 0x00000008;
+ public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010;
+ public static final int FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020;
+ public static final int FILE_NOTIFY_CHANGE_CREATION = 0x00000040;
+ public static final int FILE_NOTIFY_CHANGE_SECURITY = 0x00000100;
+
+ // notify actions
+ public final static int FILE_ACTION_ADDED = 0x00000001;
+ public final static int FILE_ACTION_REMOVED = 0x00000002;
+ public final static int FILE_ACTION_MODIFIED = 0x00000003;
+ public final static int FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
+ public final static int FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
+
+ // copy flags
+ public static final int COPY_FILE_FAIL_IF_EXISTS = 0x00000001;
+ public static final int COPY_FILE_COPY_SYMLINK = 0x00000800;
+
+ // move flags
+ public static final int MOVEFILE_REPLACE_EXISTING = 0x00000001;
+ public static final int MOVEFILE_COPY_ALLOWED = 0x00000002;
+
+ // drive types
+ public static final int DRIVE_UNKNOWN = 0;
+ public static final int DRIVE_NO_ROOT_DIR = 1;
+ public static final int DRIVE_REMOVABLE = 2;
+ public static final int DRIVE_FIXED = 3;
+ public static final int DRIVE_REMOTE = 4;
+ public static final int DRIVE_CDROM = 5;
+ public static final int DRIVE_RAMDISK = 6;
+
+ // file security
+ public static final int OWNER_SECURITY_INFORMATION = 0x00000001;
+ public static final int GROUP_SECURITY_INFORMATION = 0x00000002;
+ public static final int DACL_SECURITY_INFORMATION = 0x00000004;
+ public static final int SACL_SECURITY_INFORMATION = 0x00000008;
+
+ public static final int SidTypeUser = 1;
+ public static final int SidTypeGroup = 2;
+ public static final int SidTypeDomain = 3;
+ public static final int SidTypeAlias = 4;
+ public static final int SidTypeWellKnownGroup = 5;
+ public static final int SidTypeDeletedAccount = 6;
+ public static final int SidTypeInvalid = 7;
+ public static final int SidTypeUnknown = 8;
+ public static final int SidTypeComputer= 9;
+
+ public static final byte ACCESS_ALLOWED_ACE_TYPE = 0x0;
+ public static final byte ACCESS_DENIED_ACE_TYPE = 0x1;
+
+ public static final byte OBJECT_INHERIT_ACE = 0x1;
+ public static final byte CONTAINER_INHERIT_ACE = 0x2;
+ public static final byte NO_PROPAGATE_INHERIT_ACE = 0x4;
+ public static final byte INHERIT_ONLY_ACE = 0x8;
+
+ public static final int DELETE = 0x00010000;
+ public static final int READ_CONTROL = 0x00020000;
+ public static final int WRITE_DAC = 0x00040000;
+ public static final int WRITE_OWNER = 0x00080000;
+ public static final int SYNCHRONIZE = 0x00100000;
+
+ public static final int FILE_LIST_DIRECTORY = 0x0001;
+ public static final int FILE_READ_DATA = 0x0001;
+ public static final int FILE_WRITE_DATA = 0x0002;
+ public static final int FILE_APPEND_DATA = 0x0004;
+ public static final int FILE_READ_EA = 0x0008;
+ public static final int FILE_WRITE_EA = 0x0010;
+ public static final int FILE_EXECUTE = 0x0020;
+ public static final int FILE_DELETE_CHILD = 0x0040;
+ public static final int FILE_READ_ATTRIBUTES = 0x0080;
+ public static final int FILE_WRITE_ATTRIBUTES = 0x0100;
+
+ // operating system security
+ public static final int TOKEN_DUPLICATE = 0x0002;
+ public static final int TOKEN_IMPERSONATE = 0x0004;
+ public static final int TOKEN_QUERY = 0x0008;
+ public static final int TOKEN_ADJUST_PRIVILEGES = 0x0020;
+
+ public static final int SE_PRIVILEGE_ENABLED = 0x00000002;
+
+ public static final int TokenUser = 1;
+ public static final int PROCESS_QUERY_INFORMATION = 0x0400;
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java b/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java
new file mode 100644
index 000000000..fa0b14834
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Iterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import java.io.IOException;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Windows implementation of DirectoryStream
+ */
+
+class WindowsDirectoryStream
+ implements DirectoryStream<Path>
+{
+ private final WindowsPath dir;
+ private final DirectoryStream.Filter<? super Path> filter;
+
+ // handle to directory
+ private final long handle;
+ // first entry in the directory
+ private final String firstName;
+
+ // buffer for WIN32_FIND_DATA structure that receives information about file
+ private final NativeBuffer findDataBuffer;
+
+ private final Object closeLock = new Object();
+
+ // need closeLock to access these
+ private boolean isOpen = true;
+ private Iterator<Path> iterator;
+
+
+ WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter)
+ throws IOException
+ {
+ this.dir = dir;
+ this.filter = filter;
+
+ try {
+ // Need to append * or \* to match entries in directory.
+ String search = dir.getPathForWin32Calls();
+ char last = search.charAt(search.length() -1);
+ if (last == ':' || last == '\\') {
+ search += "*";
+ } else {
+ search += "\\*";
+ }
+
+ FirstFile first = FindFirstFile(search);
+ this.handle = first.handle();
+ this.firstName = first.name();
+ this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
+ } catch (WindowsException x) {
+ if (x.lastError() == ERROR_DIRECTORY) {
+ throw new NotDirectoryException(dir.getPathForExceptionMessage());
+ }
+ x.rethrowAsIOException(dir);
+
+ // keep compiler happy
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ synchronized (closeLock) {
+ if (!isOpen)
+ return;
+ isOpen = false;
+ }
+ findDataBuffer.release();
+ try {
+ FindClose(handle);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(dir);
+ }
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ if (!isOpen) {
+ throw new IllegalStateException("Directory stream is closed");
+ }
+ synchronized (this) {
+ if (iterator != null)
+ throw new IllegalStateException("Iterator already obtained");
+ iterator = new WindowsDirectoryIterator(firstName);
+ return iterator;
+ }
+ }
+
+ private static void throwAsConcurrentModificationException(Throwable t) {
+ ConcurrentModificationException cme = new ConcurrentModificationException();
+ cme.initCause(t);
+ throw cme;
+ }
+
+ private class WindowsDirectoryIterator implements Iterator<Path> {
+ private boolean atEof;
+ private String first;
+ private Path nextEntry;
+ private Path prevEntry;
+
+ WindowsDirectoryIterator(String first) {
+ atEof = false;
+ this.first = first;
+ }
+
+ // applies filter and also ignores "." and ".."
+ private Path acceptEntry(String s, BasicFileAttributes attrs) {
+ if (s.equals(".") || s.equals(".."))
+ return null;
+ if (dir.needsSlashWhenResolving()) {
+ StringBuilder sb = new StringBuilder(dir.toString());
+ sb.append('\\');
+ sb.append(s);
+ s = sb.toString();
+ } else {
+ s = dir + s;
+ }
+ Path entry = WindowsPath
+ .createFromNormalizedPath(dir.getFileSystem(), s, attrs);
+ if (filter.accept(entry)) {
+ return entry;
+ } else {
+ return null;
+ }
+ }
+
+ // reads next directory entry
+ private Path readNextEntry() {
+ // handle first element returned by search
+ if (first != null) {
+ nextEntry = acceptEntry(first, null);
+ first = null;
+ if (nextEntry != null)
+ return nextEntry;
+ }
+
+ for (;;) {
+ String name = null;
+ WindowsFileAttributes attrs;
+
+ // synchronize on closeLock to prevent close while reading
+ synchronized (closeLock) {
+ if (!isOpen)
+ throwAsConcurrentModificationException(new
+ IllegalStateException("Directory stream is closed"));
+ try {
+ name = FindNextFile(handle, findDataBuffer.address());
+ if (name == null) {
+ // NO_MORE_FILES
+ return null;
+ }
+ } catch (WindowsException x) {
+ try {
+ x.rethrowAsIOException(dir);
+ } catch (IOException ioe) {
+ throwAsConcurrentModificationException(ioe);
+ }
+ }
+
+ // grab the attributes from the WIN32_FIND_DATA structure
+ // (needs to be done while holding closeLock because close
+ // will release the buffer)
+ attrs = WindowsFileAttributes
+ .fromFindData(findDataBuffer.address());
+ }
+
+ // return entry if accepted by filter
+ Path entry = acceptEntry(name, attrs);
+ if (entry != null)
+ return entry;
+ }
+ }
+
+ @Override
+ public synchronized boolean hasNext() {
+ if (nextEntry == null && !atEof) {
+ nextEntry = readNextEntry();
+ atEof = (nextEntry == null);
+ }
+ return nextEntry != null;
+ }
+
+ @Override
+ public synchronized Path next() {
+ if (nextEntry == null) {
+ if (!atEof) {
+ nextEntry = readNextEntry();
+ }
+ if (nextEntry == null) {
+ atEof = true;
+ throw new NoSuchElementException();
+ }
+ }
+ prevEntry = nextEntry;
+ nextEntry = null;
+ return prevEntry;
+ }
+
+ @Override
+ public void remove() {
+ if (!isOpen) {
+ throw new IllegalStateException("Directory stream is closed");
+ }
+ Path entry;
+ synchronized (this) {
+ if (prevEntry == null)
+ throw new IllegalStateException("no last element");
+ entry = prevEntry;
+ prevEntry = null;
+ }
+ try {
+ entry.delete(true);
+ } catch (IOException ioe) {
+ throwAsConcurrentModificationException(ioe);
+ } catch (SecurityException se) {
+ throwAsConcurrentModificationException(se);
+ }
+ }
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsException.java b/src/windows/classes/sun/nio/fs/WindowsException.java
new file mode 100644
index 000000000..7da722076
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsException.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Internal exception thrown when a Win32 calls fails.
+ */
+
+class WindowsException extends Exception {
+ static final long serialVersionUID = 2765039493083748820L;
+
+ private int lastError;
+ private String msg;
+
+ WindowsException(int lastError) {
+ this.lastError = lastError;
+ this.msg = null;
+ }
+
+ WindowsException(String msg) {
+ this.lastError = 0;
+ this.msg = msg;
+ }
+
+ int lastError() {
+ return lastError;
+ }
+
+ String errorString() {
+ if (msg == null) {
+ msg = WindowsNativeDispatcher.FormatMessage(lastError);
+ if (msg == null) {
+ msg = "Unknown error: 0x" + Integer.toHexString(lastError);
+ }
+ }
+ return msg;
+ }
+
+ @Override
+ public String getMessage() {
+ return errorString();
+ }
+
+ private IOException translateToIOException(String file, String other) {
+ // not created with last error
+ if (lastError() == 0)
+ return new IOException(errorString());
+
+ // handle specific cases
+ if (lastError() == ERROR_FILE_NOT_FOUND || lastError() == ERROR_PATH_NOT_FOUND)
+ return new NoSuchFileException(file, other, null);
+ if (lastError() == ERROR_FILE_EXISTS || lastError() == ERROR_ALREADY_EXISTS)
+ return new FileAlreadyExistsException(file, other, null);
+ if (lastError() == ERROR_ACCESS_DENIED)
+ return new AccessDeniedException(file, other, null);
+
+ // fallback to the more general exception
+ return new FileSystemException(file, other, errorString());
+ }
+
+ void rethrowAsIOException(String file) throws IOException {
+ IOException x = translateToIOException(file, null);
+ throw x;
+ }
+
+ void rethrowAsIOException(WindowsPath file, WindowsPath other) throws IOException {
+ String a = (file == null) ? null : file.getPathForExceptionMessage();
+ String b = (other == null) ? null : other.getPathForExceptionMessage();
+ IOException x = translateToIOException(a, b);
+ throw x;
+ }
+
+ void rethrowAsIOException(WindowsPath file) throws IOException {
+ rethrowAsIOException(file, null);
+ }
+
+ IOException asIOException(WindowsPath file) {
+ return translateToIOException(file.getPathForExceptionMessage(), null);
+ }
+
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java b/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java
new file mode 100644
index 000000000..39c34a10e
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+class WindowsFileAttributeViews {
+
+ private static class Basic extends AbstractBasicFileAttributeView {
+ final WindowsPath file;
+ final boolean followLinks;
+
+ Basic(WindowsPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ @Override
+ public WindowsFileAttributes readAttributes() throws IOException {
+ try {
+ return WindowsFileAttributes.get(file, followLinks);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ }
+
+ /**
+ * Parameter values in Windows times.
+ */
+ void setFileTimes(long createTime, long lastAccessTime, long lastWriteTime)
+ throws IOException
+ {
+ long handle = -1L;
+ try {
+ int flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (!followLinks && file.getFileSystem().supportsLinks())
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+ handle = CreateFile(file.getPathForWin32Calls(),
+ FILE_WRITE_ATTRIBUTES,
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
+ OPEN_EXISTING,
+ flags);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ }
+
+ // update attributes
+ try {
+ SetFileTime(handle, createTime, lastAccessTime, lastWriteTime);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ } finally {
+ CloseHandle(handle);
+ }
+ }
+
+ @Override
+ public void setTimes(Long lastModifiedTime,
+ Long lastAccessTime,
+ Long createTime,
+ TimeUnit unit) throws IOException
+ {
+ file.checkWrite();
+
+ // if all null then do nothing
+ if (lastModifiedTime == null && lastAccessTime == null &&
+ createTime == null)
+ {
+ // no effect
+ return;
+ }
+
+ // null => no change
+ // -1 => change to current time
+ long now = System.currentTimeMillis();
+ long modTime = 0L, accTime = 0L, crTime = 0L;
+ if (lastModifiedTime != null) {
+ if (lastModifiedTime < 0L) {
+ if (lastModifiedTime != -1L)
+ throw new IllegalArgumentException();
+ modTime = now;
+ } else {
+ modTime = TimeUnit.MILLISECONDS.convert(lastModifiedTime, unit);
+ }
+ modTime = WindowsFileAttributes.toWindowsTime(modTime);
+ }
+ if (lastAccessTime != null) {
+ if (lastAccessTime < 0L) {
+ if (lastAccessTime != -1L)
+ throw new IllegalArgumentException();
+ accTime = now;
+ } else {
+ accTime = TimeUnit.MILLISECONDS.convert(lastAccessTime, unit);
+ }
+ accTime = WindowsFileAttributes.toWindowsTime(accTime);
+ }
+ if (createTime != null) {
+ if (createTime < 0L) {
+ if (createTime != -1L)
+ throw new IllegalArgumentException();
+ crTime = now;
+ } else {
+ crTime = TimeUnit.MILLISECONDS.convert(createTime, unit);
+ }
+ crTime = WindowsFileAttributes.toWindowsTime(crTime);
+ }
+
+ setFileTimes(crTime, accTime, modTime);
+ }
+ }
+
+ static class Dos extends Basic implements DosFileAttributeView {
+ private static final String READONLY_NAME = "readonly";
+ private static final String ARCHIVE_NAME = "archive";
+ private static final String SYSTEM_NAME = "system";
+ private static final String HIDDEN_NAME = "hidden";
+ private static final String ATTRIBUTES_NAME = "attributes";
+
+ Dos(WindowsPath file, boolean followLinks) {
+ super(file, followLinks);
+ }
+
+ @Override
+ public String name() {
+ return "dos";
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(READONLY_NAME))
+ return readAttributes().isReadOnly();
+ if (attribute.equals(ARCHIVE_NAME))
+ return readAttributes().isArchive();
+ if (attribute.equals(SYSTEM_NAME))
+ return readAttributes().isSystem();
+ if (attribute.equals(HIDDEN_NAME))
+ return readAttributes().isHidden();
+ // implementation specific
+ if (attribute.equals(ATTRIBUTES_NAME))
+ return readAttributes().attributes();
+ return super.getAttribute(attribute);
+ }
+
+ @Override
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ if (attribute.equals(READONLY_NAME)) {
+ setReadOnly((Boolean)value);
+ return;
+ }
+ if (attribute.equals(ARCHIVE_NAME)) {
+ setArchive((Boolean)value);
+ return;
+ }
+ if (attribute.equals(SYSTEM_NAME)) {
+ setSystem((Boolean)value);
+ return;
+ }
+ if (attribute.equals(HIDDEN_NAME)) {
+ setHidden((Boolean)value);
+ return;
+ }
+ super.setAttribute(attribute, value);
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String[] rest)
+ throws IOException
+ {
+ AttributesBuilder builder = AttributesBuilder.create(first, rest);
+ WindowsFileAttributes attrs = readAttributes();
+ addBasicAttributesToBuilder(attrs, builder);
+ if (builder.match(READONLY_NAME))
+ builder.add(READONLY_NAME, attrs.isReadOnly());
+ if (builder.match(ARCHIVE_NAME))
+ builder.add(ARCHIVE_NAME, attrs.isArchive());
+ if (builder.match(SYSTEM_NAME))
+ builder.add(SYSTEM_NAME, attrs.isSystem());
+ if (builder.match(HIDDEN_NAME))
+ builder.add(HIDDEN_NAME, attrs.isHidden());
+ if (builder.match(ATTRIBUTES_NAME))
+ builder.add(ATTRIBUTES_NAME, attrs.attributes());
+ return builder.unmodifiableMap();
+ }
+
+ /**
+ * Update DOS attributes
+ */
+ private void updateAttributes(int flag, boolean enable)
+ throws IOException
+ {
+ file.checkWrite();
+
+ // GetFileAttribtues & SetFileAttributes do not follow links so when
+ // following links we need the final target
+ String path = WindowsLinkSupport.getFinalPath(file, followLinks);
+ try {
+ int oldValue = GetFileAttributes(path);
+ int newValue = oldValue;
+ if (enable) {
+ newValue |= flag;
+ } else {
+ newValue &= ~flag;
+ }
+ if (newValue != oldValue) {
+ SetFileAttributes(path, newValue);
+ }
+ } catch (WindowsException x) {
+ // don't reveal target in exception
+ x.rethrowAsIOException(file);
+ }
+ }
+
+ @Override
+ public void setReadOnly(boolean value) throws IOException {
+ updateAttributes(FILE_ATTRIBUTE_READONLY, value);
+ }
+
+ @Override
+ public void setHidden(boolean value) throws IOException {
+ updateAttributes(FILE_ATTRIBUTE_HIDDEN, value);
+ }
+
+ @Override
+ public void setArchive(boolean value) throws IOException {
+ updateAttributes(FILE_ATTRIBUTE_ARCHIVE, value);
+ }
+
+ @Override
+ public void setSystem(boolean value) throws IOException {
+ updateAttributes(FILE_ATTRIBUTE_SYSTEM, value);
+ }
+
+ // package-private
+ // Copy given attributes to the file.
+ void setAttributes(WindowsFileAttributes attrs)
+ throws IOException
+ {
+ // copy DOS attributes to target
+ int flags = 0;
+ if (attrs.isReadOnly()) flags |= FILE_ATTRIBUTE_READONLY;
+ if (attrs.isHidden()) flags |= FILE_ATTRIBUTE_HIDDEN;
+ if (attrs.isArchive()) flags |= FILE_ATTRIBUTE_ARCHIVE;
+ if (attrs.isSystem()) flags |= FILE_ATTRIBUTE_SYSTEM;
+ updateAttributes(flags, true);
+
+ // copy file times to target - must be done after updating FAT attributes
+ // as otherwise the last modified time may be wrong.
+ setFileTimes(
+ WindowsFileAttributes.toWindowsTime(attrs.creationTime()),
+ WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()),
+ WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()));
+ }
+ }
+
+ static BasicFileAttributeView createBasicView(WindowsPath file, boolean followLinks) {
+ return new Basic(file, followLinks);
+ }
+
+ static WindowsFileAttributeViews.Dos createDosView(WindowsPath file, boolean followLinks) {
+ return new Dos(file, followLinks);
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java b/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java
new file mode 100644
index 000000000..ce053cf90
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.concurrent.TimeUnit;
+import java.security.AccessController;
+import sun.misc.Unsafe;
+import sun.security.action.GetPropertyAction;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Windows implementation of DosFileAttributes/BasicFileAttributes
+ */
+
+class WindowsFileAttributes
+ implements DosFileAttributes
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ /*
+ * typedef struct _BY_HANDLE_FILE_INFORMATION {
+ * DWORD dwFileAttributes;
+ * FILETIME ftCreationTime;
+ * FILETIME ftLastAccessTime;
+ * FILETIME ftLastWriteTime;
+ * DWORD dwVolumeSerialNumber;
+ * DWORD nFileSizeHigh;
+ * DWORD nFileSizeLow;
+ * DWORD nNumberOfLinks;
+ * DWORD nFileIndexHigh;
+ * DWORD nFileIndexLow;
+ * } BY_HANDLE_FILE_INFORMATION;
+ */
+ private static final short SIZEOF_FILE_INFORMATION = 52;
+ private static final short OFFSETOF_FILE_INFORMATION_ATTRIBUTES = 0;
+ private static final short OFFSETOF_FILE_INFORMATION_CREATETIME = 4;
+ private static final short OFFSETOF_FILE_INFORMATION_LASTACCESSTIME = 12;
+ private static final short OFFSETOF_FILE_INFORMATION_LASTWRITETIME = 20;
+ private static final short OFFSETOF_FILE_INFORMATION_VOLSERIALNUM = 28;
+ private static final short OFFSETOF_FILE_INFORMATION_SIZEHIGH = 32;
+ private static final short OFFSETOF_FILE_INFORMATION_SIZELOW = 36;
+ private static final short OFFSETOF_FILE_INFORMATION_NUMLINKS = 40;
+ private static final short OFFSETOF_FILE_INFORMATION_INDEXHIGH = 44;
+ private static final short OFFSETOF_FILE_INFORMATION_INDEXLOW = 48;
+
+ /*
+ * typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
+ * DWORD dwFileAttributes;
+ * FILETIME ftCreationTime;
+ * FILETIME ftLastAccessTime;
+ * FILETIME ftLastWriteTime;
+ * DWORD nFileSizeHigh;
+ * DWORD nFileSizeLow;
+ * } WIN32_FILE_ATTRIBUTE_DATA;
+ */
+ private static final short SIZEOF_FILE_ATTRIBUTE_DATA = 36;
+ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES = 0;
+ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME = 4;
+ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME = 12;
+ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME = 20;
+ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH = 28;
+ private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW = 32;
+
+ /**
+ * typedef struct _WIN32_FIND_DATA {
+ * DWORD dwFileAttributes;
+ * FILETIME ftCreationTime;
+ * FILETIME ftLastAccessTime;
+ * FILETIME ftLastWriteTime;
+ * DWORD nFileSizeHigh;
+ * DWORD nFileSizeLow;
+ * DWORD dwReserved0;
+ * DWORD dwReserved1;
+ * TCHAR cFileName[MAX_PATH];
+ * TCHAR cAlternateFileName[14];
+ * } WIN32_FIND_DATA;
+ */
+ private static final short SIZEOF_FIND_DATA = 592;
+ private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0;
+ private static final short OFFSETOF_FIND_DATA_CREATETIME = 4;
+ private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12;
+ private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20;
+ private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28;
+ private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
+ private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
+
+ // indicates if accurate metadata is required (interesting on NTFS only)
+ private static final boolean ensureAccurateMetadata;
+ static {
+ String propValue = AccessController.doPrivileged(
+ new GetPropertyAction("sun.nio.fs.ensureAccurateMetadata", "false"));
+ ensureAccurateMetadata = (propValue.length() == 0) ?
+ true : Boolean.valueOf(propValue);
+ }
+
+ // attributes
+ private final int fileAttrs;
+ private final long creationTime;
+ private final long lastAccessTime;
+ private final long lastWriteTime;
+ private final long size;
+ private final int reparseTag;
+
+ // additional attributes when using GetFileInformationByHandle
+ private final int linkCount;
+ private final int volSerialNumber;
+ private final int fileIndexHigh;
+ private final int fileIndexLow;
+
+ /**
+ * Convert 64-bit value representing the number of 100-nanosecond intervals
+ * since January 1, 1601 to java time.
+ */
+ private static long toJavaTime(long time) {
+ time /= 10000L;
+ time -= 11644473600000L;
+ return time;
+ }
+
+ /**
+ * Convert java time to 64-bit value representing the number of 100-nanosecond
+ * intervals since January 1, 1601.
+ */
+ static long toWindowsTime(long time) {
+ time += 11644473600000L;
+ time *= 10000L;
+ return time;
+ }
+
+ /**
+ * Initialize a new instance of this class
+ */
+ private WindowsFileAttributes(int fileAttrs,
+ long creationTime,
+ long lastAccessTime,
+ long lastWriteTime,
+ long size,
+ int reparseTag,
+ int linkCount,
+ int volSerialNumber,
+ int fileIndexHigh,
+ int fileIndexLow)
+ {
+ this.fileAttrs = fileAttrs;
+ this.creationTime = creationTime;
+ this.lastAccessTime = lastAccessTime;
+ this.lastWriteTime = lastWriteTime;
+ this.size = size;
+ this.reparseTag = reparseTag;
+ this.linkCount = linkCount;
+ this.volSerialNumber = volSerialNumber;
+ this.fileIndexHigh = fileIndexHigh;
+ this.fileIndexLow = fileIndexLow;
+ }
+
+ /**
+ * Create a WindowsFileAttributes from a BY_HANDLE_FILE_INFORMATION structure
+ */
+ private static WindowsFileAttributes fromFileInformation(long address, int reparseTag) {
+ int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
+ long creationTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_CREATETIME));
+ long lastAccessTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTACCESSTIME));
+ long lastWriteTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTWRITETIME));
+ long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZEHIGH)) << 32)
+ + (unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZELOW) & 0xFFFFFFFFL);
+ int linkCount = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_NUMLINKS);
+ int volSerialNumber = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_VOLSERIALNUM);
+ int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXHIGH);
+ int fileIndexLow = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXLOW);
+ return new WindowsFileAttributes(fileAttrs,
+ creationTime,
+ lastAccessTime,
+ lastWriteTime,
+ size,
+ reparseTag,
+ linkCount,
+ volSerialNumber,
+ fileIndexHigh,
+ fileIndexLow);
+ }
+
+ /**
+ * Create a WindowsFileAttributes from a WIN32_FILE_ATTRIBUTE_DATA structure
+ */
+ private static WindowsFileAttributes fromFileAttributeData(long address, int reparseTag) {
+ int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
+ long creationTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME));
+ long lastAccessTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME));
+ long lastWriteTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME));
+ long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH)) << 32)
+ + (unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW) & 0xFFFFFFFFL);
+ return new WindowsFileAttributes(fileAttrs,
+ creationTime,
+ lastAccessTime,
+ lastWriteTime,
+ size,
+ reparseTag,
+ 1, // linkCount
+ 0, // volSerialNumber
+ 0, // fileIndexHigh
+ 0); // fileIndexLow
+ }
+
+
+ /**
+ * Allocates a native buffer for a WIN32_FIND_DATA structure
+ */
+ static NativeBuffer getBufferForFindData() {
+ return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA);
+ }
+
+ /**
+ * Create a WindowsFileAttributes from a WIN32_FIND_DATA structure
+ */
+ static WindowsFileAttributes fromFindData(long address) {
+ int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES);
+ long creationTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME));
+ long lastAccessTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME));
+ long lastWriteTime =
+ toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME));
+ long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
+ + (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
+ int reparseTag = ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) ?
+ + unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
+ return new WindowsFileAttributes(fileAttrs,
+ creationTime,
+ lastAccessTime,
+ lastWriteTime,
+ size,
+ reparseTag,
+ 1, // linkCount
+ 0, // volSerialNumber
+ 0, // fileIndexHigh
+ 0); // fileIndexLow
+ }
+
+ /**
+ * Reads the attributes of an open file
+ */
+ static WindowsFileAttributes readAttributes(long handle)
+ throws WindowsException
+ {
+ NativeBuffer buffer = NativeBuffers
+ .getNativeBuffer(SIZEOF_FILE_INFORMATION);
+ try {
+ long address = buffer.address();
+ GetFileInformationByHandle(handle, address);
+
+ // if file is a reparse point then read the tag
+ int reparseTag = 0;
+ int fileAttrs = unsafe
+ .getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
+ if ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
+ NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ DeviceIoControlGetReparsePoint(handle, reparseBuffer.address(), size);
+ reparseTag = (int)unsafe.getLong(reparseBuffer.address());
+ } finally {
+ reparseBuffer.release();
+ }
+ }
+
+ return fromFileInformation(address, reparseTag);
+ } finally {
+ buffer.release();
+ }
+ }
+
+ /**
+ * Returns attributes of given file.
+ */
+ static WindowsFileAttributes get(WindowsPath path, boolean followLinks)
+ throws WindowsException
+ {
+ if (!ensureAccurateMetadata) {
+ NativeBuffer buffer =
+ NativeBuffers.getNativeBuffer(SIZEOF_FILE_ATTRIBUTE_DATA);
+ try {
+ long address = buffer.address();
+ GetFileAttributesEx(path.getPathForWin32Calls(), address);
+ // if reparse point then file may be a sym link; otherwise
+ // just return the attributes
+ int fileAttrs = unsafe
+ .getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
+ if ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+ return fromFileAttributeData(address, 0);
+ } finally {
+ buffer.release();
+ }
+ }
+
+ // file is reparse point so need to open file to get attributes
+ long handle = path.openForReadAttributeAccess(followLinks);
+ try {
+ return readAttributes(handle);
+ } finally {
+ CloseHandle(handle);
+ }
+ }
+
+ /**
+ * Returns true if the attribtues are of the same file - both files must
+ * be open.
+ */
+ static boolean isSameFile(WindowsFileAttributes attrs1,
+ WindowsFileAttributes attrs2)
+ {
+ // volume serial number and file index must be the same
+ return (attrs1.volSerialNumber == attrs2.volSerialNumber) &&
+ (attrs1.fileIndexHigh == attrs2.fileIndexHigh) &&
+ (attrs1.fileIndexLow == attrs2.fileIndexLow);
+ }
+
+ // package-private
+ int attributes() {
+ return fileAttrs;
+ }
+
+ int volSerialNumber() {
+ if (volSerialNumber == 0)
+ throw new AssertionError("Should not get here");
+ return volSerialNumber;
+ }
+
+ int fileIndexHigh() {
+ if (volSerialNumber == 0)
+ throw new AssertionError("Should not get here");
+ return fileIndexHigh;
+ }
+
+ int fileIndexLow() {
+ if (volSerialNumber == 0)
+ throw new AssertionError("Should not get here");
+ return fileIndexLow;
+ }
+
+ @Override
+ public long size() {
+ return size;
+ }
+
+ @Override
+ public long lastModifiedTime() {
+ return (lastWriteTime >= 0L) ? lastWriteTime : 0L;
+ }
+
+ @Override
+ public long lastAccessTime() {
+ return (lastAccessTime >= 0L) ? lastAccessTime : 0L;
+ }
+
+ @Override
+ public long creationTime() {
+ return (creationTime >= 0L) ? creationTime : 0L;
+ }
+
+ @Override
+ public TimeUnit resolution() {
+ return TimeUnit.MILLISECONDS;
+ }
+
+ @Override
+ public int linkCount() {
+ return linkCount;
+ }
+
+ @Override
+ public Object fileKey() {
+ return null;
+ }
+
+ // package private
+ boolean isReparsePoint() {
+ return (fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ }
+
+ boolean isDirectoryLink() {
+ return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ }
+
+ @Override
+ public boolean isSymbolicLink() {
+ return reparseTag == IO_REPARSE_TAG_SYMLINK;
+ }
+
+ @Override
+ public boolean isDirectory() {
+ // ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link
+ if (isSymbolicLink())
+ return false;
+ return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ }
+
+ @Override
+ public boolean isOther() {
+ if (isSymbolicLink())
+ return false;
+ // return true if device or reparse point
+ return ((fileAttrs & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0);
+ }
+
+ @Override
+ public boolean isRegularFile() {
+ return !isSymbolicLink() && !isDirectory() && !isOther();
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return (fileAttrs & FILE_ATTRIBUTE_READONLY) != 0;
+ }
+
+ @Override
+ public boolean isHidden() {
+ return (fileAttrs & FILE_ATTRIBUTE_HIDDEN) != 0;
+ }
+
+ @Override
+ public boolean isArchive() {
+ return (fileAttrs & FILE_ATTRIBUTE_ARCHIVE) != 0;
+ }
+
+ @Override
+ public boolean isSystem() {
+ return (fileAttrs & FILE_ATTRIBUTE_SYSTEM) != 0;
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsFileCopy.java b/src/windows/classes/sun/nio/fs/WindowsFileCopy.java
new file mode 100644
index 000000000..69a41262a
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsFileCopy.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import com.sun.nio.file.ExtendedCopyOption;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Utility methods for copying and moving files.
+ */
+
+class WindowsFileCopy {
+ private WindowsFileCopy() {
+ }
+
+ /**
+ * Copy file from source to target
+ */
+ static void copy(final WindowsPath source,
+ final WindowsPath target,
+ CopyOption... options)
+ throws IOException
+ {
+ // map options
+ boolean replaceExisting = false;
+ boolean copyAttributes = false;
+ boolean followLinks = true;
+ boolean interruptible = false;
+ for (CopyOption option: options) {
+ if (option == StandardCopyOption.REPLACE_EXISTING) {
+ replaceExisting = true;
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ followLinks = false;
+ continue;
+ }
+ if (option == StandardCopyOption.COPY_ATTRIBUTES) {
+ copyAttributes = true;
+ continue;
+ }
+ if (option == ExtendedCopyOption.INTERRUPTIBLE) {
+ interruptible = true;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException("Unsupported copy option");
+ }
+
+ // check permissions. If the source file is a symbolic link then
+ // later we must also check LinkPermission
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ source.checkRead();
+ target.checkWrite();
+ }
+
+ // get attributes of source file
+ // attempt to get attributes of target file
+ // if both files are the same there is nothing to do
+ // if target exists and !replace then throw exception
+
+ WindowsFileAttributes sourceAttrs = null;
+ WindowsFileAttributes targetAttrs = null;
+
+ long sourceHandle = 0L;
+ try {
+ sourceHandle = source.openForReadAttributeAccess(followLinks);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source);
+ }
+ try {
+ // source attributes
+ try {
+ sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source);
+ }
+
+ // open target (don't follow links)
+ long targetHandle = 0L;
+ try {
+ targetHandle = target.openForReadAttributeAccess(false);
+ try {
+ targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
+
+ // if both files are the same then nothing to do
+ if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
+ return;
+ }
+
+ // can't replace file
+ if (!replaceExisting) {
+ throw new FileAlreadyExistsException(
+ target.getPathForExceptionMessage());
+ }
+
+ } finally {
+ CloseHandle(targetHandle);
+ }
+ } catch (WindowsException x) {
+ // ignore
+ }
+
+ } finally {
+ CloseHandle(sourceHandle);
+ }
+
+ // if source file is a symbolic link then we must check for LinkPermission
+ if (sm != null && sourceAttrs.isSymbolicLink()) {
+ sm.checkPermission(new LinkPermission("symbolic"));
+ }
+
+ final String sourcePath = asWin32Path(source);
+ final String targetPath = asWin32Path(target);
+
+ // if target exists then delete it.
+ if (targetAttrs != null) {
+ try {
+ if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
+ RemoveDirectory(targetPath);
+ } else {
+ DeleteFile(targetPath);
+ }
+ } catch (WindowsException x) {
+ if (targetAttrs.isDirectory()) {
+ // ERROR_ALREADY_EXISTS is returned when attempting to delete
+ // non-empty directory on SAMBA servers.
+ if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
+ x.lastError() == ERROR_ALREADY_EXISTS)
+ {
+ throw new FileAlreadyExistsException(
+ target.getPathForExceptionMessage());
+ }
+ }
+ x.rethrowAsIOException(target);
+ }
+ }
+
+ // Use CopyFileEx if the file is not a directory or junction
+ if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
+ final int flags =
+ (source.getFileSystem().supportsLinks() && !followLinks) ?
+ COPY_FILE_COPY_SYMLINK : 0;
+
+ if (interruptible) {
+ // interruptible copy
+ Cancellable copyTask = new Cancellable() {
+ @Override
+ public int cancelValue() {
+ return 1; // TRUE
+ }
+ @Override
+ public void implRun() throws IOException {
+ try {
+ CopyFileEx(sourcePath, targetPath, flags,
+ addressToPollForCancel());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source, target);
+ }
+ }
+ };
+ try {
+ Cancellable.runInterruptibly(copyTask);
+ } catch (ExecutionException e) {
+ Throwable t = e.getCause();
+ if (t instanceof IOException)
+ throw (IOException)t;
+ throw new IOException(t);
+ }
+ } else {
+ // non-interruptible copy
+ try {
+ CopyFileEx(sourcePath, targetPath, flags, 0L);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source, target);
+ }
+ }
+ if (copyAttributes) {
+ // CopyFileEx does not copy security attributes
+ try {
+ copySecurityAttributes(source, target, followLinks);
+ } catch (IOException x) {
+ // ignore
+ }
+ }
+ return;
+ }
+
+ // copy directory or directory junction
+ try {
+ if (sourceAttrs.isDirectory()) {
+ CreateDirectory(targetPath, 0L);
+ } else {
+ String linkTarget = WindowsLinkSupport.readLink(source);
+ int flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
+ CreateSymbolicLink(targetPath,
+ addPrefixIfNeeded(linkTarget),
+ flags);
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(target);
+ }
+ if (copyAttributes) {
+ // copy DOS/timestamps attributes
+ WindowsFileAttributeViews.Dos view =
+ WindowsFileAttributeViews.createDosView(target, false);
+ try {
+ view.setAttributes(sourceAttrs);
+ } catch (IOException x) {
+ if (sourceAttrs.isDirectory()) {
+ try {
+ RemoveDirectory(targetPath);
+ } catch (WindowsException ignore) { }
+ }
+ }
+
+ // copy security attributes. If this fail it doesn't cause the move
+ // to fail.
+ try {
+ copySecurityAttributes(source, target, followLinks);
+ } catch (IOException ignore) { }
+ }
+ }
+
+ /**
+ * Move file from source to target
+ */
+ static void move(WindowsPath source, WindowsPath target, CopyOption... options)
+ throws IOException
+ {
+ // map options
+ boolean atomicMove = false;
+ boolean replaceExisting = false;
+ for (CopyOption option: options) {
+ if (option == StandardCopyOption.ATOMIC_MOVE) {
+ atomicMove = true;
+ continue;
+ }
+ if (option == StandardCopyOption.REPLACE_EXISTING) {
+ replaceExisting = true;
+ continue;
+ }
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ // ignore
+ continue;
+ }
+ if (option == null) throw new NullPointerException();
+ throw new UnsupportedOperationException("Unsupported copy option");
+ }
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ source.checkWrite();
+ target.checkWrite();
+ }
+
+ final String sourcePath = asWin32Path(source);
+ final String targetPath = asWin32Path(target);
+
+ // atomic case
+ if (atomicMove) {
+ try {
+ MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);
+ } catch (WindowsException x) {
+ if (x.lastError() == ERROR_NOT_SAME_DEVICE) {
+ throw new AtomicMoveNotSupportedException(
+ source.getPathForExceptionMessage(),
+ target.getPathForExceptionMessage(),
+ x.errorString());
+ }
+ x.rethrowAsIOException(source, target);
+ }
+ return;
+ }
+
+ // get attributes of source file
+ // attempt to get attributes of target file
+ // if both files are the same there is nothing to do
+ // if target exists and !replace then throw exception
+
+ WindowsFileAttributes sourceAttrs = null;
+ WindowsFileAttributes targetAttrs = null;
+
+ long sourceHandle = 0L;
+ try {
+ sourceHandle = source.openForReadAttributeAccess(false);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source);
+ }
+ try {
+ // source attributes
+ try {
+ sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source);
+ }
+
+ // open target (don't follow links)
+ long targetHandle = 0L;
+ try {
+ targetHandle = target.openForReadAttributeAccess(false);
+ try {
+ targetAttrs = WindowsFileAttributes.readAttributes(targetHandle);
+
+ // if both files are the same then nothing to do
+ if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) {
+ return;
+ }
+
+ // can't replace file
+ if (!replaceExisting) {
+ throw new FileAlreadyExistsException(
+ target.getPathForExceptionMessage());
+ }
+
+ } finally {
+ CloseHandle(targetHandle);
+ }
+ } catch (WindowsException x) {
+ // ignore
+ }
+
+ } finally {
+ CloseHandle(sourceHandle);
+ }
+
+ // if target exists then delete it.
+ if (targetAttrs != null) {
+ try {
+ if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) {
+ RemoveDirectory(targetPath);
+ } else {
+ DeleteFile(targetPath);
+ }
+ } catch (WindowsException x) {
+ if (targetAttrs.isDirectory()) {
+ // ERROR_ALREADY_EXISTS is returned when attempting to delete
+ // non-empty directory on SAMBA servers.
+ if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
+ x.lastError() == ERROR_ALREADY_EXISTS)
+ {
+ throw new FileAlreadyExistsException(
+ target.getPathForExceptionMessage());
+ }
+ }
+ x.rethrowAsIOException(target);
+ }
+ }
+
+ // first try MoveFileEx (no options). If target is on same volume then
+ // all attributes (including security attributes) are preserved.
+ try {
+ MoveFileEx(sourcePath, targetPath, 0);
+ return;
+ } catch (WindowsException x) {
+ if (x.lastError() != ERROR_NOT_SAME_DEVICE)
+ x.rethrowAsIOException(source, target);
+ }
+
+ // target is on different volume so use MoveFileEx with copy option
+ if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
+ try {
+ MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(source, target);
+ }
+ // MoveFileEx does not copy security attributes when moving
+ // across volumes.
+ try {
+ copySecurityAttributes(source, target, false);
+ } catch (IOException x) {
+ // ignore
+ }
+ return;
+ }
+
+ // moving directory or directory-link to another file system
+ assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink();
+
+ // create new directory or directory junction
+ try {
+ if (sourceAttrs.isDirectory()) {
+ CreateDirectory(targetPath, 0L);
+ } else {
+ String linkTarget = WindowsLinkSupport.readLink(source);
+ CreateSymbolicLink(targetPath,
+ addPrefixIfNeeded(linkTarget),
+ SYMBOLIC_LINK_FLAG_DIRECTORY);
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(target);
+ }
+
+ // copy timestamps/DOS attributes
+ WindowsFileAttributeViews.Dos view =
+ WindowsFileAttributeViews.createDosView(target, false);
+ try {
+ view.setAttributes(sourceAttrs);
+ } catch (IOException x) {
+ // rollback
+ try {
+ RemoveDirectory(targetPath);
+ } catch (WindowsException ignore) { }
+ throw x;
+ }
+
+ // copy security attributes. If this fails it doesn't cause the move
+ // to fail.
+ try {
+ copySecurityAttributes(source, target, false);
+ } catch (IOException ignore) { }
+
+ // delete source
+ try {
+ RemoveDirectory(sourcePath);
+ } catch (WindowsException x) {
+ // rollback
+ try {
+ RemoveDirectory(targetPath);
+ } catch (WindowsException ignore) { }
+ // ERROR_ALREADY_EXISTS is returned when attempting to delete
+ // non-empty directory on SAMBA servers.
+ if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
+ x.lastError() == ERROR_ALREADY_EXISTS)
+ {
+ throw new DirectoryNotEmptyException(
+ target.getPathForExceptionMessage());
+ }
+ x.rethrowAsIOException(source);
+ }
+ }
+
+
+ private static String asWin32Path(WindowsPath path) throws IOException {
+ try {
+ return path.getPathForWin32Calls();
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(path);
+ return null;
+ }
+ }
+
+ /**
+ * Copy DACL/owner/group from source to target
+ */
+ private static void copySecurityAttributes(WindowsPath source,
+ WindowsPath target,
+ boolean followLinks)
+ throws IOException
+ {
+ String path = WindowsLinkSupport.getFinalPath(source, followLinks);
+
+ // may need SeRestorePrivilege to set file owner
+ WindowsSecurity.Privilege priv =
+ WindowsSecurity.enablePrivilege("SeRestorePrivilege");
+ try {
+ int request = (DACL_SECURITY_INFORMATION |
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
+ NativeBuffer buffer =
+ WindowsAclFileAttributeView.getFileSecurity(path, request);
+ try {
+ try {
+ SetFileSecurity(target.getPathForWin32Calls(), request,
+ buffer.address());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(target);
+ }
+ } finally {
+ buffer.release();
+ }
+ } finally {
+ priv.drop();
+ }
+ }
+
+ /**
+ * Add long path prefix to path if required
+ */
+ private static String addPrefixIfNeeded(String path) {
+ if (path.length() > 248) {
+ if (path.startsWith("\\\\")) {
+ path = "\\\\?\\UNC" + path.substring(1, path.length());
+ } else {
+ path = "\\\\?\\" + path;
+ }
+ }
+ return path;
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsFileStore.java b/src/windows/classes/sun/nio/fs/WindowsFileStore.java
new file mode 100644
index 000000000..5d3a0af25
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsFileStore.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+import static sun.nio.fs.WindowsConstants.*;
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+
+/**
+ * Windows implementation of FileStore.
+ */
+
+class WindowsFileStore
+ extends FileStore
+{
+ private final String root;
+ private final VolumeInformation volInfo;
+ private final int volType;
+ private final String displayName; // returned by toString
+
+ private WindowsFileStore(String root) throws WindowsException {
+ assert root.charAt(root.length()-1) == '\\';
+ this.root = root;
+ this.volInfo = GetVolumeInformation(root);
+ this.volType = GetDriveType(root);
+
+ // file store "display name" is the volume name if available
+ String vol = volInfo.volumeName();
+ if (vol.length() > 0) {
+ this.displayName = vol;
+ } else {
+ // TBD - should we map all types? Does this need to be localized?
+ this.displayName = (volType == DRIVE_REMOVABLE) ? "Removable Disk" : "";
+ }
+ }
+
+ static WindowsFileStore create(String root, boolean ignoreNotReady)
+ throws IOException
+ {
+ try {
+ return new WindowsFileStore(root);
+ } catch (WindowsException x) {
+ if (ignoreNotReady && x.lastError() == ERROR_NOT_READY)
+ return null;
+ x.rethrowAsIOException(root);
+ return null; // keep compiler happy
+ }
+ }
+
+ static WindowsFileStore create(WindowsPath file) throws IOException {
+ try {
+ // if the file is a link then GetVolumePathName returns the
+ // volume that the link is on so we need to call it with the
+ // final target
+ String target;
+ if (file.getFileSystem().supportsLinks()) {
+ target = WindowsLinkSupport.getFinalPath(file, true);
+ } else {
+ // file must exist
+ WindowsFileAttributes.get(file, true);
+ target = file.getPathForWin32Calls();
+ }
+ String root = GetVolumePathName(target);
+ return new WindowsFileStore(root);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ return null; // keep compiler happy
+ }
+ }
+
+ VolumeInformation volumeInformation() {
+ return volInfo;
+ }
+
+ int volumeType() {
+ return volType;
+ }
+
+ @Override
+ public String name() {
+ return volInfo.volumeName(); // "SYSTEM", "DVD-RW", ...
+ }
+
+ @Override
+ public String type() {
+ return volInfo.fileSystemName(); // "FAT", "NTFS", ...
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return ((volInfo.flags() & FILE_READ_ONLY_VOLUME) != 0);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> view) {
+ if (view == FileStoreSpaceAttributeView.class)
+ return (V) new WindowsFileStoreAttributeView(this);
+ return (V) null;
+ }
+
+ @Override
+ public FileStoreAttributeView getFileStoreAttributeView(String name) {
+ if (name.equals("space"))
+ return new WindowsFileStoreAttributeView(this);
+ if (name.equals("volume"))
+ return new VolumeFileStoreAttributeView(this);
+ return null;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
+ if (type == BasicFileAttributeView.class)
+ return true;
+ if (type == AclFileAttributeView.class || type == FileOwnerAttributeView.class)
+ return ((volInfo.flags() & FILE_PERSISTENT_ACLS) != 0);
+ if (type == UserDefinedFileAttributeView.class)
+ return ((volInfo.flags() & FILE_NAMED_STREAMS) != 0);
+ return false;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name) {
+ if (name.equals("basic") || name.equals("dos"))
+ return true;
+ if (name.equals("acl"))
+ return supportsFileAttributeView(AclFileAttributeView.class);
+ if (name.equals("owner"))
+ return supportsFileAttributeView(FileOwnerAttributeView.class);
+ if (name.equals("xattr"))
+ return supportsFileAttributeView(UserDefinedFileAttributeView.class);
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object ob) {
+ if (ob == this)
+ return true;
+ if (!(ob instanceof WindowsFileStore))
+ return false;
+ WindowsFileStore other = (WindowsFileStore)ob;
+ return this.volInfo.volumeSerialNumber() == other.volInfo.volumeSerialNumber();
+ }
+
+ @Override
+ public int hashCode() {
+ // reveals VSN without permission check - okay?
+ return volInfo.volumeSerialNumber();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(displayName);
+ if (sb.length() > 0)
+ sb.append(" ");
+ sb.append("(");
+ // drop trailing slash
+ sb.append(root.subSequence(0, root.length()-1));
+ sb.append(")");
+ return sb.toString();
+ }
+
+ static class WindowsFileStoreAttributeView
+ extends AbstractFileStoreSpaceAttributeView
+ {
+ private final WindowsFileStore fs;
+
+ WindowsFileStoreAttributeView(WindowsFileStore fs) {
+ this.fs = fs;
+ }
+
+ @Override
+ public FileStoreSpaceAttributes readAttributes()
+ throws IOException
+ {
+ // read the free space info
+ DiskFreeSpace info = null;
+ try {
+ info = GetDiskFreeSpaceEx(fs.root);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(fs.root);
+ }
+
+ final DiskFreeSpace result = info;
+ return new FileStoreSpaceAttributes() {
+ @Override
+ public long totalSpace() {
+ return result.totalNumberOfBytes();
+ }
+ @Override
+ public long usableSpace() {
+ return result.freeBytesAvailable();
+ }
+ @Override
+ public long unallocatedSpace() {
+ return result.totalNumberOfFreeBytes();
+ }
+ };
+ }
+ }
+
+ /**
+ * Windows-specific attribute view to allow access to volume information.
+ */
+ static class VolumeFileStoreAttributeView
+ implements FileStoreAttributeView
+ {
+ private static final String VSN_NAME = "vsn";
+ private static final String COMPRESSED_NAME = "compressed";
+ private static final String REMOVABLE_NAME = "removable";
+ private static final String CDROM_NAME = "cdrom";
+
+ private final WindowsFileStore fs;
+
+ VolumeFileStoreAttributeView(WindowsFileStore fs) {
+ this.fs = fs;
+ }
+
+ @Override
+ public String name() {
+ return "volume";
+ }
+
+ private int vsn() {
+ return fs.volumeInformation().volumeSerialNumber();
+ }
+
+ private boolean isCompressed() {
+ return (fs.volumeInformation().flags() &
+ FILE_VOLUME_IS_COMPRESSED) > 0;
+ }
+
+ private boolean isRemovable() {
+ return fs.volumeType() == DRIVE_REMOVABLE;
+ }
+
+ private boolean isCdrom() {
+ return fs.volumeType() == DRIVE_CDROM;
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals(VSN_NAME))
+ return vsn();
+ if (attribute.equals(COMPRESSED_NAME))
+ return isCompressed();
+ if (attribute.equals(REMOVABLE_NAME))
+ return isRemovable();
+ if (attribute.equals(CDROM_NAME))
+ return isCdrom();
+ return null;
+ }
+
+ @Override
+ public void setAttribute(String attribute, Object value)
+ throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String first, String... rest)
+ throws IOException
+ {
+ boolean all = false;
+ boolean vsn = false;
+ boolean compressed = false;
+ boolean removable = false;
+ boolean cdrom = false;
+
+ if (first.equals(VSN_NAME)) vsn = true;
+ else if (first.equals(COMPRESSED_NAME)) compressed = true;
+ else if (first.equals(REMOVABLE_NAME)) removable = true;
+ else if (first.equals(CDROM_NAME)) cdrom = true;
+ else if (first.equals("*")) all = true;
+
+ if (!all) {
+ for (String attribute: rest) {
+ if (attribute.equals("*")) {
+ all = true;
+ break;
+ }
+ if (attribute.equals(VSN_NAME)) {
+ vsn = true;
+ continue;
+ }
+ if (attribute.equals(COMPRESSED_NAME)) {
+ compressed = true;
+ continue;
+ }
+ if (attribute.equals(REMOVABLE_NAME)) {
+ removable = true;
+ continue;
+ }
+ }
+ }
+
+ Map<String,Object> result = new HashMap<String,Object>();
+ if (all || vsn)
+ result.put(VSN_NAME, vsn());
+ if (all || compressed)
+ result.put(COMPRESSED_NAME, isCompressed());
+ if (all || removable)
+ result.put(REMOVABLE_NAME, isRemovable());
+ if (all || cdrom)
+ result.put(CDROM_NAME, isCdrom());
+ return result;
+ }
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsFileSystem.java b/src/windows/classes/sun/nio/fs/WindowsFileSystem.java
new file mode 100644
index 000000000..e80c829f3
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsFileSystem.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.*;
+import java.util.*;
+import java.util.regex.Pattern;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.security.action.GetPropertyAction;
+
+class WindowsFileSystem
+ extends FileSystem
+{
+ private final WindowsFileSystemProvider provider;
+
+ // default directory (is absolute), and default root
+ private final String defaultDirectory;
+ private final String defaultRoot;
+
+ private final boolean supportsLinks;
+ private final boolean supportsStreamEnumeration;
+
+ // package-private
+ WindowsFileSystem(WindowsFileSystemProvider provider,
+ String dir)
+ {
+ this.provider = provider;
+
+ // parse default directory and check it is absolute
+ WindowsPathParser.Result result = WindowsPathParser.parse(dir);
+
+ if (result.type() != WindowsPathType.ABSOLUTE)
+ throw new AssertionError("Default directory must be absolute/non-UNC");
+ this.defaultDirectory = result.path();
+ this.defaultRoot = result.root();
+
+ PrivilegedAction<String> pa = new GetPropertyAction("os.version");
+ String osversion = AccessController.doPrivileged(pa);
+ String[] vers = osversion.split("\\.", 0);
+ int major = Integer.parseInt(vers[0]);
+ int minor = Integer.parseInt(vers[1]);
+
+ // symbolic links available on Vista and newer
+ supportsLinks = (major >= 6);
+
+ // enumeration of data streams available on Windows Server 2003 and newer
+ supportsStreamEnumeration = (major >= 6) || (major == 5 && minor >= 2);
+ }
+
+ // package-private
+ String defaultDirectory() {
+ return defaultDirectory;
+ }
+
+ String defaultRoot() {
+ return defaultRoot;
+ }
+
+ boolean supportsLinks() {
+ return supportsLinks;
+ }
+
+ boolean supportsStreamEnumeration() {
+ return supportsStreamEnumeration;
+ }
+
+ @Override
+ public FileSystemProvider provider() {
+ return provider;
+ }
+
+ @Override
+ public String getSeparator() {
+ return "\\";
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ public void close() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterable<Path> getRootDirectories() {
+ int drives = 0;
+ try {
+ drives = WindowsNativeDispatcher.GetLogicalDrives();
+ } catch (WindowsException x) {
+ // shouldn't happen
+ throw new AssertionError(x.getMessage());
+ }
+
+ // iterate over roots, ignoring those that the security manager denies
+ ArrayList<Path> result = new ArrayList<Path>();
+ SecurityManager sm = System.getSecurityManager();
+ for (int i = 0; i <= 25; i++) { // 0->A, 1->B, 2->C...
+ if ((drives & (1 << i)) != 0) {
+ StringBuilder sb = new StringBuilder(3);
+ sb.append((char)('A' + i));
+ sb.append(":\\");
+ String root = sb.toString();
+ if (sm != null) {
+ try {
+ sm.checkRead(root);
+ } catch (SecurityException x) {
+ continue;
+ }
+ }
+ result.add(WindowsPath.createFromNormalizedPath(this, root));
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ /**
+ * Iterator returned by getFileStores method.
+ */
+ private class FileStoreIterator implements Iterator<FileStore> {
+ private final Iterator<Path> roots;
+ private FileStore next;
+
+ FileStoreIterator() {
+ this.roots = getRootDirectories().iterator();
+ }
+
+ private FileStore readNext() {
+ assert Thread.holdsLock(this);
+ for (;;) {
+ if (!roots.hasNext())
+ return null;
+ WindowsPath root = (WindowsPath)roots.next();
+ // ignore if security manager denies access
+ try {
+ root.checkRead();
+ } catch (SecurityException x) {
+ continue;
+ }
+ try {
+ FileStore fs = WindowsFileStore.create(root.toString(), true);
+ if (fs != null)
+ return fs;
+ } catch (IOException ioe) {
+ // skip it
+ }
+ }
+ }
+
+ @Override
+ public synchronized boolean hasNext() {
+ if (next != null)
+ return true;
+ next = readNext();
+ return next != null;
+ }
+
+ @Override
+ public synchronized FileStore next() {
+ if (next == null)
+ next = readNext();
+ if (next == null) {
+ throw new NoSuchElementException();
+ } else {
+ FileStore result = next;
+ next = null;
+ return result;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public Iterable<FileStore> getFileStores() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
+ } catch (SecurityException se) {
+ return Collections.emptyList();
+ }
+ }
+ return new Iterable<FileStore>() {
+ public Iterator<FileStore> iterator() {
+ return new FileStoreIterator();
+ }
+ };
+ }
+
+ // supported views
+ private static final Set<String> supportedFileAttributeViews = Collections
+ .unmodifiableSet(new HashSet<String>(Arrays.asList("basic", "dos", "acl", "owner", "xattr")));
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ return supportedFileAttributeViews;
+ }
+
+ @Override
+ public Path getPath(String path) {
+ return WindowsPath.parse(this, path);
+ }
+
+ @Override
+ public UserPrincipalLookupService getUserPrincipalLookupService() {
+ return theLookupService;
+ }
+
+ private static final UserPrincipalLookupService theLookupService =
+ new UserPrincipalLookupService() {
+ @Override
+ public UserPrincipal lookupPrincipalByName(String name)
+ throws IOException
+ {
+ return WindowsUserPrincipals.lookup(name);
+ }
+ @Override
+ public GroupPrincipal lookupPrincipalByGroupName(String group)
+ throws IOException
+ {
+ UserPrincipal user = WindowsUserPrincipals.lookup(group);
+ if (!(user instanceof GroupPrincipal))
+ throw new UserPrincipalNotFoundException(group);
+ return (GroupPrincipal)user;
+ }
+ };
+
+ @Override
+ public PathMatcher getPathMatcher(String syntaxAndInput) {
+ int pos = syntaxAndInput.indexOf(':');
+ if (pos <= 0 || pos == syntaxAndInput.length())
+ throw new IllegalArgumentException();
+ String syntax = syntaxAndInput.substring(0, pos);
+ String input = syntaxAndInput.substring(pos+1);
+
+ String expr;
+ if (syntax.equals(GLOB_SYNTAX)) {
+ expr = Globs.toWindowsRegexPattern(input);
+ } else {
+ if (syntax.equals(REGEX_SYNTAX)) {
+ expr = input;
+ } else {
+ throw new UnsupportedOperationException("Syntax '" + syntax +
+ "' not recognized");
+ }
+ }
+
+ // match in uppercase
+ StringBuilder sb = new StringBuilder(expr.length());
+ for (int i=0; i<expr.length(); i++) {
+ sb.append(Character.toUpperCase(expr.charAt(i)));
+ }
+ expr = sb.toString();
+
+ // return matcher
+ final Pattern pattern = Pattern.compile(expr);
+ return new PathMatcher() {
+ @Override
+ public boolean matches(Path path) {
+ // match in uppercase
+ String s = path.toString();
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int i=0; i<s.length(); i++) {
+ sb.append( Character.toUpperCase(s.charAt(i)) );
+ }
+ return pattern.matcher(sb).matches();
+ }
+ };
+ }
+ private static final String GLOB_SYNTAX = "glob";
+ private static final String REGEX_SYNTAX = "regex";
+
+ @Override
+ public WatchService newWatchService()
+ throws IOException
+ {
+ return new WindowsWatchService(this);
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java b/src/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java
new file mode 100644
index 000000000..bc28e95d0
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.spi.*;
+import java.nio.file.attribute.*;
+import java.nio.channels.*;
+import java.net.URI;
+import java.util.concurrent.ExecutorService;
+import java.io.IOException;
+import java.util.*;
+
+import sun.nio.ch.ThreadPool;
+
+public class WindowsFileSystemProvider
+ extends FileSystemProvider
+{
+ private static final String USER_DIR = "user.dir";
+ private final WindowsFileSystem theFileSystem;
+
+ public WindowsFileSystemProvider() {
+ theFileSystem = new WindowsFileSystem(this, System.getProperty(USER_DIR));
+ }
+
+ @Override
+ public String getScheme() {
+ return "file";
+ }
+
+ private void checkUri(URI uri) {
+ if (!uri.getScheme().equalsIgnoreCase(getScheme()))
+ throw new IllegalArgumentException("URI does not match this provider");
+ if (uri.getAuthority() != null)
+ throw new IllegalArgumentException("Authority component present");
+ if (uri.getPath() == null)
+ throw new IllegalArgumentException("Path component is undefined");
+ if (!uri.getPath().equals("/"))
+ throw new IllegalArgumentException("Path component should be '/'");
+ if (uri.getQuery() != null)
+ throw new IllegalArgumentException("Query component present");
+ if (uri.getFragment() != null)
+ throw new IllegalArgumentException("Fragment component present");
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String,?> env)
+ throws IOException
+ {
+ checkUri(uri);
+ throw new FileSystemAlreadyExistsException();
+ }
+
+ @Override
+ public final FileSystem getFileSystem(URI uri) {
+ checkUri(uri);
+ return theFileSystem;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ return WindowsUriSupport.fromUri(theFileSystem, uri);
+ }
+
+ @Override
+ public FileChannel newFileChannel(Path path,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ if (path == null)
+ throw new NullPointerException();
+ if (!(path instanceof WindowsPath))
+ throw new ProviderMismatchException();
+ WindowsPath file = (WindowsPath)path;
+
+ WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
+ try {
+ return WindowsChannelFactory
+ .newFileChannel(file.getPathForWin32Calls(),
+ file.getPathForPermissionCheck(),
+ options,
+ sd.address());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ return null;
+ } finally {
+ if (sd != null)
+ sd.release();
+ }
+ }
+
+ @Override
+ public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
+ Set<? extends OpenOption> options,
+ ExecutorService executor,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ if (path == null)
+ throw new NullPointerException();
+ if (!(path instanceof WindowsPath))
+ throw new ProviderMismatchException();
+ WindowsPath file = (WindowsPath)path;
+ ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
+ WindowsSecurityDescriptor sd =
+ WindowsSecurityDescriptor.fromAttribute(attrs);
+ try {
+ return WindowsChannelFactory
+ .newAsynchronousFileChannel(file.getPathForWin32Calls(),
+ file.getPathForPermissionCheck(),
+ options,
+ sd.address(),
+ pool);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ return null;
+ } finally {
+ if (sd != null)
+ sd.release();
+ }
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java b/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java
new file mode 100644
index 000000000..516275dfe
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.io.IOError;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Utility methods for symbolic link support on Windows Vista and newer.
+ */
+
+class WindowsLinkSupport {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private WindowsLinkSupport() {
+ }
+
+ /**
+ * Returns the target of a symbolic link
+ */
+ static String readLink(WindowsPath path) throws IOException {
+ long handle = 0L;
+ try {
+ handle = path.openForReadAttributeAccess(false); // don't follow links
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(path);
+ }
+ try {
+ return readLinkImpl(handle);
+ } finally {
+ CloseHandle(handle);
+ }
+ }
+
+ /**
+ * Returns the final path of a given path as a String. This should be used
+ * prior to calling Win32 system calls that do not follow links.
+ */
+ static String getFinalPath(WindowsPath input, boolean followLinks)
+ throws IOException
+ {
+ WindowsFileSystem fs = input.getFileSystem();
+
+ try {
+ // if not following links then don't need final path
+ if (!followLinks || !fs.supportsLinks())
+ return input.getPathForWin32Calls();
+
+ // if file is a sym link then don't need final path
+ if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {
+ return input.getPathForWin32Calls();
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(input);
+ }
+
+ // The file is a symbolic link so we open it and try to get the
+ // normalized path. This should succeed on NTFS but may fail if there
+ // is a link to a non-NFTS file system.
+ long h = 0;
+ try {
+ h = input.openForReadAttributeAccess(true);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(input);
+ }
+ try {
+ return stripPrefix(GetFinalPathNameByHandle(h));
+ } catch (WindowsException x) {
+ // ERROR_INVALID_LEVEL is the error returned when not supported by
+ // the file system
+ if (x.lastError() != ERROR_INVALID_LEVEL)
+ x.rethrowAsIOException(input);
+ } finally {
+ CloseHandle(h);
+ }
+
+ // Fallback: read target of link, resolve against parent, and repeat
+ // until file is not a link.
+ WindowsPath target = input;
+ int linkCount = 0;
+ do {
+ try {
+ WindowsFileAttributes attrs =
+ WindowsFileAttributes.get(target, false);
+ // non a link so we are done
+ if (!attrs.isSymbolicLink()) {
+ return target.getPathForWin32Calls();
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(target);
+ }
+ WindowsPath link = WindowsPath
+ .createFromNormalizedPath(fs, readLink(target));
+ WindowsPath parent = target.getParent();
+ if (parent == null) {
+ // no parent so use parent of absolute path
+ final WindowsPath t = target;
+ target = AccessController
+ .doPrivileged(new PrivilegedAction<WindowsPath>() {
+ @Override
+ public WindowsPath run() {
+ return t.toAbsolutePath();
+ }});
+ parent = target.getParent();
+ }
+ target = parent.resolve(link);
+
+ } while (++linkCount < 32);
+
+ throw new FileSystemException(input.getPathForExceptionMessage(), null,
+ "Too many links");
+ }
+
+ /**
+ * Returns the actual path of a file, optionally resolving all symbolic
+ * links.
+ */
+ static String getRealPath(WindowsPath input, boolean resolveLinks)
+ throws IOException
+ {
+ WindowsFileSystem fs = input.getFileSystem();
+ if (!fs.supportsLinks())
+ resolveLinks = false;
+
+ // On Vista use GetFinalPathNameByHandle. This should succeed on NTFS
+ // but may fail if there is a link to a non-NFTS file system.
+ if (resolveLinks) {
+ long h = 0;
+ try {
+ h = input.openForReadAttributeAccess(true);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(input);
+ }
+ try {
+ return stripPrefix(GetFinalPathNameByHandle(h));
+ } catch (WindowsException x) {
+ if (x.lastError() != ERROR_INVALID_LEVEL)
+ x.rethrowAsIOException(input);
+ } finally {
+ CloseHandle(h);
+ }
+ }
+
+ // Not resolving links or we are on Windows Vista (or newer) with a
+ // link to non-NFTS file system.
+
+ // Start with absolute path
+ String path = null;
+ try {
+ path = input.toAbsolutePath().toString();
+ } catch (IOError x) {
+ throw (IOException)(x.getCause());
+ }
+
+ // Collapse "." and ".."
+ try {
+ path = GetFullPathName(path);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(input);
+ }
+
+ // eliminate all symbolic links
+ if (resolveLinks) {
+ path = resolveAllLinks(WindowsPath.createFromNormalizedPath(fs, path));
+ }
+
+ // string builder to build up components of path
+ StringBuilder sb = new StringBuilder(path.length());
+
+ // Copy root component
+ int start;
+ char c0 = path.charAt(0);
+ char c1 = path.charAt(1);
+ if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&
+ c1 == ':' && path.charAt(2) == '\\') {
+ // Driver specifier
+ sb.append(Character.toUpperCase(c0));
+ sb.append(":\\");
+ start = 3;
+ } else if (c0 == '\\' && c1 == '\\') {
+ // UNC pathname, begins with "\\\\host\\share"
+ int last = path.length() - 1;
+ int pos = path.indexOf('\\', 2);
+ // skip both server and share names
+ if (pos == -1 || (pos == last)) {
+ // The UNC does not have a share name (collapsed by GetFullPathName)
+ throw new FileSystemException(input.getPathForExceptionMessage(),
+ null, "UNC has invalid share");
+ }
+ pos = path.indexOf('\\', pos+1);
+ if (pos < 0) {
+ pos = last;
+ sb.append(path).append("\\");
+ } else {
+ sb.append(path, 0, pos+1);
+ }
+ start = pos + 1;
+ } else {
+ throw new AssertionError("path type not recognized");
+ }
+
+ // check root directory exists
+ try {
+ FirstFile fileData = FindFirstFile(sb.toString() + "*");
+ FindClose(fileData.handle());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(path);
+ }
+
+ // iterate through each component to get its actual name in the
+ // directory
+ int curr = start;
+ while (curr < path.length()) {
+ int next = path.indexOf('\\', curr);
+ int end = (next == -1) ? path.length() : next;
+ String search = sb.toString() + path.substring(curr, end);
+ try {
+ FirstFile fileData = FindFirstFile(addLongPathPrefixIfNeeded(search));
+ try {
+ sb.append(fileData.name());
+ if (next != -1) {
+ sb.append('\\');
+ }
+ } finally {
+ FindClose(fileData.handle());
+ }
+ } catch (WindowsException e) {
+ e.rethrowAsIOException(path);
+ }
+ curr = end + 1;
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns target of a symbolic link given the handle of an open file
+ * (that should be a link).
+ */
+ private static String readLinkImpl(long handle) throws IOException {
+ int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ try {
+ DeviceIoControlGetReparsePoint(handle, buffer.address(), size);
+ } catch (WindowsException x) {
+ // FIXME: exception doesn't have file name
+ if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)
+ throw new NotLinkException(null, null, x.errorString());
+ x.rethrowAsIOException((String)null);
+ }
+
+ /*
+ * typedef struct _REPARSE_DATA_BUFFER {
+ * ULONG ReparseTag;
+ * USHORT ReparseDataLength;
+ * USHORT Reserved;
+ * union {
+ * struct {
+ * USHORT SubstituteNameOffset;
+ * USHORT SubstituteNameLength;
+ * USHORT PrintNameOffset;
+ * USHORT PrintNameLength;
+ * WCHAR PathBuffer[1];
+ * } SymbolicLinkReparseBuffer;
+ * struct {
+ * USHORT SubstituteNameOffset;
+ * USHORT SubstituteNameLength;
+ * USHORT PrintNameOffset;
+ * USHORT PrintNameLength;
+ * WCHAR PathBuffer[1];
+ * } MountPointReparseBuffer;
+ * struct {
+ * UCHAR DataBuffer[1];
+ * } GenericReparseBuffer;
+ * };
+ * } REPARSE_DATA_BUFFER
+ */
+ final short OFFSETOF_REPARSETAG = 0;
+ final short OFFSETOF_PATHOFFSET = 8;
+ final short OFFSETOF_PATHLENGTH = 10;
+ final short OFFSETOF_PATHBUFFER = 16 + 4; // check this
+
+ int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);
+ if (tag != IO_REPARSE_TAG_SYMLINK) {
+ // FIXME: exception doesn't have file name
+ throw new NotLinkException(null, null, "Reparse point is not a symbolic link");
+ }
+
+ // get offset and length of target
+ short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);
+ short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);
+ if ((nameLengthInBytes % 2) != 0)
+ throw new FileSystemException(null, null, "Symbolic link corrupted");
+
+ // copy into char array
+ char[] name = new char[nameLengthInBytes/2];
+ unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,
+ name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
+
+ // remove special prefix
+ String target = stripPrefix(new String(name));
+ if (target.length() == 0) {
+ throw new IOException("Symbolic link target is invalid");
+ }
+ return target;
+ } finally {
+ buffer.release();
+ }
+ }
+
+ /**
+ * Resolve all symbolic-links in a given absolute and normalized path
+ */
+ private static String resolveAllLinks(WindowsPath path)
+ throws IOException
+ {
+ assert path.isAbsolute();
+ WindowsFileSystem fs = path.getFileSystem();
+
+ // iterate through each name element of the path, resolving links as
+ // we go.
+ int linkCount = 0;
+ int elem = 0;
+ while (elem < path.getNameCount()) {
+ WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));
+
+ WindowsFileAttributes attrs = null;
+ try {
+ attrs = WindowsFileAttributes.get(current, false);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(current);
+ }
+
+ /**
+ * If a symbolic link then we resolve it against the parent
+ * of the current name element. We then resolve any remaining
+ * part of the path against the result. The target of the link
+ * may have "." and ".." components so re-normalize and restart
+ * the process from the first element.
+ */
+ if (attrs.isSymbolicLink()) {
+ linkCount++;
+ if (linkCount > 32)
+ throw new IOException("Too many links");
+ WindowsPath target = WindowsPath
+ .createFromNormalizedPath(fs, readLink(current));
+ WindowsPath remainder = null;
+ int count = path.getNameCount();
+ if ((elem+1) < count) {
+ remainder = path.subpath(elem+1, count);
+ }
+ path = current.getParent().resolve(target);
+ try {
+ String full = GetFullPathName(path.toString());
+ if (!full.equals(path.toString())) {
+ path = WindowsPath.createFromNormalizedPath(fs, full);
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(path);
+ }
+ if (remainder != null) {
+ path = path.resolve(remainder);
+ }
+
+ // reset
+ elem = 0;
+ } else {
+ // not a link
+ elem++;
+ }
+ }
+
+ return path.toString();
+ }
+
+ /**
+ * Add long path prefix to path if required.
+ */
+ private static String addLongPathPrefixIfNeeded(String path) {
+ if (path.length() > 248) {
+ if (path.startsWith("\\\\")) {
+ path = "\\\\?\\UNC" + path.substring(1, path.length());
+ } else {
+ path = "\\\\?\\" + path;
+ }
+ }
+ return path;
+ }
+
+ /**
+ * Strip long path or symbolic link prefix from path
+ */
+ private static String stripPrefix(String path) {
+ // prefix for resolved/long path
+ if (path.startsWith("\\\\?\\")) {
+ if (path.startsWith("\\\\?\\UNC\\")) {
+ path = "\\" + path.substring(7);
+ } else {
+ path = path.substring(4);
+ }
+ return path;
+ }
+
+ // prefix for target of symbolic link
+ if (path.startsWith("\\??\\")) {
+ if (path.startsWith("\\??\\UNC\\")) {
+ path = "\\" + path.substring(7);
+ } else {
+ path = path.substring(4);
+ }
+ return path;
+ }
+ return path;
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java b/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java
new file mode 100644
index 000000000..fafee20a4
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java
@@ -0,0 +1,1134 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.misc.Unsafe;
+
+/**
+ * Win32 and library calls.
+ */
+
+class WindowsNativeDispatcher {
+ private WindowsNativeDispatcher() { }
+
+ /**
+ * HANDLE CreateFile(
+ * LPCTSTR lpFileName,
+ * DWORD dwDesiredAccess,
+ * DWORD dwShareMode,
+ * LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ * DWORD dwCreationDisposition,
+ * DWORD dwFlagsAndAttributes,
+ * HANDLE hTemplateFile
+ * )
+ */
+ static long CreateFile(String path,
+ int dwDesiredAccess,
+ int dwShareMode,
+ long lpSecurityAttributes,
+ int dwCreationDisposition,
+ int dwFlagsAndAttributes)
+ throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ return CreateFile0(buffer.address(),
+ dwDesiredAccess,
+ dwShareMode,
+ lpSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes);
+ } finally {
+ buffer.release();
+ }
+ }
+ static long CreateFile(String path,
+ int dwDesiredAccess,
+ int dwShareMode,
+ int dwCreationDisposition,
+ int dwFlagsAndAttributes)
+ throws WindowsException
+ {
+ return CreateFile(path, dwDesiredAccess, dwShareMode, 0L,
+ dwCreationDisposition, dwFlagsAndAttributes);
+ }
+ private static native long CreateFile0(long lpFileName,
+ int dwDesiredAccess,
+ int dwShareMode,
+ long lpSecurityAttributes,
+ int dwCreationDisposition,
+ int dwFlagsAndAttributes)
+ throws WindowsException;
+
+ /**
+ * CloseHandle(
+ * HANDLE hObject
+ * )
+ */
+ static native void CloseHandle(long handle);
+
+ /**
+ * DeleteFile(
+ * LPCTSTR lpFileName
+ * )
+ */
+ static void DeleteFile(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ DeleteFile0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void DeleteFile0(long lpFileName)
+ throws WindowsException;
+
+ /**
+ * CreateDirectory(
+ * LPCTSTR lpPathName,
+ * LPSECURITY_ATTRIBUTES lpSecurityAttributes
+ * )
+ */
+ static void CreateDirectory(String path, long lpSecurityAttributes) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ CreateDirectory0(buffer.address(), lpSecurityAttributes);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void CreateDirectory0(long lpFileName, long lpSecurityAttributes)
+ throws WindowsException;
+
+ /**
+ * RemoveDirectory(
+ * LPCTSTR lpPathName
+ * )
+ */
+ static void RemoveDirectory(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ RemoveDirectory0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void RemoveDirectory0(long lpFileName)
+ throws WindowsException;
+
+ /**
+ * Marks a file as a sparse file.
+ *
+ * DeviceIoControl(
+ * FSCTL_SET_SPARSE
+ * )
+ */
+ static native void DeviceIoControlSetSparse(long handle)
+ throws WindowsException;
+
+ /**
+ * Retrieves the reparse point data associated with the file or directory.
+ *
+ * DeviceIoControl(
+ * FSCTL_GET_REPARSE_POINT
+ * )
+ */
+ static native void DeviceIoControlGetReparsePoint(long handle,
+ long bufferAddress, int bufferSize) throws WindowsException;
+
+ /**
+ * HANDLE FindFirstFile(
+ * LPCTSTR lpFileName,
+ * LPWIN32_FIND_DATA lpFindFileData
+ * )
+ */
+ static FirstFile FindFirstFile(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ FirstFile data = new FirstFile();
+ FindFirstFile0(buffer.address(), data);
+ return data;
+ } finally {
+ buffer.release();
+ }
+ }
+ static class FirstFile {
+ private long handle;
+ private String name;
+
+ private FirstFile() { }
+ public long handle() { return handle; }
+ public String name() { return name; }
+ }
+ private static native void FindFirstFile0(long lpFileName, FirstFile obj)
+ throws WindowsException;
+
+ /**
+ * HANDLE FindFirstFile(
+ * LPCTSTR lpFileName,
+ * LPWIN32_FIND_DATA lpFindFileData
+ * )
+ */
+ static long FindFirstFile(String path, long address) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ return FindFirstFile1(buffer.address(), address);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native long FindFirstFile1(long lpFileName, long address)
+ throws WindowsException;
+
+ /**
+ * FindNextFile(
+ * HANDLE hFindFile,
+ * LPWIN32_FIND_DATA lpFindFileData
+ * )
+ *
+ * @return lpFindFileData->cFileName or null
+ */
+ static native String FindNextFile(long handle, long address)
+ throws WindowsException;
+
+ /**
+ * HANDLE FindFirstStreamW(
+ * LPCWSTR lpFileName,
+ * STREAM_INFO_LEVELS InfoLevel,
+ * LPVOID lpFindStreamData,
+ * DWORD dwFlags
+ * )
+ */
+ static FirstStream FindFirstStream(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ FirstStream data = new FirstStream();
+ FindFirstStream0(buffer.address(), data);
+ if (data.handle() == WindowsConstants.INVALID_HANDLE_VALUE)
+ return null;
+ return data;
+ } finally {
+ buffer.release();
+ }
+ }
+ static class FirstStream {
+ private long handle;
+ private String name;
+
+ private FirstStream() { }
+ public long handle() { return handle; }
+ public String name() { return name; }
+ }
+ private static native void FindFirstStream0(long lpFileName, FirstStream obj)
+ throws WindowsException;
+
+ /*
+ * FindNextStreamW(
+ * HANDLE hFindStream,
+ * LPVOID lpFindStreamData
+ * )
+ */
+ static native String FindNextStream(long handle) throws WindowsException;
+
+ /**
+ * FindClose(
+ * HANDLE hFindFile
+ * )
+ */
+ static native void FindClose(long handle) throws WindowsException;
+
+ /**
+ * GetFileInformationByHandle(
+ * HANDLE hFile,
+ * LPBY_HANDLE_FILE_INFORMATION lpFileInformation
+ * )
+ */
+ static native void GetFileInformationByHandle(long handle, long address)
+ throws WindowsException;
+
+ /**
+ * CopyFileEx(
+ * LPCWSTR lpExistingFileName
+ * LPCWSTR lpNewFileName,
+ * LPPROGRESS_ROUTINE lpProgressRoutine
+ * LPVOID lpData,
+ * LPBOOL pbCancel,
+ * DWORD dwCopyFlags
+ * )
+ */
+ static void CopyFileEx(String source, String target, int flags,
+ long addressToPollForCancel)
+ throws WindowsException
+ {
+ NativeBuffer sourceBuffer = asNativeBuffer(source);
+ NativeBuffer targetBuffer = asNativeBuffer(target);
+ try {
+ CopyFileEx0(sourceBuffer.address(), targetBuffer.address(), flags,
+ addressToPollForCancel);
+ } finally {
+ targetBuffer.release();
+ sourceBuffer.release();
+ }
+ }
+ private static native void CopyFileEx0(long existingAddress, long newAddress,
+ int flags, long addressToPollForCancel) throws WindowsException;
+
+ /**
+ * MoveFileEx(
+ * LPCTSTR lpExistingFileName,
+ * LPCTSTR lpNewFileName,
+ * DWORD dwFlags
+ * )
+ */
+ static void MoveFileEx(String source, String target, int flags)
+ throws WindowsException
+ {
+ NativeBuffer sourceBuffer = asNativeBuffer(source);
+ NativeBuffer targetBuffer = asNativeBuffer(target);
+ try {
+ MoveFileEx0(sourceBuffer.address(), targetBuffer.address(), flags);
+ } finally {
+ targetBuffer.release();
+ sourceBuffer.release();
+ }
+ }
+ private static native void MoveFileEx0(long existingAddress, long newAddress,
+ int flags) throws WindowsException;
+
+ /**
+ * DWORD GetFileAttributes(
+ * LPCTSTR lpFileName
+ * )
+ */
+ static int GetFileAttributes(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ return GetFileAttributes0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int GetFileAttributes0(long lpFileName)
+ throws WindowsException;
+
+ /**
+ * SetFileAttributes(
+ * LPCTSTR lpFileName,
+ * DWORD dwFileAttributes
+ */
+ static void SetFileAttributes(String path, int dwFileAttributes)
+ throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ SetFileAttributes0(buffer.address(), dwFileAttributes);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void SetFileAttributes0(long lpFileName,
+ int dwFileAttributes) throws WindowsException;
+
+ /**
+ * GetFileAttributesEx(
+ * LPCTSTR lpFileName,
+ * GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ * LPVOID lpFileInformation
+ * );
+ */
+ static void GetFileAttributesEx(String path, long address) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ GetFileAttributesEx0(buffer.address(), address);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native void GetFileAttributesEx0(long lpFileName, long address)
+ throws WindowsException;
+ /**
+ * SetFileTime(
+ * HANDLE hFile,
+ * CONST FILETIME *lpCreationTime,
+ * CONST FILETIME *lpLastAccessTime,
+ * CONST FILETIME *lpLastWriteTime
+ * )
+ */
+ static native void SetFileTime(long handle, long createTime,
+ long lastAccessTime, long lastWriteTime) throws WindowsException;
+
+ /**
+ * SetEndOfFile(
+ * HANDLE hFile
+ * )
+ */
+ static native void SetEndOfFile(long handle) throws WindowsException;
+
+ /**
+ * DWORD GetLogicalDrives(VOID)
+ */
+ static native int GetLogicalDrives() throws WindowsException;
+
+ /**
+ * GetVolumeInformation(
+ * LPCTSTR lpRootPathName,
+ * LPTSTR lpVolumeNameBuffer,
+ * DWORD nVolumeNameSize,
+ * LPDWORD lpVolumeSerialNumber,
+ * LPDWORD lpMaximumComponentLength,
+ * LPDWORD lpFileSystemFlags,
+ * LPTSTR lpFileSystemNameBuffer,
+ * DWORD nFileSystemNameSize
+ * )
+ */
+ static VolumeInformation GetVolumeInformation(String root)
+ throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(root);
+ try {
+ VolumeInformation info = new VolumeInformation();
+ GetVolumeInformation0(buffer.address(), info);
+ return info;
+ } finally {
+ buffer.release();
+ }
+ }
+ static class VolumeInformation {
+ private String fileSystemName;
+ private String volumeName;
+ private int volumeSerialNumber;
+ private int flags;
+ private VolumeInformation() { }
+
+ public String fileSystemName() { return fileSystemName; }
+ public String volumeName() { return volumeName; }
+ public int volumeSerialNumber() { return volumeSerialNumber; }
+ public int flags() { return flags; }
+ }
+ private static native void GetVolumeInformation0(long lpRoot,
+ VolumeInformation obj)
+ throws WindowsException;
+
+ /**
+ * UINT GetDriveType(
+ * LPCTSTR lpRootPathName
+ * )
+ */
+ static int GetDriveType(String root) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(root);
+ try {
+ return GetDriveType0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int GetDriveType0(long lpRoot) throws WindowsException;
+
+ /**
+ * GetDiskFreeSpaceEx(
+ * LPCTSTR lpDirectoryName,
+ * PULARGE_INTEGER lpFreeBytesAvailableToCaller,
+ * PULARGE_INTEGER lpTotalNumberOfBytes,
+ * PULARGE_INTEGER lpTotalNumberOfFreeBytes
+ * )
+ */
+ static DiskFreeSpace GetDiskFreeSpaceEx(String path)
+ throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ DiskFreeSpace space = new DiskFreeSpace();
+ GetDiskFreeSpaceEx0(buffer.address(), space);
+ return space;
+ } finally {
+ buffer.release();
+ }
+ }
+ static class DiskFreeSpace {
+ private long freeBytesAvailable;
+ private long totalNumberOfBytes;
+ private long totalNumberOfFreeBytes;
+ private DiskFreeSpace() { }
+
+ public long freeBytesAvailable() { return freeBytesAvailable; }
+ public long totalNumberOfBytes() { return totalNumberOfBytes; }
+ public long totalNumberOfFreeBytes() { return totalNumberOfFreeBytes; }
+ }
+ private static native void GetDiskFreeSpaceEx0(long lpDirectoryName,
+ DiskFreeSpace obj)
+ throws WindowsException;
+
+
+ /**
+ * GetVolumePathName(
+ * LPCTSTR lpszFileName,
+ * LPTSTR lpszVolumePathName,
+ * DWORD cchBufferLength
+ * )
+ *
+ * @return lpFileName
+ */
+ static String GetVolumePathName(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ return GetVolumePathName0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native String GetVolumePathName0(long lpFileName)
+ throws WindowsException;
+
+
+ /**
+ * InitializeSecurityDescriptor(
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ * DWORD dwRevision
+ * )
+ */
+ static native void InitializeSecurityDescriptor(long sdAddress)
+ throws WindowsException;
+
+ /**
+ * InitializeAcl(
+ * PACL pAcl,
+ * DWORD nAclLength,
+ * DWORD dwAclRevision
+ * )
+ */
+ static native void InitializeAcl(long aclAddress, int size)
+ throws WindowsException;
+
+ /**
+ * GetFileSecurity(
+ * LPCTSTR lpFileName,
+ * SECURITY_INFORMATION RequestedInformation,
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ * DWORD nLength,
+ * LPDWORD lpnLengthNeeded
+ * )
+ */
+ static int GetFileSecurity(String path,
+ int requestedInformation,
+ long pSecurityDescriptor,
+ int nLength) throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ return GetFileSecurity0(buffer.address(), requestedInformation,
+ pSecurityDescriptor, nLength);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int GetFileSecurity0(long lpFileName,
+ int requestedInformation,
+ long pSecurityDescriptor,
+ int nLength) throws WindowsException;
+
+ /**
+ * SetFileSecurity(
+ * LPCTSTR lpFileName,
+ * SECURITY_INFORMATION SecurityInformation,
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor
+ * )
+ */
+ static void SetFileSecurity(String path,
+ int securityInformation,
+ long pSecurityDescriptor)
+ throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ SetFileSecurity0(buffer.address(), securityInformation,
+ pSecurityDescriptor);
+ } finally {
+ buffer.release();
+ }
+ }
+ static native void SetFileSecurity0(long lpFileName, int securityInformation,
+ long pSecurityDescriptor) throws WindowsException;
+
+ /**
+ * GetSecurityDescriptorOwner(
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor
+ * PSID *pOwner,
+ * LPBOOL lpbOwnerDefaulted
+ * )
+ *
+ * @return pOwner
+ */
+ static native long GetSecurityDescriptorOwner(long pSecurityDescriptor)
+ throws WindowsException;
+
+ /**
+ * SetSecurityDescriptorOwner(
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ * PSID pOwner,
+ * BOOL bOwnerDefaulted
+ * )
+ */
+ static native void SetSecurityDescriptorOwner(long pSecurityDescriptor,
+ long pOwner)
+ throws WindowsException;
+
+ /**
+ * GetSecurityDescriptorDacl(
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ * LPBOOL lpbDaclPresent,
+ * PACL *pDacl,
+ * LPBOOL lpbDaclDefaulted
+ * )
+ */
+ static native long GetSecurityDescriptorDacl(long pSecurityDescriptor);
+
+ /**
+ * SetSecurityDescriptorDacl(
+ * PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ * BOOL bDaclPresent,
+ * PACL pDacl,
+ * BOOL bDaclDefaulted
+ * )
+ */
+ static native void SetSecurityDescriptorDacl(long pSecurityDescriptor, long pAcl)
+ throws WindowsException;
+
+
+ /**
+ * GetAclInformation(
+ * PACL pAcl,
+ * LPVOID pAclInformation,
+ * DWORD nAclInformationLength,
+ * ACL_INFORMATION_CLASS dwAclInformationClass
+ * )
+ */
+ static AclInformation GetAclInformation(long aclAddress) {
+ AclInformation info = new AclInformation();
+ GetAclInformation0(aclAddress, info);
+ return info;
+ }
+ static class AclInformation {
+ private int aceCount;
+ private AclInformation() { }
+
+ public int aceCount() { return aceCount; }
+ }
+ private static native void GetAclInformation0(long aclAddress,
+ AclInformation obj);
+
+ /**
+ * GetAce(
+ * PACL pAcl,
+ * DWORD dwAceIndex,
+ * LPVOID *pAce
+ * )
+ */
+ static native long GetAce(long aclAddress, int aceIndex);
+
+ /**
+ * AddAccessAllowedAceEx(
+ * PACL pAcl,
+ * DWORD dwAceRevision,
+ * DWORD AceFlags,
+ * DWORD AccessMask,
+ * PSID pSid
+ * )
+ */
+ static native void AddAccessAllowedAceEx(long aclAddress, int flags,
+ int mask, long sidAddress) throws WindowsException;
+
+ /**
+ * AddAccessDeniedAceEx(
+ * PACL pAcl,
+ * DWORD dwAceRevision,
+ * DWORD AceFlags,
+ * DWORD AccessMask,
+ * PSID pSid
+ * )
+ */
+ static native void AddAccessDeniedAceEx(long aclAddress, int flags,
+ int mask, long sidAddress) throws WindowsException;
+
+ /**
+ * LookupAccountSid(
+ * LPCTSTR lpSystemName,
+ * PSID Sid,
+ * LPTSTR Name,
+ * LPDWORD cbName,
+ * LPTSTR ReferencedDomainName,
+ * LPDWORD cbReferencedDomainName,
+ * PSID_NAME_USE peUse
+ * )
+ */
+ static Account LookupAccountSid(long sidAddress) throws WindowsException {
+ Account acc = new Account();
+ LookupAccountSid0(sidAddress, acc);
+ return acc;
+ }
+ static class Account {
+ private String domain;
+ private String name;
+ private int use;
+ private Account() { }
+
+ public String domain() { return domain; }
+ public String name() { return name; }
+ public int use() { return use; }
+ }
+ private static native void LookupAccountSid0(long sidAddress, Account obj)
+ throws WindowsException;
+
+ /**
+ * LookupAccountName(
+ * LPCTSTR lpSystemName,
+ * LPCTSTR lpAccountName,
+ * PSID Sid,
+ * LPDWORD cbSid,
+ * LPTSTR ReferencedDomainName,
+ * LPDWORD cbReferencedDomainName,
+ * PSID_NAME_USE peUse
+ * )
+ *
+ * @return cbSid
+ */
+ static int LookupAccountName(String accountName,
+ long pSid,
+ int cbSid) throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(accountName);
+ try {
+ return LookupAccountName0(buffer.address(), pSid, cbSid);
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native int LookupAccountName0(long lpAccountName, long pSid,
+ int cbSid) throws WindowsException;
+
+ /**
+ * DWORD GetLengthSid(
+ * PSID pSid
+ * )
+ */
+ static native int GetLengthSid(long sidAddress);
+
+ /**
+ * ConvertSidToStringSid(
+ * PSID Sid,
+ * LPTSTR* StringSid
+ * )
+ *
+ * @return StringSid
+ */
+ static native String ConvertSidToStringSid(long sidAddress)
+ throws WindowsException;
+
+ /**
+ * ConvertStringSidToSid(
+ * LPCTSTR StringSid,
+ * PSID* pSid
+ * )
+ *
+ * @return pSid
+ */
+ static long ConvertStringSidToSid(String sidString)
+ throws WindowsException
+ {
+ NativeBuffer buffer = asNativeBuffer(sidString);
+ try {
+ return ConvertStringSidToSid0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native long ConvertStringSidToSid0(long lpStringSid)
+ throws WindowsException;
+
+ /**
+ * HANDLE GetCurrentProcess(VOID)
+ */
+ static native long GetCurrentProcess();
+
+ /**
+ * HANDLE GetCurrentThread(VOID)
+ */
+ static native long GetCurrentThread();
+
+ /**
+ * OpenProcessToken(
+ * HANDLE ProcessHandle,
+ * DWORD DesiredAccess,
+ * PHANDLE TokenHandle
+ * )
+ */
+ static native long OpenProcessToken(long hProcess, int desiredAccess)
+ throws WindowsException;
+
+ /**
+ * OpenThreadToken(
+ * HANDLE ThreadHandle,
+ * DWORD DesiredAccess,
+ * BOOL OpenAsSelf,
+ * PHANDLE TokenHandle
+ * )
+ */
+ static native long OpenThreadToken(long hThread, int desiredAccess,
+ boolean openAsSelf) throws WindowsException;
+
+ /**
+ */
+ static native long DuplicateTokenEx(long hThread, int desiredAccess)
+ throws WindowsException;
+
+ /**
+ * SetThreadToken(
+ * PHANDLE Thread,
+ * HANDLE Token
+ * )
+ */
+ static native void SetThreadToken(long thread, long hToken)
+ throws WindowsException;
+
+ /**
+ * GetTokenInformation(
+ * HANDLE TokenHandle,
+ * TOKEN_INFORMATION_CLASS TokenInformationClass,
+ * LPVOID TokenInformation,
+ * DWORD TokenInformationLength,
+ * PDWORD ReturnLength
+ * )
+ */
+ static native int GetTokenInformation(long token, int tokenInfoClass,
+ long pTokenInfo, int tokenInfoLength) throws WindowsException;
+
+ /**
+ * AdjustTokenPrivileges(
+ * HANDLE TokenHandle,
+ * BOOL DisableAllPrivileges
+ * PTOKEN_PRIVILEGES NewState
+ * DWORD BufferLength
+ * PTOKEN_PRIVILEGES
+ * PDWORD ReturnLength
+ * )
+ */
+ static native void AdjustTokenPrivileges(long token, long luid, int attributes)
+ throws WindowsException;
+
+ /**
+ */
+ static long LookupPrivilegeValue(String name) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(name);
+ try {
+ return LookupPrivilegeValue0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native long LookupPrivilegeValue0(long lpName)
+ throws WindowsException;
+
+ /**
+ * BuildTrusteeWithSid(
+ * PTRUSTEE pTrustee,
+ * PSID pSid
+ * )
+ *
+ * @return pTrustee
+ */
+ static native long BuildTrusteeWithSid(long pSid);
+
+ /**
+ * GetEffectiveRightsFromAcl(
+ * PACL pacl,
+ * PTRUSTEE pTrustee,
+ * PACCESS_MASK pAccessRights
+ * )
+ *
+ * @return AccessRights
+ */
+ static native int GetEffectiveRightsFromAcl(long pAcl, long pTrustee)
+ throws WindowsException;
+
+ /**
+ * CreateSymbolicLink(
+ * LPCWSTR lpSymlinkFileName,
+ * LPCWSTR lpTargetFileName,
+ * DWORD dwFlags
+ * )
+ */
+ static void CreateSymbolicLink(String link, String target, int flags)
+ throws WindowsException
+ {
+ NativeBuffer linkBuffer = asNativeBuffer(link);
+ NativeBuffer targetBuffer = asNativeBuffer(target);
+ try {
+ CreateSymbolicLink0(linkBuffer.address(), targetBuffer.address(),
+ flags);
+ } finally {
+ targetBuffer.release();
+ linkBuffer.release();
+ }
+ }
+ private static native void CreateSymbolicLink0(long linkAddress,
+ long targetAddress, int flags) throws WindowsException;
+
+ /**
+ * CreateHardLink(
+ * LPCTSTR lpFileName,
+ * LPCTSTR lpExistingFileName,
+ * LPSECURITY_ATTRIBUTES lpSecurityAttributes
+ * )
+ */
+ static void CreateHardLink(String newFile, String existingFile)
+ throws WindowsException
+ {
+ NativeBuffer newFileBuffer = asNativeBuffer(newFile);
+ NativeBuffer existingFileBuffer = asNativeBuffer(existingFile);
+ try {
+ CreateHardLink0(newFileBuffer.address(), existingFileBuffer.address());
+ } finally {
+ existingFileBuffer.release();
+ newFileBuffer.release();
+ }
+ }
+ private static native void CreateHardLink0(long newFileBuffer,
+ long existingFiletBuffer) throws WindowsException;
+
+ /**
+ * GetFullPathName(
+ * LPCTSTR lpFileName,
+ * DWORD nBufferLength,
+ * LPTSTR lpBuffer,
+ * LPTSTR *lpFilePart
+ * )
+ */
+ static String GetFullPathName(String path) throws WindowsException {
+ NativeBuffer buffer = asNativeBuffer(path);
+ try {
+ return GetFullPathName0(buffer.address());
+ } finally {
+ buffer.release();
+ }
+ }
+ private static native String GetFullPathName0(long pathAddress)
+ throws WindowsException;
+
+ /**
+ * GetFinalPathNameByHandle(
+ * HANDLE hFile,
+ * LPTSTR lpszFilePath,
+ * DWORD cchFilePath,
+ * DWORD dwFlags
+ * )
+ */
+ static native String GetFinalPathNameByHandle(long handle)
+ throws WindowsException;
+
+ /**
+ * FormatMessage(
+ * DWORD dwFlags,
+ * LPCVOID lpSource,
+ * DWORD dwMessageId,
+ * DWORD dwLanguageId,
+ * LPTSTR lpBuffer,
+ * DWORD nSize,
+ * va_list *Arguments
+ * )
+ */
+ static native String FormatMessage(int errorCode);
+
+ /**
+ * LocalFree(
+ * HLOCAL hMem
+ * )
+ */
+ static native void LocalFree(long address);
+
+ /**
+ * HANDLE CreateIoCompletionPort (
+ * HANDLE FileHandle,
+ * HANDLE ExistingCompletionPort,
+ * DWORD CompletionKey,
+ * DWORD NumberOfConcurrentThreads
+ * )
+ */
+ static native long CreateIoCompletionPort(long fileHandle, long existingPort,
+ int completionKey) throws WindowsException;
+
+
+ /**
+ * GetQueuedCompletionStatus(
+ * HANDLE CompletionPort,
+ * LPDWORD lpNumberOfBytesTransferred,
+ * LPDWORD lpCompletionKey,
+ * LPOVERLAPPED *lpOverlapped,
+ * DWORD dwMilliseconds
+ */
+ static CompletionStatus GetQueuedCompletionStatus(long completionPort)
+ throws WindowsException
+ {
+ CompletionStatus status = new CompletionStatus();
+ GetQueuedCompletionStatus0(completionPort, status);
+ return status;
+ }
+ static class CompletionStatus {
+ private int error;
+ private int bytesTransferred;
+ private int completionKey;
+ private CompletionStatus() { }
+
+ int error() { return error; }
+ int bytesTransferred() { return bytesTransferred; }
+ int completionKey() { return completionKey; }
+ }
+ private static native void GetQueuedCompletionStatus0(long completionPort,
+ CompletionStatus status) throws WindowsException;
+
+ /**
+ * PostQueuedCompletionStatus(
+ * HANDLE CompletionPort,
+ * DWORD dwNumberOfBytesTransferred,
+ * DWORD dwCompletionKey,
+ * LPOVERLAPPED lpOverlapped
+ * )
+ */
+ static native void PostQueuedCompletionStatus(long completionPort,
+ int completionKey) throws WindowsException;
+
+ /**
+ * ReadDirectoryChangesW(
+ * HANDLE hDirectory,
+ * LPVOID lpBuffer,
+ * DWORD nBufferLength,
+ * BOOL bWatchSubtree,
+ * DWORD dwNotifyFilter,
+ * LPDWORD lpBytesReturned,
+ * LPOVERLAPPED lpOverlapped,
+ * LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
+ * )
+ */
+ static native void ReadDirectoryChangesW(long hDirectory,
+ long bufferAddress,
+ int bufferLength,
+ boolean watchSubTree,
+ int filter,
+ long bytesReturnedAddress,
+ long pOverlapped)
+ throws WindowsException;
+
+ /**
+ * BackupRead(
+ * HANDLE hFile,
+ * LPBYTE lpBuffer,
+ * DWORD nNumberOfBytesToRead,
+ * LPDWORD lpNumberOfBytesRead,
+ * BOOL bAbort,
+ * BOOL bProcessSecurity,
+ * LPVOID* lpContext
+ * )
+ */
+ static BackupResult BackupRead(long hFile,
+ long bufferAddress,
+ int bufferSize,
+ boolean abort,
+ long context)
+ throws WindowsException
+ {
+ BackupResult result = new BackupResult();
+ BackupRead0(hFile, bufferAddress, bufferSize, abort, context, result);
+ return result;
+ }
+ static class BackupResult {
+ private int bytesTransferred;
+ private long context;
+ private BackupResult() { }
+
+ int bytesTransferred() { return bytesTransferred; }
+ long context() { return context; }
+ }
+ private static native void BackupRead0(long hFile, long bufferAddress,
+ int bufferSize, boolean abort, long context, BackupResult result)
+ throws WindowsException;
+
+ /**
+ * BackupSeek(
+ * HANDLE hFile,
+ * DWORD dwLowBytesToSeek,
+ * DWORD dwHighBytesToSeek,
+ * LPDWORD lpdwLowByteSeeked,
+ * LPDWORD lpdwHighByteSeeked,
+ * LPVOID* lpContext
+ * )
+ */
+ static native void BackupSeek(long hFile, long bytesToSeek, long context)
+ throws WindowsException;
+
+
+ // -- support for copying String with a NativeBuffer --
+
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ static NativeBuffer asNativeBuffer(String s) {
+ int stringLengthInBytes = s.length() << 1;
+ int sizeInBytes = stringLengthInBytes + 2; // char terminator
+
+ // get a native buffer of sufficient size
+ NativeBuffer buffer = NativeBuffers.getNativeBufferFromCache(sizeInBytes);
+ if (buffer == null) {
+ buffer = NativeBuffers.allocNativeBuffer(sizeInBytes);
+ } else {
+ // buffer already contains the string contents
+ if (buffer.owner() == s)
+ return buffer;
+ }
+
+ // copy into buffer and zero terminate
+ char[] chars = s.toCharArray();
+ unsafe.copyMemory(chars, Unsafe.ARRAY_CHAR_BASE_OFFSET, null,
+ buffer.address(), (long)stringLengthInBytes);
+ unsafe.putChar(buffer.address() + stringLengthInBytes, (char)0);
+ buffer.setOwner(s);
+ return buffer;
+ }
+
+ // -- native library initialization --
+
+ private static native void initIDs();
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ // nio.dll has dependency on net.dll
+ System.loadLibrary("net");
+ System.loadLibrary("nio");
+ return null;
+ }});
+ initIDs();
+ }
+
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsPath.java b/src/windows/classes/sun/nio/fs/WindowsPath.java
new file mode 100644
index 000000000..2fda59d2b
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsPath.java
@@ -0,0 +1,1375 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.AbstractPath;
+import java.nio.channels.*;
+import java.io.*;
+import java.net.URI;
+import java.security.AccessController;
+import java.util.*;
+import java.lang.ref.WeakReference;
+
+import com.sun.nio.file.ExtendedWatchEventModifier;
+
+import sun.security.util.SecurityConstants;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Windows implementation of Path
+ */
+
+class WindowsPath extends AbstractPath {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // The maximum path that does not require long path prefix. On Windows
+ // the maximum path is 260 minus 1 (NUL) but for directories it is 260
+ // minus 12 minus 1 (to allow for the creation of a 8.3 file in the
+ // directory).
+ private static final int MAX_PATH = 247;
+
+ // Maximum extended-length path
+ private static final int MAX_LONG_PATH = 32000;
+
+ // FIXME - eliminate this reference to reduce space
+ private final WindowsFileSystem fs;
+
+ // path type
+ private final WindowsPathType type;
+ // root component (may be empty)
+ private final String root;
+ // normalized path
+ private final String path;
+
+ // the path to use in Win32 calls. This differs from path for relative
+ // paths and has a long path prefix for all paths longer than MAX_PATH.
+ private volatile WeakReference<String> pathForWin32Calls;
+
+ // offsets into name components (computed lazily)
+ private volatile Integer[] offsets;
+
+ // computed hash code (computed lazily, no need to be volatile)
+ private int hash;
+
+
+ /**
+ * Initializes a new instance of this class.
+ */
+ private WindowsPath(WindowsFileSystem fs,
+ WindowsPathType type,
+ String root,
+ String path)
+ {
+ this.fs = fs;
+ this.type = type;
+ this.root = root;
+ this.path = path;
+ }
+
+ /**
+ * Creates a Path by parsing the given path.
+ */
+ static WindowsPath parse(WindowsFileSystem fs, String path) {
+ WindowsPathParser.Result result = WindowsPathParser.parse(path);
+ return new WindowsPath(fs, result.type(), result.root(), result.path());
+ }
+
+ /**
+ * Creates a Path from a given path that is known to be normalized.
+ */
+ static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
+ String path,
+ BasicFileAttributes attrs)
+ {
+ try {
+ WindowsPathParser.Result result =
+ WindowsPathParser.parseNormalizedPath(path);
+ if (attrs == null) {
+ return new WindowsPath(fs,
+ result.type(),
+ result.root(),
+ result.path());
+ } else {
+ return new WindowsPathWithAttributes(fs,
+ result.type(),
+ result.root(),
+ result.path(),
+ attrs);
+ }
+ } catch (InvalidPathException x) {
+ throw new AssertionError(x.getMessage());
+ }
+ }
+
+ /**
+ * Creates a WindowsPath from a given path that is known to be normalized.
+ */
+ static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
+ String path)
+ {
+ return createFromNormalizedPath(fs, path, null);
+ }
+
+ /**
+ * Special implementation with attached/cached attributes (used to quicken
+ * file tree traveral)
+ */
+ private static class WindowsPathWithAttributes
+ extends WindowsPath implements BasicFileAttributesHolder
+ {
+ final WeakReference<BasicFileAttributes> ref;
+
+ WindowsPathWithAttributes(WindowsFileSystem fs,
+ WindowsPathType type,
+ String root,
+ String path,
+ BasicFileAttributes attrs)
+ {
+ super(fs, type, root, path);
+ ref = new WeakReference<BasicFileAttributes>(attrs);
+ }
+
+ @Override
+ public BasicFileAttributes get() {
+ return ref.get();
+ }
+
+ @Override
+ public void invalidate() {
+ ref.clear();
+ }
+ }
+
+ // use this message when throwing exceptions
+ String getPathForExceptionMessage() {
+ return path;
+ }
+
+ // use this path for permission checks
+ String getPathForPermissionCheck() {
+ return path;
+ }
+
+ // use this path for Win32 calls
+ // This method will prefix long paths with \\?\ or \\?\UNC as required.
+ String getPathForWin32Calls() throws WindowsException {
+ // short absolute paths can be used directly
+ if (isAbsolute() && path.length() <= MAX_PATH)
+ return path;
+
+ // return cached values if available
+ WeakReference<String> ref = pathForWin32Calls;
+ String resolved = (ref != null) ? ref.get() : null;
+ if (resolved != null) {
+ // Win32 path already available
+ return resolved;
+ }
+
+ // resolve against default directory
+ resolved = getAbsolutePath();
+
+ // Long paths need to have "." and ".." removed and be prefixed with
+ // "\\?\". Note that it is okay to remove ".." even when it follows
+ // a link - for example, it is okay for foo/link/../bar to be changed
+ // to foo/bar. The reason is that Win32 APIs to access foo/link/../bar
+ // will access foo/bar anyway (which differs to Unix systems)
+ if (resolved.length() > MAX_PATH) {
+ if (resolved.length() > MAX_LONG_PATH) {
+ throw new WindowsException("Cannot access file with path exceeding "
+ + MAX_LONG_PATH + " characters");
+ }
+ resolved = addPrefixIfNeeded(GetFullPathName(resolved));
+ }
+
+ // cache the resolved path (except drive relative paths as the working
+ // directory on removal media devices can change during the lifetime
+ // of the VM)
+ if (type != WindowsPathType.DRIVE_RELATIVE) {
+ synchronized (path) {
+ pathForWin32Calls = new WeakReference<String>(resolved);
+ }
+ }
+ return resolved;
+ }
+
+ // return this path resolved against the file system's default directory
+ private String getAbsolutePath() throws WindowsException {
+ if (isAbsolute())
+ return path;
+
+ // Relative path ("foo" for example)
+ if (type == WindowsPathType.RELATIVE) {
+ String defaultDirectory = getFileSystem().defaultDirectory();
+ if (defaultDirectory.endsWith("\\")) {
+ return defaultDirectory + path;
+ } else {
+ StringBuilder sb =
+ new StringBuilder(defaultDirectory.length() + path.length() + 1);
+ return sb.append(defaultDirectory).append('\\').append(path).toString();
+ }
+ }
+
+ // Directory relative path ("\foo" for example)
+ if (type == WindowsPathType.DIRECTORY_RELATIVE) {
+ String defaultRoot = getFileSystem().defaultRoot();
+ return defaultRoot + path.substring(1);
+ }
+
+ // Drive relative path ("C:foo" for example).
+ if (isSameDrive(root, getFileSystem().defaultRoot())) {
+ // relative to default directory
+ String remaining = path.substring(root.length());
+ String defaultDirectory = getFileSystem().defaultDirectory();
+ String result;
+ if (defaultDirectory.endsWith("\\")) {
+ result = defaultDirectory + remaining;
+ } else {
+ result = defaultDirectory + "\\" + remaining;
+ }
+ return result;
+ } else {
+ // relative to some other drive
+ String wd;
+ try {
+ int dt = GetDriveType(root + "\\");
+ if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR)
+ throw new WindowsException("");
+ wd = GetFullPathName(root + ".");
+ } catch (WindowsException x) {
+ throw new WindowsException("Unable to get working directory of drive '" +
+ Character.toUpperCase(root.charAt(0)) + "'");
+ }
+ String result = wd;
+ if (wd.endsWith("\\")) {
+ result += path.substring(root.length());
+ } else {
+ if (path.length() > root.length())
+ result += "\\" + path.substring(root.length());
+ }
+ return result;
+ }
+ }
+
+ // returns true if same drive letter
+ private static boolean isSameDrive(String root1, String root2) {
+ return Character.toUpperCase(root1.charAt(0)) ==
+ Character.toUpperCase(root2.charAt(0));
+ }
+
+ // Add long path prefix to path if required
+ private static String addPrefixIfNeeded(String path) {
+ if (path.length() > 248) {
+ if (path.startsWith("\\\\")) {
+ path = "\\\\?\\UNC" + path.substring(1, path.length());
+ } else {
+ path = "\\\\?\\" + path;
+ }
+ }
+ return path;
+ }
+
+ @Override
+ public WindowsFileSystem getFileSystem() {
+ return fs;
+ }
+
+ // -- Path operations --
+
+ @Override
+ public Path getName() {
+ // represents root component only
+ if (root.length() == path.length())
+ return null;
+ int off = path.lastIndexOf('\\');
+ if (off < root.length())
+ off = root.length();
+ else
+ off++;
+ return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off));
+ }
+
+ @Override
+ public WindowsPath getParent() {
+ // represents root component only
+ if (root.length() == path.length())
+ return null;
+ int off = path.lastIndexOf('\\');
+ if (off < root.length())
+ return getRoot();
+ else
+ return new WindowsPath(getFileSystem(),
+ type,
+ root,
+ path.substring(0, off));
+ }
+
+ @Override
+ public WindowsPath getRoot() {
+ if (root.length() == 0)
+ return null;
+ return new WindowsPath(getFileSystem(), type, root, root);
+ }
+
+ // package-private
+ boolean isUnc() {
+ return type == WindowsPathType.UNC;
+ }
+
+ boolean needsSlashWhenResolving() {
+ if (path.endsWith("\\"))
+ return false;
+ return path.length() > root.length();
+ }
+
+ @Override
+ public boolean isAbsolute() {
+ return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
+ }
+
+ private WindowsPath checkPath(FileRef path) {
+ if (path == null)
+ throw new NullPointerException();
+ if (!(path instanceof WindowsPath)) {
+ throw new ProviderMismatchException();
+ }
+ return (WindowsPath)path;
+ }
+
+ @Override
+ public WindowsPath relativize(Path obj) {
+ WindowsPath other = checkPath(obj);
+ if (this.equals(other))
+ return null;
+
+ // can only relativize paths of the same type
+ if (this.type != other.type)
+ throw new IllegalArgumentException("'other' is different type of Path");
+
+ // can only relativize paths if root component matches
+ if (!this.root.equalsIgnoreCase(other.root))
+ throw new IllegalArgumentException("'other' has different root");
+
+ int bn = this.getNameCount();
+ int cn = other.getNameCount();
+
+ // skip matching names
+ int n = (bn > cn) ? cn : bn;
+ int i = 0;
+ while (i < n) {
+ if (!this.getName(i).equals(other.getName(i)))
+ break;
+ i++;
+ }
+
+ // append ..\ for remaining names in the base
+ StringBuilder result = new StringBuilder();
+ for (int j=i; j<bn; j++) {
+ result.append("..\\");
+ }
+
+ // append remaining names in child
+ for (int j=i; j<cn; j++) {
+ result.append(other.getName(j).toString());
+ result.append("\\");
+ }
+
+ // drop trailing slash in result
+ result.setLength(result.length()-1);
+ return createFromNormalizedPath(getFileSystem(), result.toString());
+ }
+
+ @Override
+ public Path normalize() {
+ final int count = getNameCount();
+ if (count == 0)
+ return this;
+
+ boolean[] ignore = new boolean[count]; // true => ignore name
+ int remaining = count; // number of names remaining
+
+ // multiple passes to eliminate all occurences of "." and "name/.."
+ int prevRemaining;
+ do {
+ prevRemaining = remaining;
+ int prevName = -1;
+ for (int i=0; i<count; i++) {
+ if (ignore[i])
+ continue;
+
+ String name = elementAsString(i);
+
+ // not "." or ".."
+ if (name.length() > 2) {
+ prevName = i;
+ continue;
+ }
+
+ // "." or something else
+ if (name.length() == 1) {
+ // ignore "."
+ if (name.charAt(0) == '.') {
+ ignore[i] = true;
+ remaining--;
+ } else {
+ prevName = i;
+ }
+ continue;
+ }
+
+ // not ".."
+ if (name.charAt(0) != '.' || name.charAt(1) != '.') {
+ prevName = i;
+ continue;
+ }
+
+ // ".." found
+ if (prevName >= 0) {
+ // name/<ignored>/.. found so mark name and ".." to be
+ // ignored
+ ignore[prevName] = true;
+ ignore[i] = true;
+ remaining = remaining - 2;
+ prevName = -1;
+ } else {
+ // Cases:
+ // C:\<ignored>\..
+ // \\server\\share\<ignored>\..
+ // \<ignored>..
+ if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) {
+ boolean hasPrevious = false;
+ for (int j=0; j<i; j++) {
+ if (!ignore[j]) {
+ hasPrevious = true;
+ break;
+ }
+ }
+ if (!hasPrevious) {
+ // all proceeding names are ignored
+ ignore[i] = true;
+ remaining--;
+ }
+ }
+ }
+ }
+ } while (prevRemaining > remaining);
+
+ // no redundant names
+ if (remaining == count)
+ return this;
+
+ // corner case - all names removed
+ if (remaining == 0) {
+ return getRoot();
+ }
+
+ // re-constitute the path from the remaining names.
+ StringBuilder result = new StringBuilder();
+ if (root != null)
+ result.append(root);
+ for (int i=0; i<count; i++) {
+ if (!ignore[i]) {
+ result.append(getName(i).toString());
+ result.append("\\");
+ }
+ }
+
+ // drop trailing slash in result
+ result.setLength(result.length()-1);
+ return createFromNormalizedPath(getFileSystem(), result.toString());
+ }
+
+ @Override
+ public WindowsPath resolve(Path obj) {
+ if (obj == null)
+ return this;
+ WindowsPath other = checkPath(obj);
+ if (other.isAbsolute())
+ return other;
+
+ switch (other.type) {
+ case RELATIVE: {
+ String result;
+ if (path.endsWith("\\") || (root.length() == path.length())) {
+ result = path + other.path;
+ } else {
+ result = path + "\\" + other.path;
+ }
+ return new WindowsPath(getFileSystem(), type, root, result);
+ }
+
+ case DIRECTORY_RELATIVE: {
+ String result;
+ if (root.endsWith("\\")) {
+ result = root + other.path.substring(1);
+ } else {
+ result = root + other.path;
+ }
+ return createFromNormalizedPath(getFileSystem(), result);
+ }
+
+ case DRIVE_RELATIVE: {
+ if (!root.endsWith("\\"))
+ return other;
+ // if different roots then return other
+ String thisRoot = root.substring(0, root.length()-1);
+ if (!thisRoot.equalsIgnoreCase(other.root))
+ return other;
+ // same roots
+ String remaining = other.path.substring(other.root.length());
+ String result;
+ if (path.endsWith("\\")) {
+ result = path + remaining;
+ } else {
+ result = path + "\\" + remaining;
+ }
+ return createFromNormalizedPath(getFileSystem(), result);
+ }
+
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public WindowsPath resolve(String other) {
+ return resolve(getFileSystem().getPath(other));
+ }
+
+ // generate offset array
+ private void initOffsets() {
+ if (offsets == null) {
+ ArrayList<Integer> list = new ArrayList<Integer>();
+ int start = root.length();
+ int off = root.length();
+ while (off < path.length()) {
+ if (path.charAt(off) != '\\') {
+ off++;
+ } else {
+ list.add(start);
+ start = ++off;
+ }
+ }
+ if (start != off)
+ list.add(start);
+ synchronized (this) {
+ if (offsets == null)
+ offsets = list.toArray(new Integer[list.size()]);
+ }
+ }
+ }
+
+ @Override
+ public int getNameCount() {
+ initOffsets();
+ return offsets.length;
+ }
+
+ private String elementAsString(int i) {
+ initOffsets();
+ if (i == (offsets.length-1))
+ return path.substring(offsets[i]);
+ return path.substring(offsets[i], offsets[i+1]-1);
+ }
+
+ @Override
+ public WindowsPath getName(int index) {
+ initOffsets();
+ if (index < 0 || index >= offsets.length)
+ throw new IllegalArgumentException();
+ return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index));
+ }
+
+ @Override
+ public WindowsPath subpath(int beginIndex, int endIndex) {
+ initOffsets();
+ if (beginIndex < 0)
+ throw new IllegalArgumentException();
+ if (beginIndex >= offsets.length)
+ throw new IllegalArgumentException();
+ if (endIndex > offsets.length)
+ throw new IllegalArgumentException();
+ if (beginIndex >= endIndex)
+ throw new IllegalArgumentException();
+
+ StringBuilder sb = new StringBuilder();
+ Integer[] nelems = new Integer[endIndex - beginIndex];
+ for (int i = beginIndex; i < endIndex; i++) {
+ nelems[i-beginIndex] = sb.length();
+ sb.append(elementAsString(i));
+ if (i != (endIndex-1))
+ sb.append("\\");
+ }
+ return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());
+ }
+
+ @Override
+ public boolean startsWith(Path obj) {
+ WindowsPath other = checkPath(obj);
+
+ // if this path has a root component the given path's root must match
+ if (!this.root.equalsIgnoreCase(other.root))
+ return false;
+
+ // roots match so compare elements
+ int thisCount = getNameCount();
+ int otherCount = other.getNameCount();
+ if (otherCount <= thisCount) {
+ while (--otherCount >= 0) {
+ String thisElement = this.elementAsString(otherCount);
+ String otherElement = other.elementAsString(otherCount);
+ // FIXME: should compare in uppercase
+ if (!thisElement.equalsIgnoreCase(otherElement))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean endsWith(Path obj) {
+ WindowsPath other = checkPath(obj);
+
+ // other path is longer
+ if (other.path.length() > path.length()) {
+ return false;
+ }
+
+ int thisCount = this.getNameCount();
+ int otherCount = other.getNameCount();
+
+ // given path has more elements that this path
+ if (otherCount > thisCount) {
+ return false;
+ }
+
+ // compare roots
+ if (other.root.length() > 0) {
+ if (otherCount < thisCount)
+ return false;
+ // FIXME: should compare in uppercase
+ if (!this.root.equalsIgnoreCase(other.root))
+ return false;
+ }
+
+ // match last 'otherCount' elements
+ int off = thisCount - otherCount;
+ while (--otherCount >= 0) {
+ String thisElement = this.elementAsString(off + otherCount);
+ String otherElement = other.elementAsString(otherCount);
+ // FIXME: should compare in uppercase
+ if (!thisElement.equalsIgnoreCase(otherElement))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(Path obj) {
+ if (obj == null)
+ throw new NullPointerException();
+ String s1 = path;
+ String s2 = ((WindowsPath)obj).path;
+ int n1 = s1.length();
+ int n2 = s2.length();
+ int min = Math.min(n1, n2);
+ for (int i = 0; i < min; i++) {
+ char c1 = s1.charAt(i);
+ char c2 = s2.charAt(i);
+ if (c1 != c2) {
+ c1 = Character.toUpperCase(c1);
+ c2 = Character.toUpperCase(c2);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ }
+ }
+ return n1 - n2;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ((obj != null) && (obj instanceof WindowsPath)) {
+ return compareTo((Path)obj) == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ // OK if two or more threads compute hash
+ int h = hash;
+ if (h == 0) {
+ for (int i = 0; i< path.length(); i++) {
+ h = 31*h + Character.toUpperCase(path.charAt(i));
+ }
+ hash = h;
+ }
+ return h;
+ }
+
+ @Override
+ public String toString() {
+ return path;
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ return new Iterator<Path>() {
+ private int i = 0;
+ @Override
+ public boolean hasNext() {
+ return (i < getNameCount());
+ }
+ @Override
+ public Path next() {
+ if (i < getNameCount()) {
+ Path result = getName(i);
+ i++;
+ return result;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ // -- file operations --
+
+ // package-private
+ long openForReadAttributeAccess(boolean followLinks)
+ throws WindowsException
+ {
+ int flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (!followLinks && getFileSystem().supportsLinks())
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ return CreateFile(getPathForWin32Calls(),
+ FILE_READ_ATTRIBUTES,
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
+ 0L,
+ OPEN_EXISTING,
+ flags);
+ }
+
+ void checkRead() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkRead(getPathForPermissionCheck());
+ }
+ }
+
+ void checkWrite() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkWrite(getPathForPermissionCheck());
+ }
+ }
+
+ void checkDelete() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkDelete(getPathForPermissionCheck());
+ }
+ }
+
+ @Override
+ public FileStore getFileStore()
+ throws IOException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
+ checkRead();
+ }
+ return WindowsFileStore.create(this);
+ }
+
+ /**
+ * Returns buffer with SID_AND_ATTRIBUTES structure representing the user
+ * associated with the current thread access token.
+ * FIXME - this should be cached.
+ */
+ private NativeBuffer getUserInfo() throws IOException {
+ try {
+ long hToken = WindowsSecurity.processTokenWithQueryAccess;
+ int size = GetTokenInformation(hToken, TokenUser, 0L, 0);
+ assert size > 0;
+
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ int newsize = GetTokenInformation(hToken, TokenUser,
+ buffer.address(), size);
+ if (newsize != size)
+ throw new AssertionError();
+ return buffer;
+ } catch (WindowsException x) {
+ buffer.release();
+ throw x;
+ }
+ } catch (WindowsException x) {
+ throw new IOException(x.getMessage());
+ }
+ }
+
+ /**
+ * Reads the file ACL and return the effective access as ACCESS_MASK
+ */
+ private int getEffectiveAccess() throws IOException {
+ // read security descriptor continaing ACL (symlinks are followed)
+ String target = WindowsLinkSupport.getFinalPath(this, true);
+ NativeBuffer aclBuffer = WindowsAclFileAttributeView
+ .getFileSecurity(target, DACL_SECURITY_INFORMATION);
+
+ // retrieves DACL from security descriptor
+ long pAcl = GetSecurityDescriptorDacl(aclBuffer.address());
+
+ // Use GetEffectiveRightsFromAcl to get effective access to file
+ try {
+ NativeBuffer userBuffer = getUserInfo();
+ try {
+ try {
+ // SID_AND_ATTRIBUTES->pSid
+ long pSid = unsafe.getAddress(userBuffer.address());
+ long pTrustee = BuildTrusteeWithSid(pSid);
+ try {
+ return GetEffectiveRightsFromAcl(pAcl, pTrustee);
+ } finally {
+ LocalFree(pTrustee);
+ }
+ } catch (WindowsException x) {
+ throw new IOException("Unable to get effective rights from ACL: " +
+ x.getMessage());
+ }
+ } finally {
+ userBuffer.release();
+ }
+ } finally {
+ aclBuffer.release();
+ }
+ }
+
+ @Override
+ public void checkAccess(AccessMode... modes) throws IOException {
+ // if no access modes then simply file attributes
+ if (modes.length == 0) {
+ checkRead();
+ try {
+ WindowsFileAttributes.get(this, true);
+ } catch (WindowsException exc) {
+ exc.rethrowAsIOException(this);
+ }
+ return;
+ }
+
+ boolean r = false;
+ boolean w = false;
+ boolean x = false;
+ for (AccessMode mode: modes) {
+ switch (mode) {
+ case READ : r = true; break;
+ case WRITE : w = true; break;
+ case EXECUTE : x = true; break;
+ default: throw new AssertionError("Should not get here");
+ }
+ }
+
+ int mask = 0;
+ if (r) {
+ checkRead();
+ mask |= FILE_READ_DATA;
+ }
+ if (w) {
+ checkWrite();
+ mask |= FILE_WRITE_DATA;
+ }
+ if (x) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkExec(getPathForPermissionCheck());
+ mask |= FILE_EXECUTE;
+ }
+
+ if ((getEffectiveAccess() & mask) == 0)
+ throw new AccessDeniedException(
+ this.getPathForExceptionMessage(), null,
+ "Effective permissions does not allow requested access");
+
+ // for write access we neeed to check if the DOS readonly attribute
+ // and if the volume is read-only
+ if (w) {
+ try {
+ WindowsFileAttributes attrs = WindowsFileAttributes.get(this, true);
+ if (!attrs.isDirectory() && attrs.isReadOnly())
+ throw new AccessDeniedException(
+ this.getPathForExceptionMessage(), null,
+ "DOS readonly attribute is set");
+ } catch (WindowsException exc) {
+ exc.rethrowAsIOException(this);
+ }
+
+ if (WindowsFileStore.create(this).isReadOnly()) {
+ throw new AccessDeniedException(
+ this.getPathForExceptionMessage(), null, "Read-only file system");
+ }
+ return;
+ }
+ }
+
+ @Override
+ public void delete(boolean failIfNotExists) throws IOException {
+ checkDelete();
+
+ WindowsFileAttributes attrs = null;
+ try {
+ // need to know if file is a directory or junction
+ attrs = WindowsFileAttributes.get(this, false);
+ if (attrs.isDirectory() || attrs.isDirectoryLink()) {
+ RemoveDirectory(getPathForWin32Calls());
+ } else {
+ DeleteFile(getPathForWin32Calls());
+ }
+ } catch (WindowsException x) {
+
+ // no-op if file does not exist
+ if (!failIfNotExists &&
+ (x.lastError() == ERROR_FILE_NOT_FOUND ||
+ x.lastError() == ERROR_PATH_NOT_FOUND)) return;
+
+ if (attrs != null && attrs.isDirectory()) {
+ // ERROR_ALREADY_EXISTS is returned when attempting to delete
+ // non-empty directory on SAMBA servers.
+ if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
+ x.lastError() == ERROR_ALREADY_EXISTS)
+ {
+ throw new DirectoryNotEmptyException(
+ getPathForExceptionMessage());
+ }
+ }
+ x.rethrowAsIOException(this);
+ }
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter)
+ throws IOException
+ {
+ checkRead();
+ if (filter == null)
+ throw new NullPointerException();
+ return new WindowsDirectoryStream(this, filter);
+ }
+
+ @Override
+ public void implCopyTo(Path obj, CopyOption... options) throws IOException {
+ WindowsPath target = (WindowsPath)obj;
+ WindowsFileCopy.copy(this, target, options);
+ }
+
+ @Override
+ public void implMoveTo(Path obj, CopyOption... options) throws IOException {
+ WindowsPath target = (WindowsPath)obj;
+ WindowsFileCopy.move(this, target, options);
+ }
+
+ private boolean followLinks(LinkOption... options) {
+ boolean followLinks = true;
+ for (LinkOption option: options) {
+ if (option == LinkOption.NOFOLLOW_LINKS) {
+ followLinks = false;
+ continue;
+ }
+ if (option == null)
+ throw new NullPointerException();
+ throw new AssertionError("Should not get here");
+ }
+ return followLinks;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileAttributeView> V
+ getFileAttributeView(Class<V> view, LinkOption... options)
+ {
+ if (view == null)
+ throw new NullPointerException();
+ boolean followLinks = followLinks(options);
+ if (view == BasicFileAttributeView.class)
+ return (V) WindowsFileAttributeViews.createBasicView(this, followLinks);
+ if (view == DosFileAttributeView.class)
+ return (V) WindowsFileAttributeViews.createDosView(this, followLinks);
+ if (view == AclFileAttributeView.class)
+ return (V) new WindowsAclFileAttributeView(this, followLinks);
+ if (view == FileOwnerAttributeView.class)
+ return (V) new FileOwnerAttributeViewImpl(
+ new WindowsAclFileAttributeView(this, followLinks));
+ if (view == UserDefinedFileAttributeView.class)
+ return (V) new WindowsUserDefinedFileAttributeView(this, followLinks);
+ return (V) null;
+ }
+
+ @Override
+ public FileAttributeView getFileAttributeView(String name, LinkOption... options) {
+ boolean followLinks = followLinks(options);
+ if (name.equals("basic"))
+ return WindowsFileAttributeViews.createBasicView(this, followLinks);
+ if (name.equals("dos"))
+ return WindowsFileAttributeViews.createDosView(this, followLinks);
+ if (name.equals("acl"))
+ return new WindowsAclFileAttributeView(this, followLinks);
+ if (name.equals("owner"))
+ return new FileOwnerAttributeViewImpl(
+ new WindowsAclFileAttributeView(this, followLinks));
+ if (name.equals("xattr"))
+ return new WindowsUserDefinedFileAttributeView(this, followLinks);
+ return null;
+ }
+
+ @Override
+ public WindowsPath createDirectory(FileAttribute<?>... attrs)
+ throws IOException
+ {
+ checkWrite();
+ WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
+ try {
+ CreateDirectory(getPathForWin32Calls(), sd.address());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ } finally {
+ sd.release();
+ }
+ return this;
+ }
+
+ @Override
+ public InputStream newInputStream()throws IOException {
+ try {
+ Set<OpenOption> options = Collections.emptySet();
+ FileChannel fc = WindowsChannelFactory
+ .newFileChannel(getPathForWin32Calls(),
+ getPathForPermissionCheck(),
+ options,
+ 0L);
+ return Channels.newInputStream(fc);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ }
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ WindowsSecurityDescriptor sd =
+ WindowsSecurityDescriptor.fromAttribute(attrs);
+ try {
+ return WindowsChannelFactory
+ .newFileChannel(getPathForWin32Calls(),
+ getPathForPermissionCheck(),
+ options,
+ sd.address());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ } finally {
+ sd.release();
+ }
+ }
+
+ @Override
+ public OutputStream newOutputStream(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ // need to copy options to add WRITE
+ Set<OpenOption> opts = new HashSet<OpenOption>(options);
+ if (opts.contains(StandardOpenOption.READ))
+ throw new IllegalArgumentException("READ not allowed");
+ opts.add(StandardOpenOption.WRITE);
+
+ WindowsSecurityDescriptor sd =
+ WindowsSecurityDescriptor.fromAttribute(attrs);
+ FileChannel fc;
+ try {
+ fc = WindowsChannelFactory
+ .newFileChannel(getPathForWin32Calls(),
+ getPathForPermissionCheck(),
+ opts,
+ sd.address());
+ return Channels.newOutputStream(fc);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ return null; // keep compiler happy
+ } finally {
+ sd.release();
+ }
+ }
+
+ @Override
+ public boolean isSameFile(FileRef obj) throws IOException {
+ if (this.equals(obj))
+ return true;
+ if (!(obj instanceof WindowsPath)) // includes null check
+ return false;
+ WindowsPath other = (WindowsPath)obj;
+
+ // check security manager access to both files
+ this.checkRead();
+ other.checkRead();
+
+ // open both files and see if they are the same
+ long h1 = 0L;
+ try {
+ h1 = this.openForReadAttributeAccess(true);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ }
+ try {
+ WindowsFileAttributes attrs1 = null;
+ try {
+ attrs1 = WindowsFileAttributes.readAttributes(h1);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ }
+ long h2 = 0L;
+ try {
+ h2 = other.openForReadAttributeAccess(true);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(other);
+ }
+ try {
+ WindowsFileAttributes attrs2 = null;
+ try {
+ attrs2 = WindowsFileAttributes.readAttributes(h2);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(other);
+ }
+ return WindowsFileAttributes.isSameFile(attrs1, attrs2);
+ } finally {
+ CloseHandle(h2);
+ }
+ } finally {
+ CloseHandle(h1);
+ }
+ }
+
+ @Override
+ public WindowsPath createSymbolicLink(Path obj, FileAttribute<?>... attrs)
+ throws IOException
+ {
+ if (!getFileSystem().supportsLinks()) {
+ throw new UnsupportedOperationException("Symbolic links not supported "
+ + "on this operating system");
+ }
+
+ WindowsPath target = checkPath(obj);
+
+ // no attributes allowed
+ if (attrs.length > 0) {
+ WindowsSecurityDescriptor.fromAttribute(attrs); // may throw NPE or UOE
+ throw new UnsupportedOperationException("Initial file attributes" +
+ "not supported when creating symbolic link");
+ }
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new LinkPermission("symbolic"));
+ this.checkWrite();
+ }
+
+ /**
+ * Throw I/O exception for the drive-relative case because Windows
+ * creates a link with the resolved target for this case.
+ */
+ if (target.type == WindowsPathType.DRIVE_RELATIVE) {
+ throw new IOException("Cannot create symbolic link to drive-relative target");
+ }
+
+ /*
+ * Windows treates symbolic links to directories differently than it
+ * does to other file types. For that reason we check if the exists and
+ * is a directory.
+ */
+ int flags = 0;
+ WindowsPath resolvedTarget =
+ WindowsPath.createFromNormalizedPath(getFileSystem(), resolve(target).path);
+ try {
+ if (WindowsFileAttributes.get(resolvedTarget, true).isDirectory())
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+ } catch (WindowsException x) {
+ // unable to access target so assume target is not a directory
+ }
+
+ // create the link
+ try {
+ CreateSymbolicLink(getPathForWin32Calls(),
+ addPrefixIfNeeded(target.toString()),
+ flags);
+ } catch (WindowsException x) {
+ if (x.lastError() == ERROR_INVALID_REPARSE_DATA) {
+ x.rethrowAsIOException(this, target);
+ } else {
+ x.rethrowAsIOException(this);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public Path createLink(Path obj) throws IOException {
+ WindowsPath existing = checkPath(obj);
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new LinkPermission("hard"));
+ this.checkWrite();
+ existing.checkWrite();
+ }
+
+ // create hard link
+ try {
+ CreateHardLink(this.getPathForWin32Calls(),
+ existing.getPathForWin32Calls());
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this, existing);
+ }
+
+ return this;
+ }
+
+ @Override
+ public WindowsPath readSymbolicLink() throws IOException {
+ if (!getFileSystem().supportsLinks()) {
+ throw new UnsupportedOperationException("symbolic links not supported");
+ }
+
+ // permission check
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ FilePermission perm = new FilePermission(getPathForPermissionCheck(),
+ SecurityConstants.FILE_READLINK_ACTION);
+ AccessController.checkPermission(perm);
+ }
+
+ String target = WindowsLinkSupport.readLink(this);
+ return createFromNormalizedPath(getFileSystem(), target);
+ }
+
+ @Override
+ public URI toUri() {
+ return WindowsUriSupport.toUri(this);
+ }
+
+ @Override
+ public WindowsPath toAbsolutePath() {
+ if (isAbsolute())
+ return this;
+
+ // permission check as per spec
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPropertyAccess("user.dir");
+ }
+
+ try {
+ return createFromNormalizedPath(getFileSystem(), getAbsolutePath());
+ } catch (WindowsException x) {
+ throw new IOError(new IOException(x.getMessage()));
+ }
+ }
+
+ @Override
+ public WindowsPath toRealPath(boolean resolveLinks) throws IOException {
+ checkRead();
+ String rp = WindowsLinkSupport.getRealPath(this, resolveLinks);
+ return createFromNormalizedPath(getFileSystem(), rp);
+ }
+
+ @Override
+ public boolean isHidden() throws IOException {
+ checkRead();
+ WindowsFileAttributes attrs = null;
+ try {
+ attrs = WindowsFileAttributes.get(this, true);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(this);
+ }
+ // DOS hidden attribute not meaningful when set on directories
+ if (attrs.isDirectory())
+ return false;
+ return attrs.isHidden();
+ }
+
+ @Override
+ public WatchKey register(WatchService watcher,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ if (watcher == null)
+ throw new NullPointerException();
+ if (!(watcher instanceof WindowsWatchService))
+ throw new ProviderMismatchException();
+
+ // When a security manager is set then we need to make a defensive
+ // copy of the modifiers and check for the Windows specific FILE_TREE
+ // modifier. When the modifier is present then check that permission
+ // has been granted recursively.
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ boolean watchSubtree = false;
+ final int ml = modifiers.length;
+ if (ml > 0) {
+ modifiers = Arrays.copyOf(modifiers, ml);
+ int i=0;
+ while (i < ml) {
+ if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) {
+ watchSubtree = true;
+ break;
+ }
+ }
+ }
+ String s = getPathForPermissionCheck();
+ sm.checkRead(s);
+ if (watchSubtree)
+ sm.checkRead(s + "\\-");
+ }
+
+ return ((WindowsWatchService)watcher).register(this, events, modifiers);
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsPathParser.java b/src/windows/classes/sun/nio/fs/WindowsPathParser.java
new file mode 100644
index 000000000..411b91b1c
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsPathParser.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.InvalidPathException;
+
+/**
+ * A parser of Windows path strings
+ */
+
+class WindowsPathParser {
+ private WindowsPathParser() { }
+
+ /**
+ * The result of a parse operation
+ */
+ static class Result {
+ private final WindowsPathType type;
+ private final String root;
+ private final String path;
+
+ Result(WindowsPathType type, String root, String path) {
+ this.type = type;
+ this.root = root;
+ this.path = path;
+ }
+
+ /**
+ * The path type
+ */
+ WindowsPathType type() {
+ return type;
+ }
+
+ /**
+ * The root component
+ */
+ String root() {
+ return root;
+ }
+
+ /**
+ * The normalized path (includes root)
+ */
+ String path() {
+ return path;
+ }
+ }
+
+ /**
+ * Parses the given input as a Windows path
+ */
+ static Result parse(String input) {
+ if (input == null || input.length() == 0)
+ throw new InvalidPathException(input, "Empty or null path");
+ return parse(input, true);
+ }
+
+ /**
+ * Parses the given input as a Windows path where it is known that the
+ * path is already normalized.
+ */
+ static Result parseNormalizedPath(String input) {
+ return parse(input, false);
+ }
+
+ /**
+ * Parses the given input as a Windows path.
+ *
+ * @param requireToNormalize
+ * Indicates if the path requires to be normalized
+ */
+ private static Result parse(String input, boolean requireToNormalize) {
+ String root = "";
+ WindowsPathType type = null;
+
+ int len = input.length();
+ int off = 0;
+ if (len > 1) {
+ char c0 = input.charAt(0);
+ char c1 = input.charAt(1);
+ char c = 0;
+ int next = 2;
+ if (isSlash(c0) && isSlash(c1)) {
+ // UNC: We keep the first two slash, collapse all the
+ // following, then take the hostname and share name out,
+ // meanwhile collapsing all the redundant slashes.
+ type = WindowsPathType.UNC;
+ off = nextNonSlash(input, next, len);
+ next = nextSlash(input, off, len);
+ if (off == next)
+ throw new InvalidPathException(input, "UNC path is missing hostname");
+ String host = input.substring(off, next); //host
+ off = nextNonSlash(input, next, len);
+ next = nextSlash(input, off, len);
+ if (off == next)
+ throw new InvalidPathException(input, "UNC path is missing sharename");
+ root = "\\\\" + host + "\\" + input.substring(off, next) + "\\";
+ off = next;
+ } else {
+ if (isLetter(c0) && c1 == ':') {
+ root = input.substring(0, 2);
+ if (len > 2 && isSlash(input.charAt(2))) {
+ off = 3;
+ root += "\\";
+ type = WindowsPathType.ABSOLUTE;
+ } else {
+ off = 2;
+ type = WindowsPathType.DRIVE_RELATIVE;
+ }
+ }
+ }
+ }
+ if (off == 0) {
+ if (isSlash(input.charAt(0))) {
+ type = WindowsPathType.DIRECTORY_RELATIVE;
+ root = "\\";
+ } else {
+ type = WindowsPathType.RELATIVE;
+ }
+ }
+
+ if (requireToNormalize) {
+ StringBuilder sb = new StringBuilder(input.length());
+ sb.append(root);
+ return new Result(type, root, normalize(sb, input, off));
+ } else {
+ return new Result(type, root, input);
+ }
+ }
+
+ /**
+ * Remove redundant slashes from the rest of the path, forcing all slashes
+ * into the preferred slash.
+ */
+ private static String normalize(StringBuilder sb, String path, int off) {
+ int len = path.length();
+ off = nextNonSlash(path, off, len);
+ int start = off;
+ char lastC = 0;
+ while (off < len) {
+ char c = path.charAt(off);
+ if (isSlash(c)) {
+ if (lastC == ' ')
+ throw new InvalidPathException(path,
+ "Trailing char <" + lastC + ">",
+ off - 1);
+ sb.append(path, start, off);
+ off = nextNonSlash(path, off, len);
+ if (off != len) //no slash at the end of normalized path
+ sb.append('\\');
+ start = off;
+ } else {
+ if (isInvalidPathChar(c))
+ throw new InvalidPathException(path,
+ "Illegal char <" + c + ">",
+ off);
+ lastC = c;
+ off++;
+ }
+ }
+ if (start != off) {
+ if (lastC == ' ')
+ throw new InvalidPathException(path,
+ "Trailing char <" + lastC + ">",
+ off - 1);
+ sb.append(path, start, off);
+ }
+ return sb.toString();
+ }
+
+ private static final boolean isSlash(char c) {
+ return (c == '\\') || (c == '/');
+ }
+
+ private static final int nextNonSlash(String path, int off, int end) {
+ while (off < end && isSlash(path.charAt(off))) { off++; }
+ return off;
+ }
+
+ private static final int nextSlash(String path, int off, int end) {
+ char c;
+ while (off < end && !isSlash(c=path.charAt(off))) {
+ if (isInvalidPathChar(c))
+ throw new InvalidPathException(path,
+ "Illegal character [" + c + "] in path",
+ off);
+ off++;
+ }
+ return off;
+ }
+
+ private static final boolean isLetter(char c) {
+ return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
+ }
+
+ // Reserved characters for window path name
+ private static final String reservedChars = "<>:\"|?*";
+ private static final boolean isInvalidPathChar(char ch) {
+ return ch < '\u0020' || reservedChars.indexOf(ch) != -1;
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsPathType.java b/src/windows/classes/sun/nio/fs/WindowsPathType.java
new file mode 100644
index 000000000..ae3651f1f
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsPathType.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * A type safe enum of Windows path types.
+ */
+
+enum WindowsPathType {
+ ABSOLUTE, // C:\foo
+ UNC, // \\server\share\foo
+ RELATIVE, // foo
+ DIRECTORY_RELATIVE, // \foo
+ DRIVE_RELATIVE // C:foo
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsSecurity.java b/src/windows/classes/sun/nio/fs/WindowsSecurity.java
new file mode 100644
index 000000000..9d44257b5
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsSecurity.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Security related utility methods.
+ */
+
+class WindowsSecurity {
+ private WindowsSecurity() { }
+
+ // opens process token for given access
+ private static long openProcessToken(int access) {
+ try {
+ return OpenProcessToken(GetCurrentProcess(), access);
+ } catch (WindowsException x) {
+ return 0L;
+ }
+ }
+
+ /**
+ * Returns the access token for this process with TOKEN_DUPLICATE access
+ */
+ static final long processTokenWithDuplicateAccess =
+ openProcessToken(TOKEN_DUPLICATE);
+
+ /**
+ * Returns the access token for this process with TOKEN_QUERY access
+ */
+ static final long processTokenWithQueryAccess =
+ openProcessToken(TOKEN_QUERY);
+
+ /**
+ * Returned by enablePrivilege when code may require a given privilege.
+ * The drop method should be invoked after the operation completes so as
+ * to revert the privilege.
+ */
+ static interface Privilege {
+ void drop();
+ }
+
+ /**
+ * Attempts to enable the given privilege for this method.
+ */
+ static Privilege enablePrivilege(String priv) {
+ final long pLuid;
+ try {
+ pLuid = LookupPrivilegeValue(priv);
+ } catch (WindowsException x) {
+ // indicates bug in caller
+ throw new AssertionError(x);
+ }
+
+ long hToken = 0L;
+ boolean impersontating = false;
+ boolean elevated = false;
+ try {
+ hToken = OpenThreadToken(GetCurrentThread(),
+ TOKEN_ADJUST_PRIVILEGES, false);
+ if (hToken == 0L && processTokenWithDuplicateAccess != 0L) {
+ hToken = DuplicateTokenEx(processTokenWithDuplicateAccess,
+ (TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE));
+ SetThreadToken(0L, hToken);
+ impersontating = true;
+ }
+
+ if (hToken != 0L) {
+ AdjustTokenPrivileges(hToken, pLuid, SE_PRIVILEGE_ENABLED);
+ elevated = true;
+ }
+ } catch (WindowsException x) {
+ // nothing to do, privilege not enabled
+ }
+
+ final long token = hToken;
+ final boolean stopImpersontating = impersontating;
+ final boolean needToRevert = elevated;
+
+ return new Privilege() {
+ @Override
+ public void drop() {
+ try {
+ if (stopImpersontating) {
+ SetThreadToken(0L, 0L);
+ } else {
+ if (needToRevert) {
+ AdjustTokenPrivileges(token, pLuid, 0);
+ }
+ }
+ } catch (WindowsException x) {
+ // should not happen
+ throw new AssertionError(x);
+ }
+ }
+ };
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java b/src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java
new file mode 100644
index 000000000..0fa0725c5
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.ProviderMismatchException;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * A SecurityDescriptor for use when setting a file's ACL or creating a file
+ * with an initial ACL.
+ */
+
+class WindowsSecurityDescriptor {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ /**
+ * typedef struct _ACL {
+ * BYTE AclRevision;
+ * BYTE Sbz1;
+ * WORD AclSize;
+ * WORD AceCount;
+ * WORD Sbz2;
+ * } ACL;
+ *
+ * typedef struct _ACE_HEADER {
+ * BYTE AceType;
+ * BYTE AceFlags;
+ * WORD AceSize;
+ * } ACE_HEADER;
+ *
+ * typedef struct _ACCESS_ALLOWED_ACE {
+ * ACE_HEADER Header;
+ * ACCESS_MASK Mask;
+ * DWORD SidStart;
+ * } ACCESS_ALLOWED_ACE;
+ *
+ * typedef struct _ACCESS_DENIED_ACE {
+ * ACE_HEADER Header;
+ * ACCESS_MASK Mask;
+ * DWORD SidStart;
+ * } ACCESS_DENIED_ACE;
+ *
+ * typedef struct _SECURITY_DESCRIPTOR {
+ * BYTE Revision;
+ * BYTE Sbz1;
+ * SECURITY_DESCRIPTOR_CONTROL Control;
+ * PSID Owner;
+ * PSID Group;
+ * PACL Sacl;
+ * PACL Dacl;
+ * } SECURITY_DESCRIPTOR;
+ */
+ private static final short SIZEOF_ACL = 8;
+ private static final short SIZEOF_ACCESS_ALLOWED_ACE = 12;
+ private static final short SIZEOF_ACCESS_DENIED_ACE = 12;
+ private static final short SIZEOF_SECURITY_DESCRIPTOR = 20;
+
+ private static final short OFFSETOF_TYPE = 0;
+ private static final short OFFSETOF_FLAGS = 1;
+ private static final short OFFSETOF_ACCESS_MASK = 4;
+ private static final short OFFSETOF_SID = 8;
+
+ // null security descriptor
+ private static final WindowsSecurityDescriptor NULL_DESCRIPTOR =
+ new WindowsSecurityDescriptor();
+
+ // native resources
+ private final List<Long> sidList;
+ private final NativeBuffer aclBuffer, sdBuffer;
+
+ /**
+ * Creates the "null" SecurityDescriptor
+ */
+ private WindowsSecurityDescriptor() {
+ this.sidList = null;
+ this.aclBuffer = null;
+ this.sdBuffer = null;
+ }
+
+ /**
+ * Creates a SecurityDescriptor from the given ACL
+ */
+ private WindowsSecurityDescriptor(List<AclEntry> acl) throws IOException {
+ boolean initialized = false;
+
+ // SECURITY: need to copy list in case size changes during processing
+ acl = new ArrayList<AclEntry>(acl);
+
+ // list of SIDs
+ sidList = new ArrayList<Long>(acl.size());
+ try {
+ // initial size of ACL
+ int size = SIZEOF_ACL;
+
+ // get the SID for each entry
+ for (AclEntry entry: acl) {
+ UserPrincipal user = entry.principal();
+ if (!(user instanceof WindowsUserPrincipals.User))
+ throw new ProviderMismatchException();
+ String sidString = ((WindowsUserPrincipals.User)user).sidString();
+ try {
+ long pSid = ConvertStringSidToSid(sidString);
+ sidList.add(pSid);
+
+ // increase size to allow for entry
+ size += GetLengthSid(pSid) +
+ Math.max(SIZEOF_ACCESS_ALLOWED_ACE, SIZEOF_ACCESS_DENIED_ACE);
+
+ } catch (WindowsException x) {
+ throw new IOException("Failed to get SID for " + user.getName()
+ + ": " + x.errorString());
+ }
+ }
+
+ // allocate memory for the ACL
+ aclBuffer = NativeBuffers.getNativeBuffer(size);
+ sdBuffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR);
+
+ InitializeAcl(aclBuffer.address(), size);
+
+ // Add entry ACE to the ACL
+ int i = 0;
+ while (i < acl.size()) {
+ AclEntry entry = acl.get(i);
+ long pSid = sidList.get(i);
+ try {
+ encode(entry, pSid, aclBuffer.address());
+ } catch (WindowsException x) {
+ throw new IOException("Failed to encode ACE: " +
+ x.errorString());
+ }
+ i++;
+ }
+
+ // initialize security descriptor and set DACL
+ InitializeSecurityDescriptor(sdBuffer.address());
+ SetSecurityDescriptorDacl(sdBuffer.address(), aclBuffer.address());
+ initialized = true;
+ } catch (WindowsException x) {
+ throw new IOException(x.getMessage());
+ } finally {
+ // release resources if not completely initialized
+ if (!initialized)
+ release();
+ }
+ }
+
+ /**
+ * Releases memory associated with SecurityDescriptor
+ */
+ void release() {
+ if (sdBuffer != null)
+ sdBuffer.release();
+ if (aclBuffer != null)
+ aclBuffer.release();
+ if (sidList != null) {
+ // release memory for SIDs
+ for (Long sid: sidList) {
+ LocalFree(sid);
+ }
+ }
+ }
+
+ /**
+ * Returns address of SecurityDescriptor
+ */
+ long address() {
+ return (sdBuffer == null) ? 0L : sdBuffer.address();
+ }
+
+ // decode Windows ACE to NFSv4 AclEntry
+ private static AclEntry decode(long aceAddress)
+ throws IOException
+ {
+ // map type
+ byte aceType = unsafe.getByte(aceAddress + OFFSETOF_TYPE);
+ if (aceType != ACCESS_ALLOWED_ACE_TYPE && aceType != ACCESS_DENIED_ACE_TYPE)
+ return null;
+ AclEntryType type;
+ if (aceType == ACCESS_ALLOWED_ACE_TYPE) {
+ type = AclEntryType.ALLOW;
+ } else {
+ type = AclEntryType.DENY;
+ }
+
+ // map flags
+ byte aceFlags = unsafe.getByte(aceAddress + OFFSETOF_FLAGS);
+ Set<AclEntryFlag> flags = new HashSet<AclEntryFlag>();
+ if ((aceFlags & OBJECT_INHERIT_ACE) != 0)
+ flags.add(AclEntryFlag.FILE_INHERIT);
+ if ((aceFlags & CONTAINER_INHERIT_ACE) != 0)
+ flags.add(AclEntryFlag.DIRECTORY_INHERIT);
+ if ((aceFlags & NO_PROPAGATE_INHERIT_ACE) != 0)
+ flags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
+ if ((aceFlags & INHERIT_ONLY_ACE) != 0)
+ flags.add(AclEntryFlag.INHERIT_ONLY);
+
+ // map access mask
+ int mask = unsafe.getInt(aceAddress + OFFSETOF_ACCESS_MASK);
+ Set<AclEntryPermission> perms = new HashSet<AclEntryPermission>();
+ if ((mask & FILE_READ_DATA) > 0)
+ perms.add(AclEntryPermission.READ_DATA);
+ if ((mask & FILE_WRITE_DATA) > 0)
+ perms.add(AclEntryPermission.WRITE_DATA);
+ if ((mask & FILE_APPEND_DATA ) > 0)
+ perms.add(AclEntryPermission.APPEND_DATA);
+ if ((mask & FILE_READ_EA) > 0)
+ perms.add(AclEntryPermission.READ_NAMED_ATTRS);
+ if ((mask & FILE_WRITE_EA) > 0)
+ perms.add(AclEntryPermission.WRITE_NAMED_ATTRS);
+ if ((mask & FILE_EXECUTE) > 0)
+ perms.add(AclEntryPermission.EXECUTE);
+ if ((mask & FILE_DELETE_CHILD ) > 0)
+ perms.add(AclEntryPermission.DELETE_CHILD);
+ if ((mask & FILE_READ_ATTRIBUTES) > 0)
+ perms.add(AclEntryPermission.READ_ATTRIBUTES);
+ if ((mask & FILE_WRITE_ATTRIBUTES) > 0)
+ perms.add(AclEntryPermission.WRITE_ATTRIBUTES);
+ if ((mask & DELETE) > 0)
+ perms.add(AclEntryPermission.DELETE);
+ if ((mask & READ_CONTROL) > 0)
+ perms.add(AclEntryPermission.READ_ACL);
+ if ((mask & WRITE_DAC) > 0)
+ perms.add(AclEntryPermission.WRITE_ACL);
+ if ((mask & WRITE_OWNER) > 0)
+ perms.add(AclEntryPermission.WRITE_OWNER);
+ if ((mask & SYNCHRONIZE) > 0)
+ perms.add(AclEntryPermission.SYNCHRONIZE);
+
+ // lookup SID to create UserPrincipal
+ long sidAddress = aceAddress + OFFSETOF_SID;
+ UserPrincipal user = WindowsUserPrincipals.fromSid(sidAddress);
+
+ return AclEntry.newBuilder()
+ .setType(type)
+ .setPrincipal(user)
+ .setFlags(flags).setPermissions(perms).build();
+ }
+
+ // encode NFSv4 AclEntry as Windows ACE to given ACL
+ private static void encode(AclEntry ace, long sidAddress, long aclAddress)
+ throws WindowsException
+ {
+ // ignore non-allow/deny entries for now
+ if (ace.type() != AclEntryType.ALLOW && ace.type() != AclEntryType.DENY)
+ return;
+ boolean allow = (ace.type() == AclEntryType.ALLOW);
+
+ // map access mask
+ Set<AclEntryPermission> aceMask = ace.permissions();
+ int mask = 0;
+ if (aceMask.contains(AclEntryPermission.READ_DATA))
+ mask |= FILE_READ_DATA;
+ if (aceMask.contains(AclEntryPermission.WRITE_DATA))
+ mask |= FILE_WRITE_DATA;
+ if (aceMask.contains(AclEntryPermission.APPEND_DATA))
+ mask |= FILE_APPEND_DATA;
+ if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS))
+ mask |= FILE_READ_EA;
+ if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS))
+ mask |= FILE_WRITE_EA;
+ if (aceMask.contains(AclEntryPermission.EXECUTE))
+ mask |= FILE_EXECUTE;
+ if (aceMask.contains(AclEntryPermission.DELETE_CHILD))
+ mask |= FILE_DELETE_CHILD;
+ if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES))
+ mask |= FILE_READ_ATTRIBUTES;
+ if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES))
+ mask |= FILE_WRITE_ATTRIBUTES;
+ if (aceMask.contains(AclEntryPermission.DELETE))
+ mask |= DELETE;
+ if (aceMask.contains(AclEntryPermission.READ_ACL))
+ mask |= READ_CONTROL;
+ if (aceMask.contains(AclEntryPermission.WRITE_ACL))
+ mask |= WRITE_DAC;
+ if (aceMask.contains(AclEntryPermission.WRITE_OWNER))
+ mask |= WRITE_OWNER;
+ if (aceMask.contains(AclEntryPermission.SYNCHRONIZE))
+ mask |= SYNCHRONIZE;
+
+ // map flags
+ Set<AclEntryFlag> aceFlags = ace.flags();
+ byte flags = 0;
+ if (aceFlags.contains(AclEntryFlag.FILE_INHERIT))
+ flags |= OBJECT_INHERIT_ACE;
+ if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT))
+ flags |= CONTAINER_INHERIT_ACE;
+ if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
+ flags |= NO_PROPAGATE_INHERIT_ACE;
+ if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY))
+ flags |= INHERIT_ONLY_ACE;
+
+ if (allow) {
+ AddAccessAllowedAceEx(aclAddress, flags, mask, sidAddress);
+ } else {
+ AddAccessDeniedAceEx(aclAddress, flags, mask, sidAddress);
+ }
+ }
+
+ /**
+ * Creates a security descriptor with a DACL representing the given ACL.
+ */
+ static WindowsSecurityDescriptor create(List<AclEntry> acl)
+ throws IOException
+ {
+ return new WindowsSecurityDescriptor(acl);
+ }
+
+ /**
+ * Processes the array of attributes looking for the attribute "acl:acl".
+ * Returns security descriptor representing the ACL or the "null" security
+ * descriptor if the attribute is not in the array.
+ */
+ @SuppressWarnings("unchecked")
+ static WindowsSecurityDescriptor fromAttribute(FileAttribute<?>... attrs)
+ throws IOException
+ {
+ WindowsSecurityDescriptor sd = NULL_DESCRIPTOR;
+ for (FileAttribute<?> attr: attrs) {
+ // if more than one ACL specified then last one wins
+ if (sd != NULL_DESCRIPTOR)
+ sd.release();
+ if (attr == null)
+ throw new NullPointerException();
+ if (attr.name().equals("acl:acl")) {
+ List<AclEntry> acl = (List<AclEntry>)attr.value();
+ sd = new WindowsSecurityDescriptor(acl);
+ } else {
+ throw new UnsupportedOperationException("'" + attr.name() +
+ "' not supported as initial attribute");
+ }
+ }
+ return sd;
+ }
+
+ /**
+ * Extracts DACL from security descriptor.
+ */
+ static List<AclEntry> getAcl(long pSecurityDescriptor) throws IOException {
+ // get address of DACL
+ long aclAddress = GetSecurityDescriptorDacl(pSecurityDescriptor);
+
+ // get ACE count
+ int aceCount = 0;
+ if (aclAddress == 0L) {
+ // no ACEs
+ aceCount = 0;
+ } else {
+ AclInformation aclInfo = GetAclInformation(aclAddress);
+ aceCount = aclInfo.aceCount();
+ }
+ ArrayList<AclEntry> result = new ArrayList<AclEntry>(aceCount);
+
+ // decode each of the ACEs to AclEntry objects
+ for (int i=0; i<aceCount; i++) {
+ long aceAddress = GetAce(aclAddress, i);
+ AclEntry entry = decode(aceAddress);
+ if (entry != null)
+ result.add(entry);
+ }
+ return result;
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsUriSupport.java b/src/windows/classes/sun/nio/fs/WindowsUriSupport.java
new file mode 100644
index 000000000..d87ba82b7
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsUriSupport.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Utility methods to convert between Path and URIs.
+ */
+
+class WindowsUriSupport {
+ private WindowsUriSupport() {
+ }
+
+ // suffix for IPv6 literal address
+ private static final String IPV6_LITERAL_SUFFIX = ".ipv6-literal.net";
+
+ /**
+ * Returns URI to represent the given (absolute) path
+ */
+ private static URI toUri(String path, boolean isUnc, boolean addSlash) {
+ String uriHost;
+ String uriPath;
+
+ if (isUnc) {
+ int slash = path.indexOf('\\', 2);
+ uriHost = path.substring(2, slash);
+ uriPath = path.substring(slash).replace('\\', '/');
+
+ // handle IPv6 literal addresses
+ // 1. drop .ivp6-literal.net
+ // 2. replace "-" with ":"
+ // 3. replace "s" with "%" (zone/scopeID delimiter)
+ if (uriHost.endsWith(IPV6_LITERAL_SUFFIX)) {
+ uriHost = uriHost
+ .substring(0, uriHost.length() - IPV6_LITERAL_SUFFIX.length())
+ .replace('-', ':')
+ .replace('s', '%');
+ }
+ } else {
+ uriHost = "";
+ uriPath = "/" + path.replace('\\', '/');
+ }
+
+ // append slash if known to be directory
+ if (addSlash)
+ uriPath += "/";
+
+ // return file:///C:/My%20Documents or file://server/share/foo
+ try {
+ return new URI("file", uriHost, uriPath, null);
+ } catch (URISyntaxException x) {
+ if (!isUnc)
+ throw new AssertionError(x);
+ }
+
+ // if we get here it means we've got a UNC with reserved characters
+ // in the server name. The authority component cannot contain escaped
+ // octets so fallback to encoding the server name into the URI path
+ // component.
+ uriPath = "//" + path.replace('\\', '/');
+ if (addSlash)
+ uriPath += "/";
+ try {
+ return new URI("file", null, uriPath, null);
+ } catch (URISyntaxException x) {
+ throw new AssertionError(x);
+ }
+ }
+
+ /**
+ * Converts given Path to a URI
+ */
+ static URI toUri(WindowsPath path) {
+ path = path.toAbsolutePath();
+ String s = path.toString();
+
+ // trailing slash will be added if file is a directory. Skip check if
+ // already have trailing space
+ boolean addSlash = false;
+ if (!s.endsWith("\\")) {
+ try {
+ addSlash = WindowsFileAttributes.get(path, true).isDirectory();
+ } catch (WindowsException x) {
+ }
+ }
+
+ return toUri(s, path.isUnc(), addSlash);
+ }
+
+ /**
+ * Converts given URI to a Path
+ */
+ static WindowsPath fromUri(WindowsFileSystem fs, URI uri) {
+ if (!uri.isAbsolute())
+ throw new IllegalArgumentException("URI is not absolute");
+ if (uri.isOpaque())
+ throw new IllegalArgumentException("URI is not hierarchical");
+ String scheme = uri.getScheme();
+ if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
+ throw new IllegalArgumentException("URI scheme is not \"file\"");
+ if (uri.getFragment() != null)
+ throw new IllegalArgumentException("URI has a fragment component");
+ if (uri.getQuery() != null)
+ throw new IllegalArgumentException("URI has a query component");
+ String path = uri.getPath();
+ if (path.equals(""))
+ throw new IllegalArgumentException("URI path component is empty");
+
+ // UNC
+ String auth = uri.getAuthority();
+ if (auth != null && !auth.equals("")) {
+ String host = uri.getHost();
+ if (host == null)
+ throw new IllegalArgumentException("URI authority component has undefined host");
+ if (uri.getUserInfo() != null)
+ throw new IllegalArgumentException("URI authority component has user-info");
+ if (uri.getPort() != -1)
+ throw new IllegalArgumentException("URI authority component has port number");
+
+ // IPv6 literal
+ // 1. drop enclosing brackets
+ // 2. replace ":" with "-"
+ // 3. replace "%" with "s" (zone/scopeID delimiter)
+ // 4. Append .ivp6-literal.net
+ if (host.startsWith("[")) {
+ host = host.substring(1, host.length()-1)
+ .replace(':', '-')
+ .replace('%', 's');
+ host += IPV6_LITERAL_SUFFIX;
+ }
+
+ // reconstitute the UNC
+ path = "\\\\" + host + path;
+ } else {
+ if ((path.length() > 2) && (path.charAt(2) == ':')) {
+ // "/c:/foo" --> "c:/foo"
+ path = path.substring(1);
+ }
+ }
+ return WindowsPath.parse(fs, path);
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java b/src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java
new file mode 100644
index 000000000..db361c97e
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import static java.nio.file.StandardOpenOption.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.io.IOException;
+import java.util.*;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Windows emulation of NamedAttributeView using Alternative Data Streams
+ */
+
+class WindowsUserDefinedFileAttributeView
+ extends AbstractUserDefinedFileAttributeView
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // syntax to address named streams
+ private String join(String file, String name) {
+ if (name == null)
+ throw new NullPointerException("'name' is null");
+ return file + ":" + name;
+ }
+ private String join(WindowsPath file, String name) throws WindowsException {
+ return join(file.getPathForWin32Calls(), name);
+ }
+
+ private final WindowsPath file;
+ private final boolean followLinks;
+
+ WindowsUserDefinedFileAttributeView(WindowsPath file, boolean followLinks) {
+ this.file = file;
+ this.followLinks = followLinks;
+ }
+
+ // enumerates the file streams using FindFirstStream/FindNextStream APIs.
+ private List<String> listUsingStreamEnumeration() throws IOException {
+ List<String> list = new ArrayList<String>();
+ try {
+ FirstStream first = FindFirstStream(file.getPathForWin32Calls());
+ if (first != null) {
+ long handle = first.handle();
+ try {
+ // first stream is always ::$DATA for files
+ String name = first.name();
+ if (!name.equals("::$DATA")) {
+ String[] segs = name.split(":");
+ list.add(segs[1]);
+ }
+ while ((name = FindNextStream(handle)) != null) {
+ String[] segs = name.split(":");
+ list.add(segs[1]);
+ }
+ } finally {
+ FindClose(handle);
+ }
+ }
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ // enumerates the file streams by reading the stream headers using
+ // BackupRead
+ private List<String> listUsingBackupRead() throws IOException {
+ long handle = -1L;
+ try {
+ int flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (!followLinks && file.getFileSystem().supportsLinks())
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+ handle = CreateFile(file.getPathForWin32Calls(),
+ GENERIC_READ,
+ FILE_SHARE_READ, // no write as we depend on file size
+ OPEN_EXISTING,
+ flags);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ }
+
+ // buffer to read stream header and stream name.
+ final int BUFFER_SIZE = 4096;
+ NativeBuffer buffer = null;
+
+ // result with names of alternative data streams
+ final List<String> list = new ArrayList<String>();
+
+ try {
+ buffer = NativeBuffers.getNativeBuffer(BUFFER_SIZE);
+ long address = buffer.address();
+
+ /**
+ * typedef struct _WIN32_STREAM_ID {
+ * DWORD dwStreamId;
+ * DWORD dwStreamAttributes;
+ * LARGE_INTEGER Size;
+ * DWORD dwStreamNameSize;
+ * WCHAR cStreamName[ANYSIZE_ARRAY];
+ * } WIN32_STREAM_ID;
+ */
+ final int SIZEOF_STREAM_HEADER = 20;
+ final int OFFSETOF_STREAM_ID = 0;
+ final int OFFSETOF_STREAM_SIZE = 8;
+ final int OFFSETOF_STREAM_NAME_SIZE = 16;
+
+ long context = 0L;
+ try {
+ for (;;) {
+ // read stream header
+ BackupResult result = BackupRead(handle, address,
+ SIZEOF_STREAM_HEADER, false, context);
+ context = result.context();
+ if (result.bytesTransferred() == 0)
+ break;
+
+ int streamId = unsafe.getInt(address + OFFSETOF_STREAM_ID);
+ long streamSize = unsafe.getLong(address + OFFSETOF_STREAM_SIZE);
+ int nameSize = unsafe.getInt(address + OFFSETOF_STREAM_NAME_SIZE);
+
+ // read stream name
+ if (nameSize > 0) {
+ result = BackupRead(handle, address, nameSize, false, context);
+ if (result.bytesTransferred() != nameSize)
+ break;
+ }
+
+ // check for alternative data stream
+ if (streamId == BACKUP_ALTERNATE_DATA) {
+ char[] nameAsArray = new char[nameSize/2];
+ unsafe.copyMemory(null, address, nameAsArray,
+ Unsafe.ARRAY_CHAR_BASE_OFFSET, nameSize);
+
+ String[] segs = new String(nameAsArray).split(":");
+ if (segs.length == 3)
+ list.add(segs[1]);
+ }
+
+ // sparse blocks not currently handled as documentation
+ // is not sufficient on how the spase block can be skipped.
+ if (streamId == BACKUP_SPARSE_BLOCK) {
+ throw new IOException("Spare blocks not handled");
+ }
+
+ // seek to end of stream
+ if (streamSize > 0L) {
+ BackupSeek(handle, streamSize, context);
+ }
+ }
+ } catch (WindowsException x) {
+ // failed to read or seek
+ throw new IOException(x.errorString());
+ } finally {
+ // release context
+ if (context != 0L) {
+ try {
+ BackupRead(handle, 0L, 0, true, context);
+ } catch (WindowsException ignore) { }
+ }
+ }
+ } finally {
+ if (buffer != null)
+ buffer.release();
+ CloseHandle(handle);
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ @Override
+ public List<String> list() throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+ // use stream APIs on Windwos Server 2003 and newer
+ if (file.getFileSystem().supportsStreamEnumeration()) {
+ return listUsingStreamEnumeration();
+ } else {
+ return listUsingBackupRead();
+ }
+ }
+
+ @Override
+ public int size(String name) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ // wrap with channel
+ FileChannel fc = null;
+ try {
+ Set<OpenOption> opts = new HashSet<OpenOption>();
+ opts.add(READ);
+ if (!followLinks)
+ opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
+ fc = WindowsChannelFactory
+ .newFileChannel(join(file, name), null, opts, 0L);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
+ }
+ try {
+ long size = fc.size();
+ if (size > Integer.MAX_VALUE)
+ throw new ArithmeticException("Stream too large");
+ return (int)size;
+ } finally {
+ fc.close();
+ }
+ }
+
+ @Override
+ public int read(String name, ByteBuffer dst) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), true, false);
+
+ // wrap with channel
+ FileChannel fc = null;
+ try {
+ Set<OpenOption> opts = new HashSet<OpenOption>();
+ opts.add(READ);
+ if (!followLinks)
+ opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
+ fc = WindowsChannelFactory
+ .newFileChannel(join(file, name), null, opts, 0L);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
+ }
+
+ // read to EOF (nothing we can do if I/O error occurs)
+ try {
+ if (fc.size() > dst.remaining())
+ throw new IOException("Stream too large");
+ int total = 0;
+ while (dst.hasRemaining()) {
+ int n = fc.read(dst);
+ if (n < 0)
+ break;
+ total += n;
+ }
+ return total;
+ } finally {
+ fc.close();
+ }
+ }
+
+ @Override
+ public int write(String name, ByteBuffer src) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), false, true);
+
+ /**
+ * Creating a named stream will cause the unnamed stream to be created
+ * if it doesn't already exist. To avoid this we open the unnamed stream
+ * for reading and hope it isn't deleted/moved while we create or
+ * replace the named stream. Opening the file without sharing options
+ * may cause sharing violations with other programs that are accessing
+ * the unnamed stream.
+ */
+ long handle = -1L;
+ try {
+ int flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (!followLinks)
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+ handle = CreateFile(file.getPathForWin32Calls(),
+ GENERIC_READ,
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
+ OPEN_EXISTING,
+ flags);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(file);
+ }
+ try {
+ Set<OpenOption> opts = new HashSet<OpenOption>();
+ if (!followLinks)
+ opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT);
+ opts.add(CREATE);
+ opts.add(WRITE);
+ opts.add(StandardOpenOption.TRUNCATE_EXISTING);
+ FileChannel named = null;
+ try {
+ named = WindowsChannelFactory
+ .newFileChannel(join(file, name), null, opts, 0L);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name));
+ }
+ // write value (nothing we can do if I/O error occurs)
+ try {
+ int rem = src.remaining();
+ while (src.hasRemaining()) {
+ named.write(src);
+ }
+ return rem;
+ } finally {
+ named.close();
+ }
+ } finally {
+ CloseHandle(handle);
+ }
+ }
+
+ @Override
+ public void delete(String name) throws IOException {
+ if (System.getSecurityManager() != null)
+ checkAccess(file.getPathForPermissionCheck(), false, true);
+
+ String path = WindowsLinkSupport.getFinalPath(file, followLinks);
+ String toDelete = join(path, name);
+ try {
+ DeleteFile(toDelete);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(toDelete);
+ }
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java b/src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java
new file mode 100644
index 000000000..caf36f17c
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.io.IOException;
+
+import static sun.nio.fs.WindowsConstants.*;
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+
+class WindowsUserPrincipals {
+ private WindowsUserPrincipals() { }
+
+ static class User implements UserPrincipal {
+ // String representation of SID
+ private final String sidString;
+
+ // SID type
+ private final int sidType;
+
+ // Account name (if available) or SID
+ private final String accountName;
+
+ User(String sidString, int sidType, String accountName) {
+ this.sidString = sidString;
+ this.sidType = sidType;
+ this.accountName = accountName;
+ }
+
+ // package-private
+ String sidString() {
+ return sidString;
+ }
+
+ @Override
+ public String getName() {
+ return accountName;
+ }
+
+ @Override
+ public String toString() {
+ String type;
+ switch (sidType) {
+ case SidTypeUser : type = "User"; break;
+ case SidTypeGroup : type = "Group"; break;
+ case SidTypeDomain : type = "Domain"; break;
+ case SidTypeAlias : type = "Alias"; break;
+ case SidTypeWellKnownGroup : type = "Well-known group"; break;
+ case SidTypeDeletedAccount : type = "Deleted"; break;
+ case SidTypeInvalid : type = "Invalid"; break;
+ case SidTypeComputer : type = "Computer"; break;
+ default: type = "Unknown";
+ }
+ return accountName + " (" + type + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof WindowsUserPrincipals.User))
+ return false;
+ WindowsUserPrincipals.User other = (WindowsUserPrincipals.User)obj;
+ return this.sidString.equals(other.sidString);
+ }
+
+ @Override
+ public int hashCode() {
+ return sidString.hashCode();
+ }
+ }
+
+ static class Group extends User implements GroupPrincipal {
+ Group(String sidString, int sidType, String accountName) {
+ super(sidString, sidType, accountName);
+ }
+ }
+
+ static UserPrincipal fromSid(long sidAddress) throws IOException {
+ String sidString;
+ try {
+ sidString = ConvertSidToStringSid(sidAddress);
+ if (sidString == null) {
+ // pre-Windows XP system?
+ throw new AssertionError();
+ }
+ } catch (WindowsException x) {
+ throw new IOException("Unable to convert SID to String: " +
+ x.errorString());
+ }
+
+ // lookup account; if not available then use the SID as the name
+ Account account = null;
+ String name;
+ try {
+ account = LookupAccountSid(sidAddress);
+ name = account.domain() + "\\" + account.name();
+ } catch (WindowsException x) {
+ name = sidString;
+ }
+
+ int sidType = (account == null) ? SidTypeUnknown : account.use();
+ if ((sidType == SidTypeGroup) ||
+ (sidType == SidTypeWellKnownGroup) ||
+ (sidType == SidTypeAlias)) // alias for local group
+ {
+ return new Group(sidString, sidType, name);
+ } else {
+ return new User(sidString, sidType, name);
+ }
+ }
+
+ static UserPrincipal lookup(String name) throws IOException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("lookupUserInformation"));
+ }
+
+ // invoke LookupAccountName to get buffer size needed for SID
+ int size = 0;
+ try {
+ size = LookupAccountName(name, 0L, 0);
+ } catch (WindowsException x) {
+ if (x.lastError() == ERROR_NONE_MAPPED)
+ throw new UserPrincipalNotFoundException(name);
+ throw new IOException(name + ": " + x.errorString());
+ }
+ assert size > 0;
+
+ // allocate buffer and re-invoke LookupAccountName get SID
+ NativeBuffer sidBuffer = NativeBuffers.getNativeBuffer(size);
+ try {
+ int newSize = LookupAccountName(name, sidBuffer.address(), size);
+ if (newSize != size) {
+ // can this happen?
+ throw new AssertionError("SID change during lookup");
+ }
+
+ // return user principal
+ return fromSid(sidBuffer.address());
+ } catch (WindowsException x) {
+ throw new IOException(name + ": " + x.errorString());
+ } finally {
+ sidBuffer.release();
+ }
+ }
+}
diff --git a/src/windows/classes/sun/nio/fs/WindowsWatchService.java b/src/windows/classes/sun/nio/fs/WindowsWatchService.java
new file mode 100644
index 000000000..6a5190784
--- /dev/null
+++ b/src/windows/classes/sun/nio/fs/WindowsWatchService.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.io.IOException;
+import java.util.*;
+import com.sun.nio.file.ExtendedWatchEventModifier;
+import sun.misc.Unsafe;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/*
+ * Win32 implementation of WatchService based on ReadDirectoryChangesW.
+ */
+
+class WindowsWatchService
+ extends AbstractWatchService
+{
+ private final Unsafe unsafe = Unsafe.getUnsafe();
+
+ // background thread to service I/O completion port
+ private final Poller poller;
+
+ /**
+ * Creates an I/O completion port and a daemon thread to service it
+ */
+ WindowsWatchService(WindowsFileSystem fs) throws IOException {
+ // create I/O completion port
+ long port = 0L;
+ try {
+ port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0);
+ } catch (WindowsException x) {
+ throw new IOException(x.getMessage());
+ }
+
+ this.poller = new Poller(fs, this, port);
+ this.poller.start();
+ }
+
+ @Override
+ WatchKey register(Path path,
+ WatchEvent.Kind<?>[] events,
+ WatchEvent.Modifier... modifiers)
+ throws IOException
+ {
+ // delegate to poller
+ return poller.register(path, events, modifiers);
+ }
+
+ @Override
+ void implClose() throws IOException {
+ // delegate to poller
+ poller.close();
+ }
+
+ /**
+ * Windows implementation of WatchKey.
+ */
+ private class WindowsWatchKey extends AbstractWatchKey {
+ // file key (used to detect existing registrations)
+ private FileKey fileKey;
+
+ // handle to directory
+ private volatile long handle = INVALID_HANDLE_VALUE;
+
+ // interest events
+ private Set<? extends WatchEvent.Kind<?>> events;
+
+ // subtree
+ private boolean watchSubtree;
+
+ // buffer for change events
+ private NativeBuffer buffer;
+
+ // pointer to bytes returned (in buffer)
+ private long countAddress;
+
+ // pointer to overlapped structure (in buffer)
+ private long overlappedAddress;
+
+ // completion key (used to map I/O completion to WatchKey)
+ private int completionKey;
+
+ WindowsWatchKey(AbstractWatchService watcher, FileKey fileKey) {
+ super(watcher);
+ this.fileKey = fileKey;
+ }
+
+ WindowsWatchKey init(long handle,
+ Set<? extends WatchEvent.Kind<?>> events,
+ boolean watchSubtree,
+ NativeBuffer buffer,
+ long countAddress,
+ long overlappedAddress,
+ int completionKey)
+ {
+ this.handle = handle;
+ this.events = events;
+ this.watchSubtree = watchSubtree;
+ this.buffer = buffer;
+ this.countAddress = countAddress;
+ this.overlappedAddress = overlappedAddress;
+ this.completionKey = completionKey;
+ return this;
+ }
+
+ long handle() {
+ return handle;
+ }
+
+ Set<? extends WatchEvent.Kind<?>> events() {
+ return events;
+ }
+
+ void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
+ this.events = events;
+ }
+
+ boolean watchSubtree() {
+ return watchSubtree;
+ }
+
+ NativeBuffer buffer() {
+ return buffer;
+ }
+
+ long countAddress() {
+ return countAddress;
+ }
+
+ long overlappedAddress() {
+ return overlappedAddress;
+ }
+
+ FileKey fileKey() {
+ return fileKey;
+ }
+
+ int completionKey() {
+ return completionKey;
+ }
+
+ // close directory and release buffer
+ void releaseResources() {
+ CloseHandle(handle);
+ buffer.cleaner().clean();
+ }
+
+ // Invalidate key by closing directory and releasing buffer
+ void invalidate() {
+ releaseResources();
+ handle = INVALID_HANDLE_VALUE;
+ buffer = null;
+ countAddress = 0;
+ overlappedAddress = 0;
+ }
+
+ @Override
+ public boolean isValid() {
+ return handle != INVALID_HANDLE_VALUE;
+ }
+
+ @Override
+ public void cancel() {
+ if (isValid()) {
+ // delegate to poller
+ poller.cancel(this);
+ }
+ }
+ }
+
+ // file key to unique identify (open) directory
+ private static class FileKey {
+ private final int volSerialNumber;
+ private final int fileIndexHigh;
+ private final int fileIndexLow;
+
+ FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) {
+ this.volSerialNumber = volSerialNumber;
+ this.fileIndexHigh = fileIndexHigh;
+ this.fileIndexLow = fileIndexLow;
+ }
+
+ @Override
+ public int hashCode() {
+ return volSerialNumber ^ fileIndexHigh ^ fileIndexLow;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof FileKey))
+ return false;
+ FileKey other = (FileKey)obj;
+ if (this.volSerialNumber != other.volSerialNumber) return false;
+ if (this.fileIndexHigh != other.fileIndexHigh) return false;
+ if (this.fileIndexLow != other.fileIndexLow) return false;
+ return true;
+ }
+ }
+
+ // all change events
+ private static final int ALL_FILE_NOTIFY_EVENTS =
+ FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE |
+ FILE_NOTIFY_CHANGE_LAST_WRITE |
+ FILE_NOTIFY_CHANGE_CREATION |
+ FILE_NOTIFY_CHANGE_SECURITY;
+
+ /**
+ * Background thread to service I/O completion port.
+ */
+ private class Poller extends AbstractPoller {
+ /*
+ * typedef struct _OVERLAPPED {
+ * DWORD Internal;
+ * DWORD InternalHigh;
+ * DWORD Offset;
+ * DWORD OffsetHigh;
+ * HANDLE hEvent;
+ * } OVERLAPPED;
+ */
+ private static final short SIZEOF_DWORD = 4;
+ private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit
+
+ /*
+ * typedef struct _FILE_NOTIFY_INFORMATION {
+ * DWORD NextEntryOffset;
+ * DWORD Action;
+ * DWORD FileNameLength;
+ * WCHAR FileName[1];
+ * } FileNameLength;
+ */
+ private static final short OFFSETOF_NEXTENTRYOFFSET = 0;
+ private static final short OFFSETOF_ACTION = 4;
+ private static final short OFFSETOF_FILENAMELENGTH = 8;
+ private static final short OFFSETOF_FILENAME = 12;
+
+ // size of per-directory buffer for events (FIXME - make this configurable)
+ private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
+
+ private final WindowsFileSystem fs;
+ private final WindowsWatchService watcher;
+ private final long port;
+
+ // maps completion key to WatchKey
+ private final Map<Integer,WindowsWatchKey> int2key;
+
+ // maps file key to WatchKey
+ private final Map<FileKey,WindowsWatchKey> fk2key;
+
+ // unique completion key for each directory
+ private int lastCompletionKey;
+
+ Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {
+ this.fs = fs;
+ this.watcher = watcher;
+ this.port = port;
+ this.int2key = new HashMap<Integer,WindowsWatchKey>();
+ this.fk2key = new HashMap<FileKey,WindowsWatchKey>();
+ this.lastCompletionKey = 0;
+ }
+
+ @Override
+ void wakeup() throws IOException {
+ try {
+ PostQueuedCompletionStatus(port, 0);
+ } catch (WindowsException x) {
+ throw new IOException(x.getMessage());
+ }
+ }
+
+ /**
+ * Register a directory for changes as follows:
+ *
+ * 1. Open directory
+ * 2. Read its attributes (and check it really is a directory)
+ * 3. Assign completion key and associated handle with completion port
+ * 4. Call ReadDirectoryChangesW to start (async) read of changes
+ * 5. Create or return existing key representing registration
+ */
+ @Override
+ Object implRegister(Path obj,
+ Set<? extends WatchEvent.Kind<?>> events,
+ WatchEvent.Modifier... modifiers)
+ {
+ WindowsPath dir = (WindowsPath)obj;
+ boolean watchSubtree = false;
+
+ // FILE_TREE modifier allowed
+ for (WatchEvent.Modifier modifier: modifiers) {
+ if (modifier == ExtendedWatchEventModifier.FILE_TREE) {
+ watchSubtree = true;
+ continue;
+ } else {
+ if (modifier == null)
+ return new NullPointerException();
+ if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
+ continue; // ignore
+ return new UnsupportedOperationException("Modifier not supported");
+ }
+ }
+
+ // open directory
+ long handle = -1L;
+ try {
+ handle = CreateFile(dir.getPathForWin32Calls(),
+ FILE_LIST_DIRECTORY,
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED);
+ } catch (WindowsException x) {
+ return x.asIOException(dir);
+ }
+
+ boolean registered = false;
+ try {
+ // read attributes and check file is a directory
+ WindowsFileAttributes attrs = null;
+ try {
+ attrs = WindowsFileAttributes.readAttributes(handle);
+ } catch (WindowsException x) {
+ return x.asIOException(dir);
+ }
+ if (!attrs.isDirectory()) {
+ return new NotDirectoryException(dir.getPathForExceptionMessage());
+ }
+
+ // check if this directory is already registered
+ FileKey fk = new FileKey(attrs.volSerialNumber(),
+ attrs.fileIndexHigh(),
+ attrs.fileIndexLow());
+ WindowsWatchKey existing = fk2key.get(fk);
+
+ // if already registered and we're not changing the subtree
+ // modifier then simply update the event and return the key.
+ if (existing != null && watchSubtree == existing.watchSubtree()) {
+ existing.setEvents(events);
+ return existing;
+ }
+
+ // unique completion key (skip 0)
+ int completionKey = ++lastCompletionKey;
+ if (completionKey == 0)
+ completionKey = ++lastCompletionKey;
+
+ // associate handle with completion port
+ try {
+ CreateIoCompletionPort(handle, port, completionKey);
+ } catch (WindowsException x) {
+ return new IOException(x.getMessage());
+ }
+
+ // allocate memory for events, including space for other structures
+ // needed to do overlapped I/O
+ int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED;
+ NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
+
+ long bufferAddress = buffer.address();
+ long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED;
+ long countAddress = overlappedAddress - SIZEOF_DWORD;
+
+ // start async read of changes to directory
+ try {
+ ReadDirectoryChangesW(handle,
+ bufferAddress,
+ CHANGES_BUFFER_SIZE,
+ watchSubtree,
+ ALL_FILE_NOTIFY_EVENTS,
+ countAddress,
+ overlappedAddress);
+ } catch (WindowsException x) {
+ buffer.release();
+ return new IOException(x.getMessage());
+ }
+
+ WindowsWatchKey watchKey;
+ if (existing == null) {
+ // not registered so create new watch key
+ watchKey = new WindowsWatchKey(watcher, fk)
+ .init(handle, events, watchSubtree, buffer, countAddress,
+ overlappedAddress, completionKey);
+ // map file key to watch key
+ fk2key.put(fk, watchKey);
+ } else {
+ // directory already registered so need to:
+ // 1. remove mapping from old completion key to existing watch key
+ // 2. release existing key's resources (handle/buffer)
+ // 3. re-initialize key with new handle/buffer
+ int2key.remove(existing.completionKey());
+ existing.releaseResources();
+ watchKey = existing.init(handle, events, watchSubtree, buffer,
+ countAddress, overlappedAddress, completionKey);
+ }
+ // map completion map to watch key
+ int2key.put(completionKey, watchKey);
+
+ registered = true;
+ return watchKey;
+
+ } finally {
+ if (!registered) CloseHandle(handle);
+ }
+ }
+
+ // cancel single key
+ @Override
+ void implCancelKey(WatchKey obj) {
+ WindowsWatchKey key = (WindowsWatchKey)obj;
+ if (key.isValid()) {
+ fk2key.remove(key.fileKey());
+ int2key.remove(key.completionKey());
+ key.invalidate();
+ }
+ }
+
+ // close watch service
+ @Override
+ void implCloseAll() {
+ // cancel all keys
+ for (Map.Entry<Integer,WindowsWatchKey> entry: int2key.entrySet()) {
+ entry.getValue().invalidate();
+ }
+ fk2key.clear();
+ int2key.clear();
+
+ // close I/O completion port
+ CloseHandle(port);
+ }
+
+ // Translate file change action into watch event
+ private WatchEvent.Kind<?> translateActionToEvent(int action)
+ {
+ switch (action) {
+ case FILE_ACTION_MODIFIED :
+ return StandardWatchEventKind.ENTRY_MODIFY;
+
+ case FILE_ACTION_ADDED :
+ case FILE_ACTION_RENAMED_NEW_NAME :
+ return StandardWatchEventKind.ENTRY_CREATE;
+
+ case FILE_ACTION_REMOVED :
+ case FILE_ACTION_RENAMED_OLD_NAME :
+ return StandardWatchEventKind.ENTRY_DELETE;
+
+ default :
+ return null; // action not recognized
+ }
+ }
+
+ // process events (list of FILE_NOTIFY_INFORMATION structures)
+ private void processEvents(WindowsWatchKey key, int size) {
+ long address = key.buffer().address();
+
+ int nextOffset;
+ do {
+ int action = unsafe.getInt(address + OFFSETOF_ACTION);
+
+ // map action to event
+ WatchEvent.Kind<?> kind = translateActionToEvent(action);
+ if (key.events().contains(kind)) {
+ // copy the name
+ int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FILENAMELENGTH);
+ if ((nameLengthInBytes % 2) != 0) {
+ throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2");
+ }
+ char[] nameAsArray = new char[nameLengthInBytes/2];
+ unsafe.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,
+ Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
+
+ // create FileName and queue event
+ WindowsPath name = WindowsPath
+ .createFromNormalizedPath(fs, new String(nameAsArray));
+ key.signalEvent(kind, name);
+ }
+
+ // next event
+ nextOffset = unsafe.getInt(address + OFFSETOF_NEXTENTRYOFFSET);
+ address += (long)nextOffset;
+ } while (nextOffset != 0);
+ }
+
+ /**
+ * Poller main loop
+ */
+ @Override
+ public void run() {
+ for (;;) {
+ CompletionStatus info = null;
+ try {
+ info = GetQueuedCompletionStatus(port);
+ } catch (WindowsException x) {
+ // this should not happen
+ x.printStackTrace();
+ return;
+ }
+
+ // wakeup
+ if (info.completionKey() == 0) {
+ boolean shutdown = processRequests();
+ if (shutdown) {
+ return;
+ }
+ continue;
+ }
+
+ // map completionKey to get WatchKey
+ WindowsWatchKey key = int2key.get(info.completionKey());
+ if (key == null) {
+ // We get here when a registration is changed. In that case
+ // the directory is closed which causes an event with the
+ // old completion key.
+ continue;
+ }
+
+ // ReadDirectoryChangesW failed
+ if (info.error() != 0) {
+ // buffer overflow
+ if (info.error() == ERROR_NOTIFY_ENUM_DIR) {
+ key.signalEvent(StandardWatchEventKind.OVERFLOW, null);
+ } else {
+ // other error so cancel key
+ implCancelKey(key);
+ key.signal();
+ }
+ continue;
+ }
+
+ // process the events
+ if (info.bytesTransferred() > 0) {
+ processEvents(key, info.bytesTransferred());
+ } else {
+ // insufficient buffer size
+ key.signalEvent(StandardWatchEventKind.OVERFLOW, null);
+ }
+
+ // start read for next batch of changes
+ try {
+ ReadDirectoryChangesW(key.handle(),
+ key.buffer().address(),
+ CHANGES_BUFFER_SIZE,
+ key.watchSubtree(),
+ ALL_FILE_NOTIFY_EVENTS,
+ key.countAddress(),
+ key.overlappedAddress());
+ } catch (WindowsException x) {
+ // no choice but to cancel key
+ implCancelKey(key);
+ key.signal();
+ }
+ }
+ }
+ }
+}
diff --git a/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c b/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c
index 069cf301d..c5b42b9f4 100644
--- a/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c
+++ b/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2008 Sun Microsystems, Inc. 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
@@ -23,24 +23,21 @@
* have any questions.
*/
+// copy from awt.h
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+// copy from awt.h
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0600
+#endif
+
#include "splashscreen_impl.h"
#include <windowsx.h>
#include <windows.h>
#include <winuser.h>
-/* layered windows api prototypes. wouldn't be needed if we could use an updated version of the MS PSDK. */
-
-typedef BOOL WINAPI UpdateLayeredWindowT(HWND hwnd, // handle to layered window
- HDC hdcDst, // handle to screen DC
- POINT * pptDst, // new screen position
- SIZE * psize, // new size of the layered window
- HDC hdcSrc, // handle to surface DC
- POINT * pptSrc, // layer position
- COLORREF crKey, // color key
- BLENDFUNCTION * pblend, // blend function
- DWORD dwFlags // options
- );
-
#ifndef WS_EX_LAYERED
#define WS_EX_LAYERED 0x80000
#endif
@@ -57,21 +54,6 @@ typedef BOOL WINAPI UpdateLayeredWindowT(HWND hwnd, // handle to layered win
#define AC_SRC_ALPHA 0x01
#endif
-static UpdateLayeredWindowT *UpdateLayeredWindow = NULL;
-
-/* Get/SetWindowLongPtr prototypes, for the case we're compiling with old headers for a 32-bit platform
- copied from Component.cpp
- FIXME: remove this as soon as the build process is using up-to-date headers */
-#if !defined(__int3264)
-#define GetWindowLongPtr GetWindowLong
-#define SetWindowLongPtr SetWindowLong
-#define GWLP_USERDATA GWL_USERDATA
-#define GWLP_WNDPROC GWL_WNDPROC
-typedef __int32 LONG_PTR;
-typedef unsigned __int32 ULONG_PTR;
-#endif // __int3264
-
-
#define WM_SPLASHUPDATE WM_USER+1
#define WM_SPLASHRECONFIGURE WM_USER+2
@@ -436,16 +418,11 @@ SplashUnlock(Splash * splash)
void
SplashInitPlatform(Splash * splash)
{
- HMODULE user32 = LoadLibrary("user32.dll");
HDC hdc;
int paletteMode;
InitializeCriticalSection(&splash->lock);
splash->isLayered = FALSE;
- if (user32) {
- UpdateLayeredWindow = (UpdateLayeredWindowT *)
- GetProcAddress(user32, "UpdateLayeredWindow");
- }
hdc = GetDC(NULL);
paletteMode = (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0;
if (UpdateLayeredWindow && !paletteMode) {
diff --git a/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp b/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp
index f0838dd56..bee55638d 100644
--- a/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp
+++ b/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp
@@ -184,7 +184,7 @@ void D3DPipelineManager::NotifyAdapterEventListeners(UINT adapter,
pMgr = D3DPipelineManager::GetInstance();
RETURN_IF_NULL(pMgr);
hMon = pMgr->pd3d9->GetAdapterMonitor(adapter);
- gdiScreen = AwtWin32GraphicsDevice::GetScreenFromMHND((MHND)hMon);
+ gdiScreen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hMon);
JNU_CallStaticMethodByName(env, NULL,
"sun/java2d/pipe/hw/AccelDeviceEventNotifier",
@@ -194,21 +194,21 @@ void D3DPipelineManager::NotifyAdapterEventListeners(UINT adapter,
UINT D3DPipelineManager::GetAdapterOrdinalForScreen(jint gdiScreen)
{
- MHND mHnd = AwtWin32GraphicsDevice::GetMonitor(gdiScreen);
- if (mHnd == (MHND)0) {
+ HMONITOR mHnd = AwtWin32GraphicsDevice::GetMonitor(gdiScreen);
+ if (mHnd == (HMONITOR)0) {
return D3DADAPTER_DEFAULT;
}
return GetAdapterOrdinalByHmon((HMONITOR)mHnd);
}
// static
-HRESULT D3DPipelineManager::HandleAdaptersChange(HMONITOR *pMHNDs, UINT monNum)
+HRESULT D3DPipelineManager::HandleAdaptersChange(HMONITOR *pHMONITORs, UINT monNum)
{
HRESULT res = S_OK;
BOOL bResetD3D = FALSE, bFound;
D3DPipelineManager *pMgr = D3DPipelineManager::GetInstance();
- RETURN_STATUS_IF_NULL(pMHNDs, E_FAIL);
+ RETURN_STATUS_IF_NULL(pHMONITORs, E_FAIL);
if (pMgr == NULL) {
// NULL pMgr is valid when the pipeline is not enabled or if it hasn't
// been created yet
@@ -234,7 +234,7 @@ HRESULT D3DPipelineManager::HandleAdaptersChange(HMONITOR *pMHNDs, UINT monNum)
}
bFound = FALSE;
for (UINT mon = 0; mon < monNum; mon++) {
- if (pMHNDs[mon] == hMon) {
+ if (pHMONITORs[mon] == hMon) {
J2dTraceLn3(J2D_TRACE_VERBOSE,
" adapter %d: found hmnd[%d]=0x%x", i, mon, hMon);
bFound = TRUE;
@@ -364,8 +364,8 @@ D3DPipelineManager::CheckOSVersion()
HRESULT
D3DPipelineManager::GDICheckForBadHardware()
{
- _DISPLAY_DEVICE dd;
- dd.dwSize = sizeof(DISPLAY_DEVICE);
+ DISPLAY_DEVICE dd;
+ dd.cb = sizeof(DISPLAY_DEVICE);
int failedDevices = 0;
int attachedDevices = 0;
@@ -379,9 +379,9 @@ D3DPipelineManager::GDICheckForBadHardware()
// i<20 is to guard against buggy drivers
while (EnumDisplayDevices(NULL, i, &dd, 0) && i < 20) {
- if (dd.dwFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
+ if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
attachedDevices++;
- id = dd.deviceID;
+ id = dd.DeviceID;
if (wcslen(id) > 21) {
// get vendor ID
wcsncpy(vendorId, id+8, 4);
@@ -796,7 +796,7 @@ HWND D3DPipelineManager::CreateDefaultFocusWindow()
ZeroMemory(&mi, sizeof(MONITORINFO));
mi.cbSize = sizeof(MONITORINFO);
HMONITOR hMon = pd3d9->GetAdapterMonitor(adapterOrdinal);
- if (hMon == 0 || !GetMonitorInfo(hMon, (PMONITOR_INFO)&mi)) {
+ if (hMon == 0 || !GetMonitorInfo(hMon, (LPMONITORINFO)&mi)) {
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"D3DPPLM::CreateDefaultFocusWindow: "\
"error getting monitor info for adapter=%d", adapterOrdinal);
diff --git a/src/windows/native/sun/java2d/d3d/D3DRenderQueue.cpp b/src/windows/native/sun/java2d/d3d/D3DRenderQueue.cpp
index e33f1c4e1..d9222ff8b 100644
--- a/src/windows/native/sun/java2d/d3d/D3DRenderQueue.cpp
+++ b/src/windows/native/sun/java2d/d3d/D3DRenderQueue.cpp
@@ -23,8 +23,8 @@
* have any questions.
*/
+#include "D3DPipeline.h"
#include <malloc.h>
-#include <jni.h>
#include "sun_java2d_pipe_BufferedOpCodes.h"
#include "jlong.h"
diff --git a/src/windows/native/sun/java2d/d3d/D3DRenderer.cpp b/src/windows/native/sun/java2d/d3d/D3DRenderer.cpp
index 8da2398eb..f9351da4c 100644
--- a/src/windows/native/sun/java2d/d3d/D3DRenderer.cpp
+++ b/src/windows/native/sun/java2d/d3d/D3DRenderer.cpp
@@ -23,6 +23,8 @@
* have any questions.
*/
+#include "D3DPipeline.h"
+
#include "sun_java2d_d3d_D3DRenderer.h"
#include "D3DContext.h"
diff --git a/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp b/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp
index 0cadb3bd6..9c865dc97 100644
--- a/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp
+++ b/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp
@@ -23,8 +23,7 @@
* have any questions.
*/
-#include <jni.h>
-#include <jni_util.h>
+#include "D3DPipeline.h"
#include <jlong.h>
#include "D3DSurfaceData.h"
#include "D3DPipelineManager.h"
diff --git a/src/windows/native/sun/java2d/windows/GDIBlitLoops.cpp b/src/windows/native/sun/java2d/windows/GDIBlitLoops.cpp
index 06d3c5f3f..736f39b58 100644
--- a/src/windows/native/sun/java2d/windows/GDIBlitLoops.cpp
+++ b/src/windows/native/sun/java2d/windows/GDIBlitLoops.cpp
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include <sun_java2d_windows_GDIBlitLoops.h>
#include "gdefs.h"
#include "Trace.h"
diff --git a/src/windows/native/sun/java2d/windows/GDIRenderer.cpp b/src/windows/native/sun/java2d/windows/GDIRenderer.cpp
index 5a06df00b..b2dc4a0e7 100644
--- a/src/windows/native/sun/java2d/windows/GDIRenderer.cpp
+++ b/src/windows/native/sun/java2d/windows/GDIRenderer.cpp
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include "sun_java2d_windows_GDIRenderer.h"
#include "java_awt_geom_PathIterator.h"
@@ -31,11 +32,8 @@
#include "awt_Pen.h"
#include "awt_Brush.h"
-#include "jni.h"
-
#include "GraphicsPrimitiveMgr.h"
-#include <windows.h>
#include <math.h> /* for cos(), sin(), etc */
#define MAX_CLAMP_BND (1<<26)
diff --git a/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp b/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp
index b7032df7b..9acabfc66 100644
--- a/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp
+++ b/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp
@@ -246,7 +246,7 @@ static BOOL GDIWinSD_CheckMonitorArea(GDIWinSDOps *wsdo,
}
if( numScreens > 1 ) {
- MONITOR_INFO *miInfo;
+ LPMONITORINFO miInfo;
RECT rSect ={0,0,0,0};
RECT rView ={bounds->x1, bounds->y1, bounds->x2, bounds->y2};
retCode = FALSE;
@@ -258,7 +258,7 @@ static BOOL GDIWinSD_CheckMonitorArea(GDIWinSDOps *wsdo,
::OffsetRect(&rView,
(ptOrig.x), (ptOrig.y));
- ::IntersectRect(&rSect,&rView,&(miInfo->rMonitor));
+ ::IntersectRect(&rSect,&rView,&(miInfo->rcMonitor));
if( FALSE == ::IsRectEmpty(&rSect) ) {
if( TRUE == ::EqualRect(&rSect,&rView) ) {
diff --git a/src/windows/native/sun/java2d/windows/WindowsFlags.cpp b/src/windows/native/sun/java2d/windows/WindowsFlags.cpp
index b7040b8d8..01c0f6d8f 100644
--- a/src/windows/native/sun/java2d/windows/WindowsFlags.cpp
+++ b/src/windows/native/sun/java2d/windows/WindowsFlags.cpp
@@ -23,8 +23,6 @@
* have any questions.
*/
-
-#include <jni.h>
#include <awt.h>
#include "Trace.h"
#include "WindowsFlags.h"
diff --git a/src/windows/native/sun/nio/ch/FileChannelImpl.c b/src/windows/native/sun/nio/ch/FileChannelImpl.c
index da96a109a..d1056b883 100644
--- a/src/windows/native/sun/nio/ch/FileChannelImpl.c
+++ b/src/windows/native/sun/nio/ch/FileChannelImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -34,10 +34,6 @@
static jfieldID chan_fd; /* id for jobject 'fd' in java.io.FileChannel */
-
-/* false for 95/98/ME, true for NT/W2K */
-static jboolean onNT = JNI_FALSE;
-
/**************************************************************
* static method to store field ID's in initializers
* and retrieve the allocation granularity
@@ -47,15 +43,9 @@ Java_sun_nio_ch_FileChannelImpl_initIDs(JNIEnv *env, jclass clazz)
{
SYSTEM_INFO si;
jint align;
- OSVERSIONINFO ver;
GetSystemInfo(&si);
align = si.dwAllocationGranularity;
chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;");
- ver.dwOSVersionInfoSize = sizeof(ver);
- GetVersionEx(&ver);
- if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
- onNT = JNI_TRUE;
- }
return align;
}
@@ -146,56 +136,6 @@ Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this,
return 0;
}
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileChannelImpl_truncate0(JNIEnv *env, jobject this,
- jobject fdo, jlong size)
-{
- DWORD lowPos = 0;
- long highPos = 0;
- BOOL result = 0;
- HANDLE h = (HANDLE)(handleval(env, fdo));
-
- lowPos = (DWORD)size;
- highPos = (long)(size >> 32);
- lowPos = SetFilePointer(h, lowPos, &highPos, FILE_BEGIN);
- if (lowPos == ((DWORD)-1)) {
- if (GetLastError() != ERROR_SUCCESS) {
- JNU_ThrowIOExceptionWithLastError(env, "Truncation failed");
- return IOS_THROWN;
- }
- }
- result = SetEndOfFile(h);
- if (result == 0) {
- JNU_ThrowIOExceptionWithLastError(env, "Truncation failed");
- return IOS_THROWN;
- }
- return 0;
-}
-
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileChannelImpl_force0(JNIEnv *env, jobject this,
- jobject fdo, jboolean md)
-{
- int result = 0;
- HANDLE h = (HANDLE)(handleval(env, fdo));
-
- if (h != INVALID_HANDLE_VALUE) {
- result = FlushFileBuffers(h);
- if (result == 0) {
- int error = GetLastError();
- if (error != ERROR_ACCESS_DENIED) {
- JNU_ThrowIOExceptionWithLastError(env, "Force failed");
- return IOS_THROWN;
- }
- }
- } else {
- JNU_ThrowIOExceptionWithLastError(env, "Force failed");
- return IOS_THROWN;
- }
- return 0;
-}
-
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this,
jobject fdo, jlong offset)
@@ -220,23 +160,6 @@ Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this,
return (((jlong)highPos) << 32) | lowPos;
}
-JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileChannelImpl_size0(JNIEnv *env, jobject this, jobject fdo)
-{
- DWORD sizeLow = 0;
- DWORD sizeHigh = 0;
- HANDLE h = (HANDLE)(handleval(env, fdo));
-
- sizeLow = GetFileSize(h, &sizeHigh);
- if (sizeLow == ((DWORD)-1)) {
- if (GetLastError() != ERROR_SUCCESS) {
- JNU_ThrowIOExceptionWithLastError(env, "Size failed");
- return IOS_THROWN;
- }
- }
- return (((jlong)sizeHigh) << 32) | sizeLow;
-}
-
JNIEXPORT void JNICALL
Java_sun_nio_ch_FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo)
{
@@ -257,99 +180,3 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
{
return IOS_UNSUPPORTED;
}
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileChannelImpl_lock0(JNIEnv *env, jobject this, jobject fdo,
- jboolean block, jlong pos, jlong size,
- jboolean shared)
-{
- HANDLE h = (HANDLE)(handleval(env, fdo));
- DWORD lowPos = (DWORD)pos;
- long highPos = (long)(pos >> 32);
- DWORD lowNumBytes = (DWORD)size;
- DWORD highNumBytes = (DWORD)(size >> 32);
- jint result = 0;
- if (onNT) {
- DWORD flags = 0;
- OVERLAPPED o;
- o.hEvent = 0;
- o.Offset = lowPos;
- o.OffsetHigh = highPos;
- if (block == JNI_FALSE) {
- flags |= LOCKFILE_FAIL_IMMEDIATELY;
- }
- if (shared == JNI_FALSE) {
- flags |= LOCKFILE_EXCLUSIVE_LOCK;
- }
- result = LockFileEx(h, flags, 0, lowNumBytes, highNumBytes, &o);
- if (result == 0) {
- int error = GetLastError();
- if (error != ERROR_LOCK_VIOLATION) {
- JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
- }
- if (flags & LOCKFILE_FAIL_IMMEDIATELY) {
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
- }
- JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
- }
- return sun_nio_ch_FileChannelImpl_LOCKED;
- } else {
- for(;;) {
- if (size > 0x7fffffff) {
- size = 0x7fffffff;
- }
- lowNumBytes = (DWORD)size;
- highNumBytes = 0;
- result = LockFile(h, lowPos, highPos, lowNumBytes, highNumBytes);
- if (result != 0) {
- if (shared == JNI_TRUE) {
- return sun_nio_ch_FileChannelImpl_RET_EX_LOCK;
- } else {
- return sun_nio_ch_FileChannelImpl_LOCKED;
- }
- } else {
- int error = GetLastError();
- if (error != ERROR_LOCK_VIOLATION) {
- JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
- }
- if (block == JNI_FALSE) {
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
- }
- }
- Sleep(100);
- }
- }
- return sun_nio_ch_FileChannelImpl_NO_LOCK;
-}
-
-JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileChannelImpl_release0(JNIEnv *env, jobject this,
- jobject fdo, jlong pos, jlong size)
-{
- HANDLE h = (HANDLE)(handleval(env, fdo));
- DWORD lowPos = (DWORD)pos;
- long highPos = (long)(pos >> 32);
- DWORD lowNumBytes = (DWORD)size;
- DWORD highNumBytes = (DWORD)(size >> 32);
- jint result = 0;
- if (onNT) {
- OVERLAPPED o;
- o.hEvent = 0;
- o.Offset = lowPos;
- o.OffsetHigh = highPos;
- result = UnlockFileEx(h, 0, lowNumBytes, highNumBytes, &o);
- } else {
- if (size > 0x7fffffff) {
- size = 0x7fffffff;
- }
- lowNumBytes = (DWORD)size;
- highNumBytes = 0;
- result = UnlockFile(h, lowPos, highPos, lowNumBytes, highNumBytes);
- }
- if (result == 0) {
- JNU_ThrowIOExceptionWithLastError(env, "Release failed");
- }
-}
diff --git a/src/windows/native/sun/nio/ch/FileDispatcher.c b/src/windows/native/sun/nio/ch/FileDispatcherImpl.c
index a3c3985e7..a65ad90e2 100644
--- a/src/windows/native/sun/nio/ch/FileDispatcher.c
+++ b/src/windows/native/sun/nio/ch/FileDispatcherImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. 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
@@ -28,18 +28,18 @@
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
-#include "sun_nio_ch_FileDispatcher.h"
+#include "sun_nio_ch_FileDispatcherImpl.h"
#include <io.h>
#include "nio.h"
#include "nio_util.h"
/**************************************************************
- * FileDispatcher.c
+ * FileDispatcherImpl.c
*/
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_read0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len)
{
DWORD read = 0;
@@ -70,7 +70,7 @@ Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_readv0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len)
{
DWORD read = 0;
@@ -119,7 +119,7 @@ Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len, jlong offset)
{
DWORD read = 0;
@@ -182,7 +182,7 @@ Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len)
{
BOOL result = 0;
@@ -205,7 +205,7 @@ Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len)
{
BOOL result = 0;
@@ -244,7 +244,7 @@ Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz, jobject fdo,
}
JNIEXPORT jint JNICALL
-Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo,
+Java_sun_nio_ch_FileDispatcherImpl_pwrite0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len, jlong offset)
{
BOOL result = 0;
@@ -295,6 +295,130 @@ Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo,
return convertReturnVal(env, (jint)written, JNI_FALSE);
}
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_force0(JNIEnv *env, jobject this,
+ jobject fdo, jboolean md)
+{
+ int result = 0;
+ HANDLE h = (HANDLE)(handleval(env, fdo));
+
+ if (h != INVALID_HANDLE_VALUE) {
+ result = FlushFileBuffers(h);
+ if (result == 0) {
+ int error = GetLastError();
+ if (error != ERROR_ACCESS_DENIED) {
+ JNU_ThrowIOExceptionWithLastError(env, "Force failed");
+ return IOS_THROWN;
+ }
+ }
+ } else {
+ JNU_ThrowIOExceptionWithLastError(env, "Force failed");
+ return IOS_THROWN;
+ }
+ return 0;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_truncate0(JNIEnv *env, jobject this,
+ jobject fdo, jlong size)
+{
+ DWORD lowPos = 0;
+ long highPos = 0;
+ BOOL result = 0;
+ HANDLE h = (HANDLE)(handleval(env, fdo));
+
+ lowPos = (DWORD)size;
+ highPos = (long)(size >> 32);
+ lowPos = SetFilePointer(h, lowPos, &highPos, FILE_BEGIN);
+ if (lowPos == ((DWORD)-1)) {
+ if (GetLastError() != ERROR_SUCCESS) {
+ JNU_ThrowIOExceptionWithLastError(env, "Truncation failed");
+ return IOS_THROWN;
+ }
+ }
+ result = SetEndOfFile(h);
+ if (result == 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "Truncation failed");
+ return IOS_THROWN;
+ }
+ return 0;
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_size0(JNIEnv *env, jobject this, jobject fdo)
+{
+ DWORD sizeLow = 0;
+ DWORD sizeHigh = 0;
+ HANDLE h = (HANDLE)(handleval(env, fdo));
+
+ sizeLow = GetFileSize(h, &sizeHigh);
+ if (sizeLow == ((DWORD)-1)) {
+ if (GetLastError() != ERROR_SUCCESS) {
+ JNU_ThrowIOExceptionWithLastError(env, "Size failed");
+ return IOS_THROWN;
+ }
+ }
+ return (((jlong)sizeHigh) << 32) | sizeLow;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo,
+ jboolean block, jlong pos, jlong size,
+ jboolean shared)
+{
+ HANDLE h = (HANDLE)(handleval(env, fdo));
+ DWORD lowPos = (DWORD)pos;
+ long highPos = (long)(pos >> 32);
+ DWORD lowNumBytes = (DWORD)size;
+ DWORD highNumBytes = (DWORD)(size >> 32);
+ BOOL result;
+ DWORD flags = 0;
+ OVERLAPPED o;
+ o.hEvent = 0;
+ o.Offset = lowPos;
+ o.OffsetHigh = highPos;
+ if (block == JNI_FALSE) {
+ flags |= LOCKFILE_FAIL_IMMEDIATELY;
+ }
+ if (shared == JNI_FALSE) {
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ }
+ result = LockFileEx(h, flags, 0, lowNumBytes, highNumBytes, &o);
+ if (result == 0) {
+ int error = GetLastError();
+ if (error != ERROR_LOCK_VIOLATION) {
+ JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
+ return sun_nio_ch_FileDispatcherImpl_NO_LOCK;
+ }
+ if (flags & LOCKFILE_FAIL_IMMEDIATELY) {
+ return sun_nio_ch_FileDispatcherImpl_NO_LOCK;
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "Lock failed");
+ return sun_nio_ch_FileDispatcherImpl_NO_LOCK;
+ }
+ return sun_nio_ch_FileDispatcherImpl_LOCKED;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_FileDispatcherImpl_release0(JNIEnv *env, jobject this,
+ jobject fdo, jlong pos, jlong size)
+{
+ HANDLE h = (HANDLE)(handleval(env, fdo));
+ DWORD lowPos = (DWORD)pos;
+ long highPos = (long)(pos >> 32);
+ DWORD lowNumBytes = (DWORD)size;
+ DWORD highNumBytes = (DWORD)(size >> 32);
+ jint result = 0;
+ OVERLAPPED o;
+ o.hEvent = 0;
+ o.Offset = lowPos;
+ o.OffsetHigh = highPos;
+ result = UnlockFileEx(h, 0, lowNumBytes, highNumBytes, &o);
+ if (result == 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "Release failed");
+ }
+}
+
static void closeFile(JNIEnv *env, jlong fd) {
HANDLE h = (HANDLE)fd;
if (h != INVALID_HANDLE_VALUE) {
@@ -305,14 +429,14 @@ static void closeFile(JNIEnv *env, jlong fd) {
}
JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileDispatcher_close0(JNIEnv *env, jclass clazz, jobject fdo)
+Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo)
{
jlong fd = handleval(env, fdo);
closeFile(env, fd);
}
JNIEXPORT void JNICALL
-Java_sun_nio_ch_FileDispatcher_closeByHandle(JNIEnv *env, jclass clazz,
+Java_sun_nio_ch_FileDispatcherImpl_closeByHandle(JNIEnv *env, jclass clazz,
jlong fd)
{
closeFile(env, fd);
diff --git a/src/windows/native/sun/nio/ch/Iocp.c b/src/windows/native/sun/nio/ch/Iocp.c
new file mode 100644
index 000000000..9568189ee
--- /dev/null
+++ b/src/windows/native/sun/nio/ch/Iocp.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+#include "nio.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_Iocp.h"
+
+
+static jfieldID completionStatus_error;
+static jfieldID completionStatus_bytesTransferred;
+static jfieldID completionStatus_completionKey;
+static jfieldID completionStatus_overlapped;
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_Iocp_initIDs(JNIEnv* env, jclass this)
+{
+ jclass clazz;
+
+ clazz = (*env)->FindClass(env, "sun/nio/ch/Iocp$CompletionStatus");
+ if (clazz == NULL) {
+ return;
+ }
+ completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I");
+ if (completionStatus_error == NULL) return;
+ completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I");
+ if (completionStatus_bytesTransferred == NULL) return;
+ completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "I");
+ if (completionStatus_completionKey == NULL) return;
+ completionStatus_overlapped = (*env)->GetFieldID(env, clazz, "overlapped", "J");
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_ch_Iocp_createIoCompletionPort(JNIEnv* env, jclass this,
+ jlong handle, jlong existingPort, jint completionKey, jint concurrency)
+{
+ HANDLE port = CreateIoCompletionPort((HANDLE)jlong_to_ptr(handle),
+ (HANDLE)jlong_to_ptr(existingPort),
+ (DWORD)completionKey,
+ (DWORD)concurrency);
+ if (port == NULL) {
+ JNU_ThrowIOExceptionWithLastError(env, "CreateIoCompletionPort failed");
+ }
+ return ptr_to_jlong(port);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_Iocp_close0(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ CloseHandle(h);
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_Iocp_getQueuedCompletionStatus(JNIEnv* env, jclass this,
+ jlong completionPort, jobject obj)
+{
+ DWORD bytesTransferred;
+ DWORD completionKey;
+ OVERLAPPED *lpOverlapped;
+ BOOL res;
+
+ res = GetQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
+ &bytesTransferred,
+ &completionKey,
+ &lpOverlapped,
+ INFINITE);
+ if (res == 0 && lpOverlapped == NULL) {
+ JNU_ThrowIOExceptionWithLastError(env, "GetQueuedCompletionStatus failed");
+ } else {
+ DWORD ioResult = (res == 0) ? GetLastError() : 0;
+ (*env)->SetIntField(env, obj, completionStatus_error, ioResult);
+ (*env)->SetIntField(env, obj, completionStatus_bytesTransferred,
+ (jint)bytesTransferred);
+ (*env)->SetIntField(env, obj, completionStatus_completionKey,
+ (jint)completionKey);
+ (*env)->SetLongField(env, obj, completionStatus_overlapped,
+ ptr_to_jlong(lpOverlapped));
+
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_Iocp_postQueuedCompletionStatus(JNIEnv* env, jclass this,
+ jlong completionPort, jint completionKey)
+{
+ BOOL res;
+
+ res = PostQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
+ (DWORD)0,
+ (DWORD)completionKey,
+ NULL);
+ if (res == 0) {
+ JNU_ThrowIOExceptionWithLastError(env, "PostQueuedCompletionStatus");
+ }
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_ch_Iocp_getErrorMessage(JNIEnv* env, jclass this, jint errorCode)
+{
+ WCHAR message[255];
+
+ DWORD len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ (DWORD)errorCode,
+ 0,
+ &message[0],
+ 255,
+ NULL);
+
+
+ if (len == 0) {
+ return NULL;
+ } else {
+ return (*env)->NewString(env, (const jchar *)message, (jsize)wcslen(message));
+ }
+}
diff --git a/src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c b/src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c
new file mode 100644
index 000000000..d8346ba3e
--- /dev/null
+++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+#include "nio.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_WindowsAsynchronousFileChannelImpl.h"
+
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_readFile(JNIEnv* env, jclass this,
+ jlong handle, jlong address, jint len, jlong offset, jlong ov)
+{
+ BOOL res;
+ DWORD nread = 0;
+
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov);
+ lpOverlapped->Offset = (DWORD)offset;
+ lpOverlapped->OffsetHigh = (DWORD)((long)(offset >> 32));
+ lpOverlapped->hEvent = NULL;
+
+ res = ReadFile((HANDLE) jlong_to_ptr(handle),
+ (LPVOID) jlong_to_ptr(address),
+ (DWORD)len,
+ &nread,
+ lpOverlapped);
+
+ if (res == 0) {
+ int error = GetLastError();
+ if (error == ERROR_IO_PENDING)
+ return IOS_UNAVAILABLE;
+ if (error == ERROR_HANDLE_EOF)
+ return IOS_EOF;
+ JNU_ThrowIOExceptionWithLastError(env, "ReadFile failed");
+ return IOS_THROWN;
+ }
+
+ return (jint)nread;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_writeFile(JNIEnv* env, jclass this,
+ jlong handle, jlong address, jint len, jlong offset, jlong ov)
+{
+ BOOL res;
+ DWORD nwritten = 0;
+
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov);
+ lpOverlapped->Offset = (DWORD)offset;
+ lpOverlapped->OffsetHigh = (DWORD)((long)(offset >> 32));
+ lpOverlapped->hEvent = NULL;
+
+ res = WriteFile((HANDLE)jlong_to_ptr(handle),
+ (LPVOID) jlong_to_ptr(address),
+ (DWORD)len,
+ &nwritten,
+ lpOverlapped);
+
+ if (res == 0) {
+ int error = GetLastError();
+ if (error == ERROR_IO_PENDING) {
+ return IOS_UNAVAILABLE;
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "WriteFile failed");
+ return IOS_THROWN;
+ }
+ return (jint)nwritten;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_lockFile(JNIEnv *env, jobject this, jlong handle,
+ jlong pos, jlong size, jboolean shared, jlong ov)
+{
+ BOOL res;
+ HANDLE h = jlong_to_ptr(handle);
+ DWORD lowPos = (DWORD)pos;
+ long highPos = (long)(pos >> 32);
+ DWORD lowNumBytes = (DWORD)size;
+ DWORD highNumBytes = (DWORD)(size >> 32);
+ DWORD flags = (shared == JNI_TRUE) ? 0 : LOCKFILE_EXCLUSIVE_LOCK;
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov);
+
+ lpOverlapped->Offset = lowPos;
+ lpOverlapped->OffsetHigh = highPos;
+ lpOverlapped->hEvent = NULL;
+
+ res = LockFileEx(h, flags, 0, lowNumBytes, highNumBytes, lpOverlapped);
+ if (res == 0) {
+ int error = GetLastError();
+ if (error == ERROR_IO_PENDING) {
+ return IOS_UNAVAILABLE;
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "WriteFile failed");
+ return IOS_THROWN;
+ }
+ return 0;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_close0(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ CloseHandle(h);
+}
diff --git a/src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c b/src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c
new file mode 100644
index 000000000..ba706a4d8
--- /dev/null
+++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+#include <winsock2.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+#include "nio.h"
+#include "nio_util.h"
+#include "net_util.h"
+
+#include "sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl.h"
+
+
+#ifndef WSAID_ACCEPTEX
+#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
+#endif
+
+#ifndef SO_UPDATE_ACCEPT_CONTEXT
+#define SO_UPDATE_ACCEPT_CONTEXT 0x700B
+#endif
+
+
+typedef BOOL (*AcceptEx_t)
+(
+ SOCKET sListenSocket,
+ SOCKET sAcceptSocket,
+ PVOID lpOutputBuffer,
+ DWORD dwReceiveDataLength,
+ DWORD dwLocalAddressLength,
+ DWORD dwRemoteAddressLength,
+ LPDWORD lpdwBytesReceived,
+ LPOVERLAPPED lpOverlapped
+);
+
+
+static AcceptEx_t AcceptEx_func;
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_initIDs(JNIEnv* env, jclass this) {
+ GUID GuidAcceptEx = WSAID_ACCEPTEX;
+ SOCKET s;
+ int rv;
+ DWORD dwBytes;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s == INVALID_SOCKET) {
+ JNU_ThrowIOExceptionWithLastError(env, "socket failed");
+ return;
+ }
+ rv = WSAIoctl(s,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ (LPVOID)&GuidAcceptEx,
+ sizeof(GuidAcceptEx),
+ &AcceptEx_func,
+ sizeof(AcceptEx_func),
+ &dwBytes,
+ NULL,
+ NULL);
+ if (rv != 0)
+ JNU_ThrowIOExceptionWithLastError(env, "WSAIoctl failed");
+ closesocket(s);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_accept0(JNIEnv* env, jclass this,
+ jlong listenSocket, jlong acceptSocket, jlong ov, jlong buf)
+{
+ BOOL res;
+ SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket);
+ SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket);
+ PVOID outputBuffer = (PVOID)jlong_to_ptr(buf);
+
+ DWORD nread = 0;
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov);
+ ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));
+
+ res = (*AcceptEx_func)(s1,
+ s2,
+ outputBuffer,
+ 0,
+ sizeof(SOCKETADDRESS)+16,
+ sizeof(SOCKETADDRESS)+16,
+ &nread,
+ lpOverlapped);
+ if (res == 0) {
+ int error = WSAGetLastError();
+ if (error == ERROR_IO_PENDING) {
+ return IOS_UNAVAILABLE;
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "AcceptEx failed");
+ return IOS_THROWN;
+ }
+
+ return 0;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_updateAcceptContext(JNIEnv* env, jclass this,
+ jlong listenSocket, jlong acceptSocket)
+{
+ SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket);
+ SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket);
+
+ setsockopt(s2, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&s1, sizeof(s1));
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_closesocket0(JNIEnv* env, jclass this,
+ jlong socket)
+{
+ SOCKET s = (SOCKET)jlong_to_ptr(socket);
+
+ if (closesocket(s) == SOCKET_ERROR)
+ JNU_ThrowIOExceptionWithLastError(env, "closesocket failed");
+}
diff --git a/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c b/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c
new file mode 100644
index 000000000..97c49f60a
--- /dev/null
+++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+#include <winsock2.h>
+#include <stddef.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+#include "nio.h"
+#include "nio_util.h"
+#include "net_util.h"
+
+#include "sun_nio_ch_WindowsAsynchronousSocketChannelImpl.h"
+
+#ifndef WSAID_CONNECTEX
+#define WSAID_CONNECTEX {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
+#endif
+
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+typedef BOOL (*ConnectEx_t)
+(
+ SOCKET s,
+ const struct sockaddr* name,
+ int namelen,
+ PVOID lpSendBuffer,
+ DWORD dwSendDataLength,
+ LPDWORD lpdwBytesSent,
+ LPOVERLAPPED lpOverlapped
+);
+
+static ConnectEx_t ConnectEx_func;
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_initIDs(JNIEnv* env, jclass this) {
+ GUID GuidConnectEx = WSAID_CONNECTEX;
+ SOCKET s;
+ int rv;
+ DWORD dwBytes;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s == INVALID_SOCKET) {
+ JNU_ThrowIOExceptionWithLastError(env, "socket failed");
+ return;
+ }
+ rv = WSAIoctl(s,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ (LPVOID)&GuidConnectEx,
+ sizeof(GuidConnectEx),
+ &ConnectEx_func,
+ sizeof(ConnectEx_func),
+ &dwBytes,
+ NULL,
+ NULL);
+ if (rv != 0)
+ JNU_ThrowIOExceptionWithLastError(env, "WSAIoctl failed");
+ closesocket(s);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_connect0(JNIEnv* env, jclass this,
+ jlong socket, jboolean preferIPv6, jobject iao, jint port, jlong ov)
+{
+ SOCKET s = (SOCKET) jlong_to_ptr(socket);
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov);
+
+ SOCKETADDRESS sa;
+ int sa_len;
+ BOOL res;
+
+ if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) {
+ return IOS_THROWN;
+ }
+
+ ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));
+
+ res = (*ConnectEx_func)(s,
+ (struct sockaddr *)&sa,
+ sa_len,
+ NULL,
+ 0,
+ NULL,
+ lpOverlapped);
+ if (res == 0) {
+ int error = GetLastError();
+ if (error == ERROR_IO_PENDING) {
+ return IOS_UNAVAILABLE;
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "ConnectEx failed");
+ return IOS_THROWN;
+ }
+ return 0;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_updateConnectContext(JNIEnv* env, jclass this,
+ jlong socket)
+{
+ SOCKET s = (SOCKET)jlong_to_ptr(socket);
+ setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_shutdown0(JNIEnv *env, jclass cl,
+ jlong socket, jint how)
+{
+ SOCKET s =(SOCKET) jlong_to_ptr(socket);
+ if (shutdown(s, how) == SOCKET_ERROR) {
+ JNU_ThrowIOExceptionWithLastError(env, "shutdown failed");
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_closesocket0(JNIEnv* env, jclass this,
+ jlong socket)
+{
+ SOCKET s = (SOCKET)jlong_to_ptr(socket);
+ if (closesocket(s) == SOCKET_ERROR)
+ JNU_ThrowIOExceptionWithLastError(env, "closesocket failed");
+}
+
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_read0(JNIEnv* env, jclass this,
+ jlong socket, jint count, jlong address, jlong ov)
+{
+ SOCKET s = (SOCKET) jlong_to_ptr(socket);
+ WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address);
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov);
+ BOOL res;
+ DWORD nread = 0;
+ DWORD flags = 0;
+
+ ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));
+ res = WSARecv(s,
+ lpWsaBuf,
+ (DWORD)count,
+ &nread,
+ &flags,
+ lpOverlapped,
+ NULL);
+
+ if (res == SOCKET_ERROR) {
+ int error = WSAGetLastError();
+ if (error == WSA_IO_PENDING) {
+ return IOS_UNAVAILABLE;
+ }
+ if (error == WSAESHUTDOWN) {
+ return 0; // input shutdown
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed");
+ return IOS_THROWN;
+ }
+ if (nread == 0) {
+ // Handle graceful close or bytes not yet available cases
+ // via completion port notification.
+ return IOS_UNAVAILABLE;
+ }
+ return (jint)nread;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_write0(JNIEnv* env, jclass this,
+ jlong socket, jint count, jlong address, jlong ov)
+{
+ SOCKET s = (SOCKET) jlong_to_ptr(socket);
+ WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address);
+ OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov);
+ BOOL res;
+ DWORD nwritten;
+
+ ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));
+ res = WSASend(s,
+ lpWsaBuf,
+ (DWORD)count,
+ &nwritten,
+ 0,
+ lpOverlapped,
+ NULL);
+
+ if (res == SOCKET_ERROR) {
+ int error = WSAGetLastError();
+ if (error == WSA_IO_PENDING) {
+ return IOS_UNAVAILABLE;
+ }
+ if (error == WSAESHUTDOWN) {
+ return IOS_EOF; // output shutdown
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
+ return IOS_THROWN;
+ }
+ return (jint)nwritten;
+}
diff --git a/src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c b/src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c
new file mode 100644
index 000000000..14c7f6af9
--- /dev/null
+++ b/src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+
+#include "sun_nio_fs_RegistryFileTypeDetector.h"
+
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_RegistryFileTypeDetector_queryStringValue(JNIEnv* env, jclass this,
+ jlong keyAddress, jlong nameAddress)
+{
+ LPCWSTR lpSubKey= (LPCWSTR)jlong_to_ptr(keyAddress);
+ LPWSTR lpValueName = (LPWSTR)jlong_to_ptr(nameAddress);
+ LONG res;
+ HKEY hKey;
+ jstring result = NULL;
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, lpSubKey, 0, KEY_READ, &hKey);
+ if (res == ERROR_SUCCESS) {
+ DWORD type;
+ BYTE data[255];
+ DWORD size = sizeof(data);
+
+ res = RegQueryValueExW(hKey, lpValueName, NULL, &type, (LPBYTE)&data, &size);
+ if (res == ERROR_SUCCESS) {
+ if (type == REG_SZ) {
+ jsize len = wcslen((WCHAR*)data);
+ result = (*env)->NewString(env, (const jchar*)&data, len);
+ }
+ }
+
+ RegCloseKey(hKey);
+ }
+ return result;
+}
diff --git a/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c b/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c
new file mode 100644
index 000000000..45a364627
--- /dev/null
+++ b/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c
@@ -0,0 +1,1345 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <direct.h>
+#include <malloc.h>
+#include <io.h>
+#include <windows.h>
+#include <aclapi.h>
+#include <winioctl.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+
+#include "sun_nio_fs_WindowsNativeDispatcher.h"
+
+/**
+ * jfieldIDs
+ */
+static jfieldID findFirst_handle;
+static jfieldID findFirst_name;
+
+static jfieldID findStream_handle;
+static jfieldID findStream_name;
+
+static jfieldID volumeInfo_fsName;
+static jfieldID volumeInfo_volName;
+static jfieldID volumeInfo_volSN;
+static jfieldID volumeInfo_flags;
+
+static jfieldID diskSpace_bytesAvailable;
+static jfieldID diskSpace_totalBytes;
+static jfieldID diskSpace_totalFree;
+
+static jfieldID account_domain;
+static jfieldID account_name;
+static jfieldID account_use;
+
+static jfieldID aclInfo_aceCount;
+
+static jfieldID completionStatus_error;
+static jfieldID completionStatus_bytesTransferred;
+static jfieldID completionStatus_completionKey;
+
+static jfieldID backupResult_bytesTransferred;
+static jfieldID backupResult_context;
+
+
+/**
+ * Win32 APIs not defined in Visual Studio 2003 header files
+ */
+
+typedef enum {
+ FindStreamInfoStandard
+} MY_STREAM_INFO_LEVELS;
+
+typedef struct _MY_WIN32_FIND_STREAM_DATA {
+ LARGE_INTEGER StreamSize;
+ WCHAR cStreamName[MAX_PATH + 36];
+} MY_WIN32_FIND_STREAM_DATA;
+
+typedef HANDLE (WINAPI* FindFirstStream_Proc)(LPCWSTR, MY_STREAM_INFO_LEVELS, LPVOID, DWORD);
+typedef BOOL (WINAPI* FindNextStream_Proc)(HANDLE, LPVOID);
+
+typedef BOOLEAN (WINAPI* CreateSymbolicLinkProc) (LPCWSTR, LPCWSTR, DWORD);
+typedef BOOL (WINAPI* CreateHardLinkProc) (LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
+typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD);
+
+typedef BOOL (WINAPI* ConvertSidToStringSidProc) (PSID, LPWSTR*);
+typedef BOOL (WINAPI* ConvertStringSidToSidProc) (LPWSTR, PSID*);
+typedef DWORD (WINAPI* GetLengthSidProc) (PSID);
+
+static FindFirstStream_Proc FindFirstStream_func;
+static FindNextStream_Proc FindNextStream_func;
+
+static CreateSymbolicLinkProc CreateSymbolicLink_func;
+static CreateHardLinkProc CreateHardLink_func;
+static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func;
+
+static ConvertSidToStringSidProc ConvertSidToStringSid_func;
+static ConvertStringSidToSidProc ConvertStringSidToSid_func;
+static GetLengthSidProc GetLengthSid_func;
+
+static void throwWindowsException(JNIEnv* env, DWORD lastError) {
+ jobject x = JNU_NewObjectByName(env, "sun/nio/fs/WindowsException",
+ "(I)V", lastError);
+ if (x != NULL) {
+ (*env)->Throw(env, x);
+ }
+}
+
+/**
+ * Initializes jfieldIDs and get address of Win32 calls that are located
+ * at runtime.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
+{
+ jclass clazz;
+ HMODULE h;
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstFile");
+ if (clazz == NULL) {
+ return;
+ }
+ findFirst_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
+ findFirst_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstStream");
+ if (clazz == NULL) {
+ return;
+ }
+ findStream_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
+ findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation");
+ if (clazz == NULL) {
+ return;
+ }
+ volumeInfo_fsName = (*env)->GetFieldID(env, clazz, "fileSystemName", "Ljava/lang/String;");
+ volumeInfo_volName = (*env)->GetFieldID(env, clazz, "volumeName", "Ljava/lang/String;");
+ volumeInfo_volSN = (*env)->GetFieldID(env, clazz, "volumeSerialNumber", "I");
+ volumeInfo_flags = (*env)->GetFieldID(env, clazz, "flags", "I");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$DiskFreeSpace");
+ if (clazz == NULL) {
+ return;
+ }
+ diskSpace_bytesAvailable = (*env)->GetFieldID(env, clazz, "freeBytesAvailable", "J");
+ diskSpace_totalBytes = (*env)->GetFieldID(env, clazz, "totalNumberOfBytes", "J");
+ diskSpace_totalFree = (*env)->GetFieldID(env, clazz, "totalNumberOfFreeBytes", "J");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$Account");
+ if (clazz == NULL) {
+ return;
+ }
+ account_domain = (*env)->GetFieldID(env, clazz, "domain", "Ljava/lang/String;");
+ account_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
+ account_use = (*env)->GetFieldID(env, clazz, "use", "I");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$AclInformation");
+ if (clazz == NULL) {
+ return;
+ }
+ aclInfo_aceCount = (*env)->GetFieldID(env, clazz, "aceCount", "I");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$CompletionStatus");
+ if (clazz == NULL) {
+ return;
+ }
+ completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I");
+ completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I");
+ completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "I");
+
+ clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$BackupResult");
+ if (clazz == NULL) {
+ return;
+ }
+ backupResult_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I");
+ backupResult_context = (*env)->GetFieldID(env, clazz, "context", "J");
+
+
+ h = LoadLibrary("kernel32");
+ if (h != INVALID_HANDLE_VALUE) {
+ FindFirstStream_func =
+ (FindFirstStream_Proc)GetProcAddress(h, "FindFirstStreamW");
+ FindNextStream_func =
+ (FindNextStream_Proc)GetProcAddress(h, "FindNextStreamW");
+ CreateSymbolicLink_func =
+ (CreateSymbolicLinkProc)GetProcAddress(h, "CreateSymbolicLinkW");
+ CreateHardLink_func =
+ (CreateHardLinkProc)GetProcAddress(h, "CreateHardLinkW");
+ GetFinalPathNameByHandle_func =
+ (GetFinalPathNameByHandleProc)GetProcAddress(h, "GetFinalPathNameByHandleW");
+ FreeLibrary(h);
+ }
+
+ h = LoadLibrary("advapi32");
+ if (h != INVALID_HANDLE_VALUE) {
+ ConvertSidToStringSid_func =
+ (ConvertSidToStringSidProc)GetProcAddress(h, "ConvertSidToStringSidW");
+ ConvertStringSidToSid_func =
+ (ConvertStringSidToSidProc)GetProcAddress(h, "ConvertStringSidToSidW");
+ GetLengthSid_func =
+ (GetLengthSidProc)GetProcAddress(h, "GetLengthSid");
+ FreeLibrary(h);
+ }
+
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FormatMessage(JNIEnv* env, jclass this, jint errorCode) {
+ WCHAR message[255];
+
+ DWORD len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ (DWORD)errorCode,
+ 0,
+ &message[0],
+ 255,
+ NULL);
+
+
+ if (len == 0) {
+ return NULL;
+ } else {
+ return (*env)->NewString(env, (const jchar *)message, (jsize)wcslen(message));
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_LocalFree(JNIEnv* env, jclass this, jlong address)
+{
+ HLOCAL hMem = (HLOCAL)jlong_to_ptr(address);
+ LocalFree(hMem);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CreateFile0(JNIEnv* env, jclass this,
+ jlong address, jint dwDesiredAccess, jint dwShareMode, jlong sdAddress,
+ jint dwCreationDisposition, jint dwFlagsAndAttributes)
+{
+ HANDLE handle;
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+
+ SECURITY_ATTRIBUTES securityAttributes;
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes;
+ PSECURITY_DESCRIPTOR lpSecurityDescriptor = jlong_to_ptr(sdAddress);
+
+
+ if (lpSecurityDescriptor == NULL) {
+ lpSecurityAttributes = NULL;
+ } else {
+ securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ securityAttributes.lpSecurityDescriptor = lpSecurityDescriptor;
+ securityAttributes.bInheritHandle = FALSE;
+ lpSecurityAttributes = &securityAttributes;
+ }
+
+ handle = CreateFileW(lpFileName,
+ (DWORD)dwDesiredAccess,
+ (DWORD)dwShareMode,
+ lpSecurityAttributes,
+ (DWORD)dwCreationDisposition,
+ (DWORD)dwFlagsAndAttributes,
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ throwWindowsException(env, GetLastError());
+ }
+ return ptr_to_jlong(handle);
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_DeviceIoControlSetSparse(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ DWORD bytesReturned;
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ if (DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesReturned, NULL) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_DeviceIoControlGetReparsePoint(JNIEnv* env, jclass this,
+ jlong handle, jlong bufferAddress, jint bufferSize)
+{
+ DWORD bytesReturned;
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ LPVOID outBuffer = (LPVOID)jlong_to_ptr(bufferAddress);
+
+ if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, outBuffer, (DWORD)bufferSize,
+ &bytesReturned, NULL) == 0)
+ {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_DeleteFile0(JNIEnv* env, jclass this, jlong address)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+ if (DeleteFileW(lpFileName) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CreateDirectory0(JNIEnv* env, jclass this,
+ jlong address, jlong sdAddress)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+
+ SECURITY_ATTRIBUTES securityAttributes;
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes;
+ PSECURITY_DESCRIPTOR lpSecurityDescriptor = jlong_to_ptr(sdAddress);
+
+
+ if (lpSecurityDescriptor == NULL) {
+ lpSecurityAttributes = NULL;
+ } else {
+ securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ securityAttributes.lpSecurityDescriptor = lpSecurityDescriptor;
+ securityAttributes.bInheritHandle = FALSE;
+ lpSecurityAttributes = &securityAttributes;
+ }
+
+ if (CreateDirectoryW(lpFileName, lpSecurityAttributes) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_RemoveDirectory0(JNIEnv* env, jclass this, jlong address)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+ if (RemoveDirectoryW(lpFileName) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CloseHandle(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ CloseHandle(h);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile0(JNIEnv* env, jclass this,
+ jlong address, jobject obj)
+{
+ WIN32_FIND_DATAW data;
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+
+ HANDLE handle = FindFirstFileW(lpFileName, &data);
+ if (handle != INVALID_HANDLE_VALUE) {
+ jstring name = (*env)->NewString(env, data.cFileName, wcslen(data.cFileName));
+ if (name == NULL)
+ return;
+ (*env)->SetLongField(env, obj, findFirst_handle, ptr_to_jlong(handle));
+ (*env)->SetObjectField(env, obj, findFirst_name, name);
+ } else {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile1(JNIEnv* env, jclass this,
+ jlong pathAddress, jlong dataAddress)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
+ WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress);
+
+ HANDLE handle = FindFirstFileW(lpFileName, data);
+ if (handle == INVALID_HANDLE_VALUE) {
+ throwWindowsException(env, GetLastError());
+ }
+ return ptr_to_jlong(handle);
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindNextFile(JNIEnv* env, jclass this,
+ jlong handle, jlong dataAddress)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress);
+
+ if (FindNextFileW(h, data) != 0) {
+ return (*env)->NewString(env, data->cFileName, wcslen(data->cFileName));
+ } else {
+ if (GetLastError() != ERROR_NO_MORE_FILES)
+ throwWindowsException(env, GetLastError());
+ return NULL;
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstStream0(JNIEnv* env, jclass this,
+ jlong address, jobject obj)
+{
+ MY_WIN32_FIND_STREAM_DATA data;
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+ HANDLE handle;
+
+ if (FindFirstStream_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return;
+ }
+
+ handle = (*FindFirstStream_func)(lpFileName, FindStreamInfoStandard, &data, 0);
+ if (handle != INVALID_HANDLE_VALUE) {
+ jstring name = (*env)->NewString(env, data.cStreamName, wcslen(data.cStreamName));
+ if (name == NULL)
+ return;
+ (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle));
+ (*env)->SetObjectField(env, obj, findStream_name, name);
+ } else {
+ if (GetLastError() == ERROR_HANDLE_EOF) {
+ (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle));
+ } else {
+ throwWindowsException(env, GetLastError());
+ }
+ }
+
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindNextStream(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ MY_WIN32_FIND_STREAM_DATA data;
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+
+ if (FindNextStream_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return NULL;
+ }
+
+ if ((*FindNextStream_func)(h, &data) != 0) {
+ return (*env)->NewString(env, data.cStreamName, wcslen(data.cStreamName));
+ } else {
+ if (GetLastError() != ERROR_HANDLE_EOF)
+ throwWindowsException(env, GetLastError());
+ return NULL;
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_FindClose(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ if (FindClose(h) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetFileInformationByHandle(JNIEnv* env, jclass this,
+ jlong handle, jlong address)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ BY_HANDLE_FILE_INFORMATION* info =
+ (BY_HANDLE_FILE_INFORMATION*)jlong_to_ptr(address);
+ if (GetFileInformationByHandle(h, info) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CopyFileEx0(JNIEnv* env, jclass this,
+ jlong existingAddress, jlong newAddress, jint flags, jlong cancelAddress)
+{
+ LPCWSTR lpExistingFileName = jlong_to_ptr(existingAddress);
+ LPCWSTR lpNewFileName = jlong_to_ptr(newAddress);
+ LPBOOL cancel = (LPBOOL)jlong_to_ptr(cancelAddress);
+ if (CopyFileExW(lpExistingFileName, lpNewFileName, NULL, NULL, cancel,
+ (DWORD)flags) == 0)
+ {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_MoveFileEx0(JNIEnv* env, jclass this,
+ jlong existingAddress, jlong newAddress, jint flags)
+{
+ LPCWSTR lpExistingFileName = jlong_to_ptr(existingAddress);
+ LPCWSTR lpNewFileName = jlong_to_ptr(newAddress);
+ if (MoveFileExW(lpExistingFileName, lpNewFileName, (DWORD)flags) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetLogicalDrives(JNIEnv* env, jclass this)
+{
+ DWORD res = GetLogicalDrives();
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+ return (jint)res;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributes0(JNIEnv* env, jclass this,
+ jlong address)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+ DWORD value = GetFileAttributesW(lpFileName);
+
+ if (value == INVALID_FILE_ATTRIBUTES) {
+ throwWindowsException(env, GetLastError());
+ }
+ return (jint)value;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetFileAttributes0(JNIEnv* env, jclass this,
+ jlong address, jint value)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+ if (SetFileAttributesW(lpFileName, (DWORD)value) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributesEx0(JNIEnv* env, jclass this,
+ jlong pathAddress, jlong dataAddress)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
+ WIN32_FILE_ATTRIBUTE_DATA* data = (WIN32_FILE_ATTRIBUTE_DATA*)jlong_to_ptr(dataAddress);
+
+ BOOL res = GetFileAttributesExW(lpFileName, GetFileExInfoStandard, (LPVOID)data);
+ if (res == 0)
+ throwWindowsException(env, GetLastError());
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetFileTime(JNIEnv* env, jclass this,
+ jlong handle, jlong createTime, jlong lastAccessTime, jlong lastWriteTime)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+
+ if (SetFileTime(h,
+ (createTime == (jlong)0) ? NULL : (CONST FILETIME *)&createTime,
+ (lastAccessTime == (jlong)0) ? NULL : (CONST FILETIME *)&lastAccessTime,
+ (lastWriteTime == (jlong)0) ? NULL : (CONST FILETIME *)&lastWriteTime) == 0)
+ {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetEndOfFile(JNIEnv* env, jclass this,
+ jlong handle)
+{
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+
+ if (SetEndOfFile(h) == 0)
+ throwWindowsException(env, GetLastError());
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetVolumeInformation0(JNIEnv* env, jclass this,
+ jlong address, jobject obj)
+{
+ WCHAR volumeName[MAX_PATH+1];
+ DWORD volumeSerialNumber;
+ DWORD maxComponentLength;
+ DWORD flags;
+ WCHAR fileSystemName[MAX_PATH+1];
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+ jstring str;
+
+ BOOL res = GetVolumeInformationW(lpFileName,
+ &volumeName[0],
+ MAX_PATH+1,
+ &volumeSerialNumber,
+ &maxComponentLength,
+ &flags,
+ &fileSystemName[0],
+ MAX_PATH+1);
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ return;
+ }
+
+ str = (*env)->NewString(env, (const jchar *)fileSystemName, (jsize)wcslen(fileSystemName));
+ if (str == NULL) return;
+ (*env)->SetObjectField(env, obj, volumeInfo_fsName, str);
+
+ str = (*env)->NewString(env, (const jchar *)volumeName, (jsize)wcslen(volumeName));
+ if (str == NULL) return;
+ (*env)->SetObjectField(env, obj, volumeInfo_volName, str);
+
+ (*env)->SetIntField(env, obj, volumeInfo_volSN, (jint)volumeSerialNumber);
+ (*env)->SetIntField(env, obj, volumeInfo_flags, (jint)flags);
+}
+
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetDriveType0(JNIEnv* env, jclass this, jlong address) {
+ LPCWSTR lpRootPathName = jlong_to_ptr(address);
+ return (jint)GetDriveTypeW(lpRootPathName);
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetDiskFreeSpaceEx0(JNIEnv* env, jclass this,
+ jlong address, jobject obj)
+{
+ ULARGE_INTEGER freeBytesAvailable;
+ ULARGE_INTEGER totalNumberOfBytes;
+ ULARGE_INTEGER totalNumberOfFreeBytes;
+ LPCWSTR lpDirName = jlong_to_ptr(address);
+
+
+ BOOL res = GetDiskFreeSpaceExW(lpDirName,
+ &freeBytesAvailable,
+ &totalNumberOfBytes,
+ &totalNumberOfFreeBytes);
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ return;
+ }
+
+ (*env)->SetLongField(env, obj, diskSpace_bytesAvailable,
+ long_to_jlong(freeBytesAvailable.QuadPart));
+ (*env)->SetLongField(env, obj, diskSpace_totalBytes,
+ long_to_jlong(totalNumberOfBytes.QuadPart));
+ (*env)->SetLongField(env, obj, diskSpace_totalFree,
+ long_to_jlong(totalNumberOfFreeBytes.QuadPart));
+}
+
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetVolumePathName0(JNIEnv* env, jclass this,
+ jlong address)
+{
+ WCHAR volumeName[MAX_PATH+1];
+ LPCWSTR lpFileName = jlong_to_ptr(address);
+
+
+ BOOL res = GetVolumePathNameW(lpFileName,
+ &volumeName[0],
+ MAX_PATH+1);
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ return NULL;
+ } else {
+ return (*env)->NewString(env, (const jchar *)volumeName, (jsize)wcslen(volumeName));
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_InitializeSecurityDescriptor(JNIEnv* env, jclass this,
+ jlong address)
+{
+ PSECURITY_DESCRIPTOR pSecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)jlong_to_ptr(address);
+
+ if (InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_InitializeAcl(JNIEnv* env, jclass this,
+ jlong address, jint size)
+{
+ PACL pAcl = (PACL)jlong_to_ptr(address);
+
+ if (InitializeAcl(pAcl, (DWORD)size, ACL_REVISION) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetFileSecurity0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint requestedInformation, jlong descAddress)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress);
+ DWORD lengthNeeded = 0;
+
+ BOOL res = SetFileSecurityW(lpFileName,
+ (SECURITY_INFORMATION)requestedInformation,
+ pSecurityDescriptor);
+
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetFileSecurity0(JNIEnv* env, jclass this,
+ jlong pathAddress, jint requestedInformation, jlong descAddress, jint nLength)
+{
+ LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress);
+ DWORD lengthNeeded = 0;
+
+ BOOL res = GetFileSecurityW(lpFileName,
+ (SECURITY_INFORMATION)requestedInformation,
+ pSecurityDescriptor,
+ (DWORD)nLength,
+ &lengthNeeded);
+
+ if (res == 0) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ return (jint)lengthNeeded;
+ } else {
+ throwWindowsException(env, GetLastError());
+ return 0;
+ }
+ } else {
+ return (jint)nLength;
+ }
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetSecurityDescriptorOwner(JNIEnv* env,
+ jclass this, jlong address)
+{
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(address);
+ PSID pOwner;
+ BOOL bOwnerDefaulted;
+
+
+ if (GetSecurityDescriptorOwner(pSecurityDescriptor, &pOwner, &bOwnerDefaulted) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+ return ptr_to_jlong(pOwner);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetSecurityDescriptorOwner(JNIEnv* env,
+ jclass this, jlong descAddress, jlong ownerAddress)
+{
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress);
+ PSID pOwner = jlong_to_ptr(ownerAddress);
+
+ if (SetSecurityDescriptorOwner(pSecurityDescriptor, pOwner, FALSE) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetSecurityDescriptorDacl(JNIEnv* env,
+ jclass this, jlong address)
+{
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(address);
+ BOOL bDaclPresent;
+ PACL pDacl;
+ BOOL bDaclDefaulted;
+
+ if (GetSecurityDescriptorDacl(pSecurityDescriptor, &bDaclPresent, &pDacl, &bDaclDefaulted) == 0) {
+ throwWindowsException(env, GetLastError());
+ return (jlong)0;
+ } else {
+ return (bDaclPresent) ? ptr_to_jlong(pDacl) : (jlong)0;
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetSecurityDescriptorDacl(JNIEnv* env,
+ jclass this, jlong descAddress, jlong aclAddress)
+{
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = (PSECURITY_DESCRIPTOR)jlong_to_ptr(descAddress);
+ PACL pAcl = (PACL)jlong_to_ptr(aclAddress);
+
+ if (SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, pAcl, FALSE) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetAclInformation0(JNIEnv* env,
+ jclass this, jlong address, jobject obj)
+{
+ PACL pAcl = (PACL)jlong_to_ptr(address);
+ ACL_SIZE_INFORMATION acl_size_info;
+
+ if (GetAclInformation(pAcl, (void *) &acl_size_info, sizeof(acl_size_info), AclSizeInformation) == 0) {
+ throwWindowsException(env, GetLastError());
+ } else {
+ (*env)->SetIntField(env, obj, aclInfo_aceCount, (jint)acl_size_info.AceCount);
+ }
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetAce(JNIEnv* env, jclass this, jlong address,
+ jint aceIndex)
+{
+ PACL pAcl = (PACL)jlong_to_ptr(address);
+ LPVOID pAce;
+
+ if (GetAce(pAcl, (DWORD)aceIndex, &pAce) == 0) {
+ throwWindowsException(env, GetLastError());
+ return (jlong)0;
+ } else {
+ return ptr_to_jlong(pAce);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_AddAccessAllowedAceEx(JNIEnv* env,
+ jclass this, jlong aclAddress, jint flags, jint mask, jlong sidAddress)
+{
+ PACL pAcl = (PACL)jlong_to_ptr(aclAddress);
+ PSID pSid = (PSID)jlong_to_ptr(sidAddress);
+
+ if (AddAccessAllowedAceEx(pAcl, ACL_REVISION, (DWORD)flags, (DWORD)mask, pSid) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_AddAccessDeniedAceEx(JNIEnv* env,
+ jclass this, jlong aclAddress, jint flags, jint mask, jlong sidAddress)
+{
+ PACL pAcl = (PACL)jlong_to_ptr(aclAddress);
+ PSID pSid = (PSID)jlong_to_ptr(sidAddress);
+
+ if (AddAccessDeniedAceEx(pAcl, ACL_REVISION, (DWORD)flags, (DWORD)mask, pSid) == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_LookupAccountSid0(JNIEnv* env,
+ jclass this, jlong address, jobject obj)
+{
+ WCHAR domain[255];
+ WCHAR name[255];
+ DWORD domainLen = sizeof(domain);
+ DWORD nameLen = sizeof(name);
+ SID_NAME_USE use;
+ PSID sid = jlong_to_ptr(address);
+ jstring s;
+
+ if (LookupAccountSidW(NULL, sid, &name[0], &nameLen, &domain[0], &domainLen, &use) == 0) {
+ throwWindowsException(env, GetLastError());
+ return;
+ }
+
+ s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));
+ if (s == NULL)
+ return;
+ (*env)->SetObjectField(env, obj, account_domain, s);
+
+ s = (*env)->NewString(env, (const jchar *)name, (jsize)wcslen(name));
+ if (s == NULL)
+ return;
+ (*env)->SetObjectField(env, obj, account_name, s);
+ (*env)->SetIntField(env, obj, account_use, (jint)use);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_LookupAccountName0(JNIEnv* env,
+ jclass this, jlong nameAddress, jlong sidAddress, jint cbSid)
+{
+
+ LPCWSTR accountName = jlong_to_ptr(nameAddress);
+ PSID sid = jlong_to_ptr(sidAddress);
+ WCHAR domain[255];
+ DWORD domainLen = sizeof(domain);
+ SID_NAME_USE use;
+
+ if (LookupAccountNameW(NULL, accountName, sid, (LPDWORD)&cbSid,
+ &domain[0], &domainLen, &use) == 0)
+ {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ throwWindowsException(env, GetLastError());
+ }
+ }
+
+ return cbSid;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetLengthSid(JNIEnv* env,
+ jclass this, jlong address)
+{
+ PSID sid = jlong_to_ptr(address);
+
+ if (GetLengthSid_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return 0;
+ }
+ return (jint)(*GetLengthSid_func)(sid);
+}
+
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_ConvertSidToStringSid(JNIEnv* env,
+ jclass this, jlong address)
+{
+ PSID sid = jlong_to_ptr(address);
+ LPWSTR string;
+
+ if (ConvertSidToStringSid_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return NULL;
+ }
+
+ if ((*ConvertSidToStringSid_func)(sid, &string) == 0) {
+ throwWindowsException(env, GetLastError());
+ return NULL;
+ } else {
+ jstring s = (*env)->NewString(env, (const jchar *)string,
+ (jsize)wcslen(string));
+ LocalFree(string);
+ return s;
+ }
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_ConvertStringSidToSid0(JNIEnv* env,
+ jclass this, jlong address)
+{
+ LPWSTR lpStringSid = jlong_to_ptr(address);
+ PSID pSid;
+
+ if (ConvertStringSidToSid_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return (jlong)0;
+ }
+
+ if ((*ConvertStringSidToSid_func)(lpStringSid, &pSid) == 0)
+ throwWindowsException(env, GetLastError());
+
+ return ptr_to_jlong(pSid);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetCurrentProcess(JNIEnv* env, jclass this) {
+ HANDLE hProcess = GetCurrentProcess();
+ return ptr_to_jlong(hProcess);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetCurrentThread(JNIEnv* env, jclass this) {
+ HANDLE hThread = GetCurrentThread();
+ return ptr_to_jlong(hThread);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_OpenProcessToken(JNIEnv* env,
+ jclass this, jlong process, jint desiredAccess)
+{
+ HANDLE hProcess = (HANDLE)jlong_to_ptr(process);
+ HANDLE hToken;
+
+ if (OpenProcessToken(hProcess, (DWORD)desiredAccess, &hToken) == 0)
+ throwWindowsException(env, GetLastError());
+ return ptr_to_jlong(hToken);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_OpenThreadToken(JNIEnv* env,
+ jclass this, jlong thread, jint desiredAccess, jboolean openAsSelf)
+{
+ HANDLE hThread = (HANDLE)jlong_to_ptr(thread);
+ HANDLE hToken;
+ BOOL bOpenAsSelf = (openAsSelf == JNI_TRUE) ? TRUE : FALSE;
+
+ if (OpenThreadToken(hThread, (DWORD)desiredAccess, bOpenAsSelf, &hToken) == 0) {
+ if (GetLastError() == ERROR_NO_TOKEN)
+ return (jlong)0;
+ throwWindowsException(env, GetLastError());
+ }
+ return ptr_to_jlong(hToken);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_DuplicateTokenEx(JNIEnv* env,
+ jclass this, jlong token, jint desiredAccess)
+{
+ HANDLE hToken = (HANDLE)jlong_to_ptr(token);
+ HANDLE resultToken;
+ BOOL res;
+
+ res = DuplicateTokenEx(hToken,
+ (DWORD)desiredAccess,
+ NULL,
+ SecurityImpersonation,
+ TokenImpersonation,
+ &resultToken);
+ if (res == 0)
+ throwWindowsException(env, GetLastError());
+ return ptr_to_jlong(resultToken);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_SetThreadToken(JNIEnv* env,
+ jclass this, jlong thread, jlong token)
+{
+ HANDLE hThread = (HANDLE)jlong_to_ptr(thread);
+ HANDLE hToken = (HANDLE)jlong_to_ptr(token);
+
+ if (SetThreadToken(hThread, hToken) == 0)
+ throwWindowsException(env, GetLastError());
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetTokenInformation(JNIEnv* env,
+ jclass this, jlong token, jint tokenInfoClass, jlong tokenInfo, jint tokenInfoLength)
+{
+ BOOL res;
+ DWORD lengthNeeded;
+ HANDLE hToken = (HANDLE)jlong_to_ptr(token);
+ LPVOID result = (LPVOID)jlong_to_ptr(tokenInfo);
+
+ res = GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)tokenInfoClass, (LPVOID)result,
+ tokenInfoLength, &lengthNeeded);
+ if (res == 0) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ return (jint)lengthNeeded;
+ } else {
+ throwWindowsException(env, GetLastError());
+ return 0;
+ }
+ } else {
+ return tokenInfoLength;
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_AdjustTokenPrivileges(JNIEnv* env,
+ jclass this, jlong token, jlong luid, jint attributes)
+{
+ TOKEN_PRIVILEGES privs[1];
+ HANDLE hToken = (HANDLE)jlong_to_ptr(token);
+ PLUID pLuid = (PLUID)jlong_to_ptr(luid);
+
+ privs[0].PrivilegeCount = 1;
+ privs[0].Privileges[0].Luid = *pLuid;
+ privs[0].Privileges[0].Attributes = (DWORD)attributes;
+
+ if (AdjustTokenPrivileges(hToken, FALSE, &privs[0], 1, NULL, NULL) == 0)
+ throwWindowsException(env, GetLastError());
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_LookupPrivilegeValue0(JNIEnv* env,
+ jclass this, jlong name)
+{
+ LPCWSTR lpName = (LPCWSTR)jlong_to_ptr(name);
+ PLUID pLuid = LocalAlloc(0, sizeof(LUID));
+
+ if (pLuid == NULL) {
+ JNU_ThrowInternalError(env, "Unable to allocate LUID structure");
+ } else {
+ if (LookupPrivilegeValueW(NULL, lpName, pLuid) == 0)
+ throwWindowsException(env, GetLastError());
+ }
+ return ptr_to_jlong(pLuid);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_BuildTrusteeWithSid(JNIEnv* env,
+ jclass this, jlong sid)
+{
+ PSID pSid = (HANDLE)jlong_to_ptr(sid);
+ PTRUSTEE_W pTrustee = LocalAlloc(0, sizeof(TRUSTEE_W));
+
+ if (pTrustee == NULL) {
+ JNU_ThrowInternalError(env, "Unable to allocate TRUSTEE_W structure");
+ } else {
+ BuildTrusteeWithSidW(pTrustee, pSid);
+ }
+ return ptr_to_jlong(pTrustee);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetEffectiveRightsFromAcl(JNIEnv* env,
+ jclass this, jlong acl, jlong trustee)
+{
+ ACCESS_MASK access;
+ PACL pAcl = (PACL)jlong_to_ptr(acl);
+ PTRUSTEE pTrustee = (PTRUSTEE)jlong_to_ptr(trustee);
+
+ if (GetEffectiveRightsFromAcl(pAcl, pTrustee, &access) != ERROR_SUCCESS) {
+ throwWindowsException(env, GetLastError());
+ }
+ return (jint)access;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CreateSymbolicLink0(JNIEnv* env,
+ jclass this, jlong linkAddress, jlong targetAddress, jint flags)
+{
+ LPCWSTR link = jlong_to_ptr(linkAddress);
+ LPCWSTR target = jlong_to_ptr(targetAddress);
+
+ if (CreateSymbolicLink_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return;
+ }
+
+ /* On Windows 64-bit this appears to succeed even when there is insufficient privileges */
+ if ((*CreateSymbolicLink_func)(link, target, (DWORD)flags) == 0)
+ throwWindowsException(env, GetLastError());
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CreateHardLink0(JNIEnv* env,
+ jclass this, jlong newFileAddress, jlong existingFileAddress)
+{
+ LPCWSTR newFile = jlong_to_ptr(newFileAddress);
+ LPCWSTR existingFile = jlong_to_ptr(existingFileAddress);
+
+ if (CreateHardLink_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return;
+ }
+ if ((*CreateHardLink_func)(newFile, existingFile, NULL) == 0)
+ throwWindowsException(env, GetLastError());
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetFullPathName0(JNIEnv *env,
+ jclass clz,
+ jlong pathAddress)
+{
+ jstring rv = NULL;
+ WCHAR *lpBuf = NULL;
+ WCHAR buf[MAX_PATH];
+ DWORD len;
+ LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
+
+ len = GetFullPathNameW(lpFileName, MAX_PATH, buf, NULL);
+ if (len > 0) {
+ if (len < MAX_PATH) {
+ rv = (*env)->NewString(env, buf, len);
+ } else {
+ len += 1; /* return length does not include terminator */
+ lpBuf = (WCHAR*)malloc(len * sizeof(WCHAR));
+ if (lpBuf != NULL) {
+ len = GetFullPathNameW(lpFileName, len, lpBuf, NULL);
+ if (len > 0) {
+ rv = (*env)->NewString(env, lpBuf, len);
+ } else {
+ JNU_ThrowInternalError(env, "GetFullPathNameW failed");
+ }
+ free(lpBuf);
+ }
+ }
+ }
+ if (len == 0)
+ throwWindowsException(env, GetLastError());
+
+ return rv;
+}
+
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetFinalPathNameByHandle(JNIEnv* env,
+ jclass this, jlong handle)
+{
+ jstring rv = NULL;
+ WCHAR *lpBuf = NULL;
+ WCHAR path[MAX_PATH];
+ HANDLE h = (HANDLE)jlong_to_ptr(handle);
+ DWORD len;
+
+ if (GetFinalPathNameByHandle_func == NULL) {
+ JNU_ThrowInternalError(env, "Should not get here");
+ return NULL;
+ }
+
+ len = (*GetFinalPathNameByHandle_func)(h, path, MAX_PATH, 0);
+ if (len > 0) {
+ if (len < MAX_PATH) {
+ rv = (*env)->NewString(env, (const jchar *)path, (jsize)len);
+ } else {
+ len += 1; /* return length does not include terminator */
+ lpBuf = (WCHAR*)malloc(len * sizeof(WCHAR));
+ if (lpBuf != NULL) {
+ len = (*GetFinalPathNameByHandle_func)(h, lpBuf, len, 0);
+ if (len > 0) {
+ rv = (*env)->NewString(env, (const jchar *)lpBuf, (jsize)len);
+ } else {
+ JNU_ThrowInternalError(env, "GetFinalPathNameByHandleW failed");
+ }
+ free(lpBuf);
+ }
+ }
+ }
+
+ if (len == 0)
+ throwWindowsException(env, GetLastError());
+
+ return rv;
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_CreateIoCompletionPort(JNIEnv* env, jclass this,
+ jlong fileHandle, jlong existingPort, jint completionKey)
+{
+ HANDLE port = CreateIoCompletionPort((HANDLE)jlong_to_ptr(fileHandle),
+ (HANDLE)jlong_to_ptr(existingPort),
+ (DWORD)completionKey,
+ 0);
+ if (port == NULL) {
+ throwWindowsException(env, GetLastError());
+ }
+ return ptr_to_jlong(port);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_GetQueuedCompletionStatus0(JNIEnv* env, jclass this,
+ jlong completionPort, jobject obj)
+{
+ DWORD bytesTransferred;
+ DWORD completionKey;
+ OVERLAPPED *lpOverlapped;
+ BOOL res;
+
+ res = GetQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
+ &bytesTransferred,
+ &completionKey,
+ &lpOverlapped,
+ INFINITE);
+ if (res == 0 && lpOverlapped == NULL) {
+ throwWindowsException(env, GetLastError());
+ } else {
+ DWORD ioResult = (res == 0) ? GetLastError() : 0;
+ (*env)->SetIntField(env, obj, completionStatus_error, ioResult);
+ (*env)->SetIntField(env, obj, completionStatus_bytesTransferred,
+ (jint)bytesTransferred);
+ (*env)->SetIntField(env, obj, completionStatus_completionKey,
+ (jint)completionKey);
+
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_PostQueuedCompletionStatus(JNIEnv* env, jclass this,
+ jlong completionPort, jint completionKey)
+{
+ BOOL res;
+
+ res = PostQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
+ (DWORD)0, /* dwNumberOfBytesTransferred */
+ (DWORD)completionKey,
+ NULL); /* lpOverlapped */
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_ReadDirectoryChangesW(JNIEnv* env, jclass this,
+ jlong hDirectory, jlong bufferAddress, jint bufferLength, jboolean watchSubTree, jint filter,
+ jlong bytesReturnedAddress, jlong pOverlapped)
+{
+ BOOL res;
+ BOOL subtree = (watchSubTree == JNI_TRUE) ? TRUE : FALSE;
+
+ ((LPOVERLAPPED)jlong_to_ptr(pOverlapped))->hEvent = NULL;
+ res = ReadDirectoryChangesW((HANDLE)jlong_to_ptr(hDirectory),
+ (LPVOID)jlong_to_ptr(bufferAddress),
+ (DWORD)bufferLength,
+ subtree,
+ (DWORD)filter,
+ (LPDWORD)jlong_to_ptr(bytesReturnedAddress),
+ (LPOVERLAPPED)jlong_to_ptr(pOverlapped),
+ NULL);
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_BackupRead0(JNIEnv* env, jclass this,
+ jlong hFile, jlong bufferAddress, jint bufferSize, jboolean abort,
+ jlong context, jobject obj)
+{
+ BOOL res;
+ DWORD bytesTransferred;
+ BOOL a = (abort == JNI_TRUE) ? TRUE : FALSE;
+ VOID* pContext = (VOID*)jlong_to_ptr(context);
+
+ res = BackupRead((HANDLE)jlong_to_ptr(hFile),
+ (LPBYTE)jlong_to_ptr(bufferAddress),
+ (DWORD)bufferSize,
+ &bytesTransferred,
+ a,
+ FALSE,
+ &pContext);
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ } else {
+ (*env)->SetIntField(env, obj, backupResult_bytesTransferred,
+ bytesTransferred);
+ (*env)->SetLongField(env, obj, backupResult_context,
+ ptr_to_jlong(pContext));
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_WindowsNativeDispatcher_BackupSeek(JNIEnv* env, jclass this,
+ jlong hFile, jlong bytesToSeek, jlong context)
+{
+ BOOL res;
+ jint lowBytesToSeek = (jint)bytesToSeek;
+ jint highBytesToSeek = (jint)(bytesToSeek >> 32);
+ DWORD lowBytesSeeked;
+ DWORD highBytesSeeked;
+ VOID* pContext = jlong_to_ptr(context);
+
+ res = BackupSeek((HANDLE)jlong_to_ptr(hFile),
+ (DWORD)lowBytesToSeek,
+ (DWORD)highBytesToSeek,
+ &lowBytesSeeked,
+ &highBytesSeeked,
+ &pContext);
+ if (res == 0) {
+ throwWindowsException(env, GetLastError());
+ }
+}
diff --git a/src/windows/native/sun/security/krb5/WindowsDirectory.c b/src/windows/native/sun/security/krb5/WindowsDirectory.c
index dc96bad91..7b1116386 100644
--- a/src/windows/native/sun/security/krb5/WindowsDirectory.c
+++ b/src/windows/native/sun/security/krb5/WindowsDirectory.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc. 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
@@ -23,6 +23,7 @@
* have any questions.
*/
+#define UNICODE
#include <jni.h>
#include <windows.h>
#include <stdlib.h>
@@ -30,22 +31,20 @@
/*
* Class: sun_security_krb5_Config
* Method: getWindowsDirectory
- * Signature: ()Ljava/lang/String;
+ * Signature: (Z)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_sun_security_krb5_Config_getWindowsDirectory(
- JNIEnv* env, jclass configClass) {
- LPTSTR lpPath = NULL;
- UINT uLength ;
- jstring path = NULL;
-
- if (uLength = GetWindowsDirectory(lpPath, 0)) {
- lpPath = (LPTSTR)malloc(sizeof(TCHAR) * uLength);
- if (lpPath != NULL) {
- if (GetWindowsDirectory(lpPath, uLength)) {
- path = (*env)->NewStringUTF(env, lpPath);
- }
- free(lpPath);
- }
+ JNIEnv* env, jclass configClass, jboolean isSystem) {
+ TCHAR lpPath[MAX_PATH+1];
+ UINT len;
+ if (isSystem) {
+ len = GetSystemWindowsDirectory(lpPath, MAX_PATH);
+ } else {
+ len = GetWindowsDirectory(lpPath, MAX_PATH);
+ }
+ if (len) {
+ return (*env)->NewString(env, lpPath, len);
+ } else {
+ return NULL;
}
- return path;
}
diff --git a/src/windows/native/sun/windows/ComCtl32Util.cpp b/src/windows/native/sun/windows/ComCtl32Util.cpp
index a36ac6df9..ac69cf3af 100644
--- a/src/windows/native/sun/windows/ComCtl32Util.cpp
+++ b/src/windows/native/sun/windows/ComCtl32Util.cpp
@@ -23,54 +23,26 @@
* have any questions.
*/
+#include "awt.h"
#include "ComCtl32Util.h"
ComCtl32Util::ComCtl32Util() {
- hModComCtl32 = NULL;
- m_bNewSubclassing = FALSE;
-
- m_lpfnSetWindowSubclass = NULL;
- m_lpfnRemoveWindowSubclass = NULL;
- m_lpfnDefSubclassProc = NULL;
}
ComCtl32Util::~ComCtl32Util() {
- DASSERT(hModComCtl32 == NULL);
}
void ComCtl32Util::InitLibraries() {
- if (hModComCtl32 == NULL) {
- hModComCtl32 = ::LoadLibrary(TEXT("comctl32.dll"));
- if (hModComCtl32 != NULL) {
- m_lpfnSetWindowSubclass = (PFNSETWINDOWSUBCLASS)::GetProcAddress(hModComCtl32, "SetWindowSubclass");
- m_lpfnRemoveWindowSubclass = (PFNREMOVEWINDOWSUBCLASS)::GetProcAddress(hModComCtl32, "RemoveWindowSubclass");
- m_lpfnDefSubclassProc = (PFNDEFSUBCLASSPROC)::GetProcAddress(hModComCtl32, "DefSubclassProc");
-
- m_bNewSubclassing = (m_lpfnSetWindowSubclass != NULL) &&
- (m_lpfnRemoveWindowSubclass != NULL) &&
- (m_lpfnDefSubclassProc != NULL);
-
- fn_InitCommonControlsEx = (ComCtl32Util::InitCommonControlsExType)::GetProcAddress(hModComCtl32, "InitCommonControlsEx");
- InitCommonControls();
- }
- }
-}
-
-void ComCtl32Util::FreeLibraries() {
- if (hModComCtl32 != NULL) {
- m_lpfnSetWindowSubclass = NULL;
- m_lpfnRemoveWindowSubclass = NULL;
- m_lpfnDefSubclassProc = NULL;
- ::FreeLibrary(hModComCtl32);
- hModComCtl32 = NULL;
- }
+ INITCOMMONCONTROLSEX iccex;
+ memset(&iccex, 0, sizeof(INITCOMMONCONTROLSEX));
+ iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ ::InitCommonControlsEx(&iccex);
}
WNDPROC ComCtl32Util::SubclassHWND(HWND hwnd, WNDPROC _WindowProc) {
- if (m_bNewSubclassing) {
- DASSERT(hModComCtl32 != NULL);
+ if (IS_WINXP) {
const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc
- m_lpfnSetWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc, NULL); // _WindowProc is used as subclass ID
+ ::SetWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc, NULL); // _WindowProc is used as subclass ID
return NULL;
} else {
return (WNDPROC)::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_WindowProc);
@@ -78,21 +50,17 @@ WNDPROC ComCtl32Util::SubclassHWND(HWND hwnd, WNDPROC _WindowProc) {
}
void ComCtl32Util::UnsubclassHWND(HWND hwnd, WNDPROC _WindowProc, WNDPROC _DefWindowProc) {
- if (m_bNewSubclassing) {
- DASSERT(hModComCtl32 != NULL);
- DASSERT(_DefWindowProc == NULL);
+ if (IS_WINXP) {
const SUBCLASSPROC p = SharedWindowProc; // let compiler check type of SharedWindowProc
- m_lpfnRemoveWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc); // _WindowProc is used as subclass ID
+ ::RemoveWindowSubclass(hwnd, p, (UINT_PTR)_WindowProc); // _WindowProc is used as subclass ID
} else {
::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_DefWindowProc);
}
}
LRESULT ComCtl32Util::DefWindowProc(WNDPROC _DefWindowProc, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
- if (m_bNewSubclassing) {
- DASSERT(hModComCtl32 != NULL);
- DASSERT(_DefWindowProc == NULL);
- return m_lpfnDefSubclassProc(hwnd, msg, wParam, lParam);
+ if (IS_WINXP) {
+ return ::DefSubclassProc(hwnd, msg, wParam, lParam);
} else if (_DefWindowProc != NULL) {
return ::CallWindowProc(_DefWindowProc, hwnd, msg, wParam, lParam);
} else {
@@ -111,15 +79,3 @@ LRESULT ComCtl32Util::SharedWindowProc(HWND hwnd, UINT msg,
CATCH_BAD_ALLOC_RET(0);
}
-
-void ComCtl32Util::InitCommonControls()
-{
- if (fn_InitCommonControlsEx == NULL) {
- return;
- }
-
- INITCOMMONCONTROLSEX iccex;
- memset(&iccex, 0, sizeof(INITCOMMONCONTROLSEX));
- iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
- fn_InitCommonControlsEx(&iccex);
-}
diff --git a/src/windows/native/sun/windows/ComCtl32Util.h b/src/windows/native/sun/windows/ComCtl32Util.h
index 888a14db1..e137b43d0 100644
--- a/src/windows/native/sun/windows/ComCtl32Util.h
+++ b/src/windows/native/sun/windows/ComCtl32Util.h
@@ -30,20 +30,6 @@
#ifndef _COMCTL32UTIL_H
#define _COMCTL32UTIL_H
-
-/*
- * comctl32.dll version 6 subclassing - taken from PlatformSDK/Include/commctrl.h
- */
-typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, \
- LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
-
-typedef BOOL (WINAPI *PFNSETWINDOWSUBCLASS)(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass, \
- DWORD_PTR dwRefData);
-typedef BOOL (WINAPI *PFNREMOVEWINDOWSUBCLASS)(HWND hWnd, SUBCLASSPROC pfnSubclass, \
- UINT_PTR uIdSubclass);
-
-typedef LRESULT (WINAPI *PFNDEFSUBCLASSPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
-
class ComCtl32Util
{
public:
@@ -52,21 +38,8 @@ class ComCtl32Util
return theInstance;
}
- // loads comctl32.dll and checks if required routines are available
- // called from AwtToolkit::AwtToolkit()
void InitLibraries();
- // unloads comctl32.dll
- // called from AwtToolkit::Dispose()
- void FreeLibraries();
-
- //-- comctl32.dll version 6 subclassing API --//
- INLINE BOOL IsNewSubclassing() {
- return m_bNewSubclassing;
- }
-
- // if comctl32.dll version 6 is used returns NULL, otherwise
- // returns default window proc
WNDPROC SubclassHWND(HWND hwnd, WNDPROC _WindowProc);
// DefWindowProc is the same as returned from SubclassHWND
void UnsubclassHWND(HWND hwnd, WNDPROC _WindowProc, WNDPROC _DefWindowProc);
@@ -77,19 +50,6 @@ class ComCtl32Util
ComCtl32Util();
~ComCtl32Util();
- HMODULE hModComCtl32;
-
- PFNSETWINDOWSUBCLASS m_lpfnSetWindowSubclass;
- PFNREMOVEWINDOWSUBCLASS m_lpfnRemoveWindowSubclass;
- PFNDEFSUBCLASSPROC m_lpfnDefSubclassProc;
-
- typedef BOOL (WINAPI * InitCommonControlsExType)(const LPINITCOMMONCONTROLSEX lpInitCtrls);
- InitCommonControlsExType fn_InitCommonControlsEx;
-
- void InitCommonControls();
-
- BOOL m_bNewSubclassing;
-
// comctl32.dll version 6 window proc
static LRESULT CALLBACK SharedWindowProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
diff --git a/src/windows/native/sun/windows/Devices.cpp b/src/windows/native/sun/windows/Devices.cpp
index 8bcee15b8..79b36b3f1 100644
--- a/src/windows/native/sun/windows/Devices.cpp
+++ b/src/windows/native/sun/windows/Devices.cpp
@@ -83,9 +83,83 @@
#include "Devices.h"
#include "Trace.h"
-#include "awt_Multimon.h"
#include "D3DPipelineManager.h"
+
+/* Some helper functions (from awt_MMStub.h/cpp) */
+
+int g_nMonitorCounter;
+int g_nMonitorLimit;
+HMONITOR* g_hmpMonitors;
+
+// Callback for CountMonitors below
+BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
+{
+ g_nMonitorCounter ++;
+ return TRUE;
+}
+
+int WINAPI CountMonitors(void)
+{
+ g_nMonitorCounter = 0;
+ ::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, 0L);
+ return g_nMonitorCounter;
+
+}
+
+// Callback for CollectMonitors below
+BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
+{
+
+ if ((g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors)) {
+ g_hmpMonitors[g_nMonitorCounter] = hMon;
+ g_nMonitorCounter ++;
+ }
+
+ return TRUE;
+}
+
+int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum)
+{
+ int retCode = 0;
+
+ if (NULL != hmpMonitors) {
+
+ g_nMonitorCounter = 0;
+ g_nMonitorLimit = nNum;
+ g_hmpMonitors = hmpMonitors;
+
+ ::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, 0L);
+
+ retCode = g_nMonitorCounter;
+
+ g_nMonitorCounter = 0;
+ g_nMonitorLimit = 0;
+ g_hmpMonitors = NULL;
+
+ }
+ return retCode;
+}
+
+BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT* rpBounds)
+{
+ BOOL retCode = FALSE;
+
+ if ((NULL != hmMonitor) && (NULL != rpBounds)) {
+ MONITORINFOEX miInfo;
+
+ memset((void*)(&miInfo), 0, sizeof(MONITORINFOEX));
+ miInfo.cbSize = sizeof(MONITORINFOEX);
+
+ if (TRUE == (retCode = ::GetMonitorInfo(hmMonitor, &miInfo))) {
+ (*rpBounds) = miInfo.rcMonitor;
+ }
+ }
+ return retCode;
+}
+
+/* End of helper functions */
+
Devices* Devices::theInstance = NULL;
CriticalSection Devices::arrayLock;
@@ -113,9 +187,9 @@ BOOL Devices::UpdateInstance(JNIEnv *env)
{
J2dTraceLn(J2D_TRACE_INFO, "Devices::UpdateInstance");
- int numScreens = ::CountMonitors();
- MHND *monHds = (MHND *)safe_Malloc(numScreens * sizeof(MHND));
- if (numScreens != ::CollectMonitors(monHds, numScreens)) {
+ int numScreens = CountMonitors();
+ HMONITOR *monHds = (HMONITOR *)safe_Malloc(numScreens * sizeof(HMONITOR));
+ if (numScreens != CollectMonitors(monHds, numScreens)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"Devices::UpdateInstance: Failed to get all "\
"monitor handles.");
diff --git a/src/windows/native/sun/windows/Devices.h b/src/windows/native/sun/windows/Devices.h
index 949bfdb2d..34e359aa4 100644
--- a/src/windows/native/sun/windows/Devices.h
+++ b/src/windows/native/sun/windows/Devices.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2008 Sun Microsystems, Inc. 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
@@ -70,4 +70,8 @@ static CriticalSection arrayLock;
};
+// Some helper functions (from awt_MMStub.h/cpp)
+
+BOOL WINAPI MonitorBounds (HMONITOR, RECT*);
+
#endif _DEVICES_H_
diff --git a/src/windows/native/sun/windows/GDIHashtable.cpp b/src/windows/native/sun/windows/GDIHashtable.cpp
index a3bc8ebe7..25c0dac45 100644
--- a/src/windows/native/sun/windows/GDIHashtable.cpp
+++ b/src/windows/native/sun/windows/GDIHashtable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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,7 +25,6 @@
#include "GDIHashtable.h"
#include "awt_GDIObject.h"
-#include "awt_dlls.h"
GDIHashtable::BatchDestructionManager GDIHashtable::manager;
@@ -46,7 +45,6 @@ void GDIHashtable::release(void* key) {
DASSERT(value != NULL);
m_deleteProc(value);
}
- manager.update();
}
void GDIHashtable::flush() {
@@ -128,9 +126,6 @@ void GDIHashtable::List::clear() {
}
}
-#undef GFSR_GDIRESOURCES
-#define GFSR_GDIRESOURCES 0x0001
-
GDIHashtable::BatchDestructionManager::BatchDestructionManager(UINT nFirstThreshold,
UINT nSecondThreshold,
UINT nDestroyPeriod) :
@@ -138,48 +133,6 @@ GDIHashtable::BatchDestructionManager::BatchDestructionManager(UINT nFirstThresh
m_nSecondThreshold(nSecondThreshold),
m_nDestroyPeriod(nDestroyPeriod),
m_nCounter(0),
- m_bBatchingEnabled(TRUE) {
- load_rsrc32_procs();
-}
-
-void GDIHashtable::BatchDestructionManager::update() {
-
- if (get_free_system_resources != NULL) {
-
- CriticalSection::Lock l(m_managerLock);
-
- if (m_nCounter < 0) {
- UINT nFreeResources = (*get_free_system_resources)(GFSR_GDIRESOURCES);
- /*
- * If m_bBatchingEnabled is FALSE there is no need
- * to flush since we have been destroying all
- * GDI resources as soon as they were released.
- */
- if (m_bBatchingEnabled) {
- if (nFreeResources < m_nFirstThreshold) {
- flushAll();
- nFreeResources = (*get_free_system_resources)(GFSR_GDIRESOURCES);
- }
- }
- if (nFreeResources < m_nSecondThreshold) {
- m_bBatchingEnabled = FALSE;
- m_nCounter = m_nDestroyPeriod;
- } else {
- m_bBatchingEnabled = TRUE;
- /*
- * The frequency of checks must depend on the currect amount
- * of free space in GDI heaps. Otherwise we can run into the
- * Resource Meter warning dialog when GDI resources are low.
- * This is a heuristic rule that provides this dependency.
- * These numbers have been chosen because:
- * Resource Meter posts a warning dialog when less than 10%
- * of GDI resources are free.
- * 5 pens/brushes take 1%. So 3 is the upper bound.
- * When changing this rule you should check that performance
- * isn't affected (with Caffeine Mark and JMark).
- */
- m_nCounter = (nFreeResources - 10) * 3;
- }
- }
- }
+ m_bBatchingEnabled(TRUE)
+{
}
diff --git a/src/windows/native/sun/windows/GDIHashtable.h b/src/windows/native/sun/windows/GDIHashtable.h
index 406a59e23..a324fd97e 100644
--- a/src/windows/native/sun/windows/GDIHashtable.h
+++ b/src/windows/native/sun/windows/GDIHashtable.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -151,12 +151,6 @@ class GDIHashtable : public Hashtable {
*/
INLINE void decrementCounter() { m_nCounter--; }
- /**
- * Depending on the amount of free space in GDI heaps flushes
- * all GDIHashtables and sets the initial counter value.
- */
- void update();
-
INLINE CriticalSection& getLock() { return m_managerLock; }
};
diff --git a/src/windows/native/sun/windows/ShellFolder2.cpp b/src/windows/native/sun/windows/ShellFolder2.cpp
index 7ed24aadb..3baf5bcf9 100644
--- a/src/windows/native/sun/windows/ShellFolder2.cpp
+++ b/src/windows/native/sun/windows/ShellFolder2.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2008 Sun Microsystems, Inc. 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
@@ -31,17 +31,27 @@
// This file should stand independent of AWT and should ultimately be
// put into its own DLL.
#include <awt.h>
-#endif
+#else
+// Include jni_util.h first, so JNU_* macros can be redefined
+#include "jni_util.h"
+// Borrow some macros from awt.h
+#define JNU_NewStringPlatform(env, x) env->NewString(reinterpret_cast<jchar*>(x), static_cast<jsize>(_tcslen(x)))
+#define JNU_GetStringPlatformChars(env, x, y) reinterpret_cast<LPCWSTR>(env->GetStringChars(x, y))
+#define JNU_ReleaseStringPlatformChars(env, x, y) env->ReleaseStringChars(x, reinterpret_cast<const jchar*>(y))
+#endif // DEBUG
#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
-#include "jni_util.h"
#include "jlong.h"
#include "alloc.h"
#include "stdhdrs.h"
-#include "UnicowsLoader.h"
+
+// Copy from shlguid.h which is no longer in PlatformSDK
+#ifndef DEFINE_SHLGUID
+#define DEFINE_SHLGUID(name, l, w1, w2) DEFINE_GUID(name, l, w1, w2, 0xC0, 0, 0, 0, 0, 0, 0, 0x46)
+#endif
// {93F2F68C-1D1B-11d3-A30E-00C04F79ABD1}
DEFINE_GUID(IID_IShellFolder2, 0x93f2f68c, 0x1d1b, 0x11d3, 0xa3, 0xe, 0x0, 0xc0, 0x4f, 0x79, 0xab, 0xd1);
@@ -86,13 +96,15 @@ static jfieldID FID_folderType;
static IMalloc* pMalloc;
static IShellFolder* pDesktop;
-static BOOL isXP;
-
-// copied from awt.h, because it is not included in release
-#if defined (WIN32)
- #define IS_WINVISTA (!(::GetVersion() & 0x80000000) && LOBYTE(LOWORD(::GetVersion())) >= 6)
-#else
- #define IS_WINVISTA FALSE
+// Some macros from awt.h, because it is not included in release
+#ifndef IS_WIN2000
+#define IS_WIN2000 (LOBYTE(LOWORD(::GetVersion())) >= 5)
+#endif
+#ifndef IS_WINXP
+#define IS_WINXP ((IS_WIN2000 && HIBYTE(LOWORD(::GetVersion())) >= 1) || LOBYTE(LOWORD(::GetVersion())) > 5)
+#endif
+#ifndef IS_WINVISTA
+#define IS_WINVISTA (!(::GetVersion() & 0x80000000) && LOBYTE(LOWORD(::GetVersion())) >= 6)
#endif
@@ -103,7 +115,6 @@ static BOOL initShellProcs()
static HMODULE libShell32 = NULL;
static HMODULE libUser32 = NULL;
static HMODULE libComCtl32 = NULL;
- static HMODULE libUnicows = UnicowsLoader::GetModuleHandle();
// If already initialized, return TRUE
if (libShell32 != NULL && libUser32 != NULL) {
return TRUE;
@@ -130,7 +141,7 @@ static BOOL initShellProcs()
// Set up procs - libShell32
fn_FindExecutable = (FindExecutableType)GetProcAddress(
- (libUnicows ? libUnicows : libShell32), "FindExecutableW");
+ libShell32, "FindExecutableW");
if (fn_FindExecutable == NULL) {
return FALSE;
}
@@ -140,7 +151,7 @@ static BOOL initShellProcs()
return FALSE;
}
fn_SHGetFileInfo = (SHGetFileInfoType)GetProcAddress(
- (libUnicows ? libUnicows : libShell32), "SHGetFileInfoW");
+ libShell32, "SHGetFileInfoW");
if (fn_SHGetFileInfo == NULL) {
return FALSE;
}
@@ -154,7 +165,7 @@ static BOOL initShellProcs()
return FALSE;
}
fn_SHGetPathFromIDList = (SHGetPathFromIDListType)GetProcAddress(
- (libUnicows ? libUnicows : libShell32), "SHGetPathFromIDListW");
+ libShell32, "SHGetPathFromIDListW");
if (fn_SHGetPathFromIDList == NULL) {
return FALSE;
}
@@ -181,19 +192,19 @@ static BOOL initShellProcs()
static jstring jstringFromSTRRET(JNIEnv* env, LPITEMIDLIST pidl, STRRET* pStrret) {
switch (pStrret->uType) {
case STRRET_CSTR :
- return JNU_NewStringPlatform(env, pStrret->cStr);
+ return JNU_NewStringPlatform(env, reinterpret_cast<const char*>(pStrret->cStr));
case STRRET_OFFSET :
// Note : this may need to be WCHAR instead
return JNU_NewStringPlatform(env,
(CHAR*)pidl + pStrret->uOffset);
case STRRET_WSTR :
- return env->NewString(pStrret->pOleStr,
+ return env->NewString(reinterpret_cast<const jchar*>(pStrret->pOleStr),
static_cast<jsize>(wcslen(pStrret->pOleStr)));
}
return NULL;
}
// restoring the original definition
-#define JNU_NewStringPlatform(env, x) env->NewString(x, static_cast<jsize>(_tcslen(x)))
+#define JNU_NewStringPlatform(env, x) env->NewString(reinterpret_cast<jchar*>(x), static_cast<jsize>(_tcslen(x)))
/*
* Class: sun_awt_shell_Win32ShellFolder2
@@ -212,13 +223,6 @@ JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolder2_initIDs
MID_relativePIDL = env->GetMethodID(cls, "setRelativePIDL", "(J)V");
FID_displayName = env->GetFieldID(cls, "displayName", "Ljava/lang/String;");
FID_folderType = env->GetFieldID(cls, "folderType", "Ljava/lang/String;");
-
- // Find out if we are on XP or later
- long version = GetVersion();
- isXP = (!(version & 0x80000000) &&
- (LOBYTE(LOWORD(version)) == 5 &&
- HIBYTE(LOWORD(version)) >= 1) ||
- LOBYTE(LOWORD(version)) > 5);
}
static IShellIcon* getIShellIcon(IShellFolder* pIShellFolder) {
@@ -669,46 +673,24 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation
if (!CoInit(doCoUninit)) {
return 0;
}
- if (IS_NT) {
- IShellLinkW* psl;
- hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&psl);
- if (SUCCEEDED(hres)) {
- IPersistFile* ppf;
- hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
- if (SUCCEEDED(hres)) {
- hres = ppf->Load(wstr, STGM_READ);
- if (SUCCEEDED(hres)) {
- if (resolve) {
- hres = psl->Resolve(NULL, 0);
- // Ignore failure
- }
- pidl = (LPITEMIDLIST)NULL;
- hres = psl->GetIDList(&pidl);
- }
- ppf->Release();
- }
- psl->Release();
- }
- } else {
- IShellLinkA* psl;
- hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, (LPVOID *)&psl);
+ IShellLinkW* psl;
+ hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&psl);
+ if (SUCCEEDED(hres)) {
+ IPersistFile* ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
if (SUCCEEDED(hres)) {
- IPersistFile* ppf;
- hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
+ hres = ppf->Load(wstr, STGM_READ);
if (SUCCEEDED(hres)) {
- hres = ppf->Load(wstr, STGM_READ);
- if (SUCCEEDED(hres)) {
- if (resolve) {
- hres = psl->Resolve(NULL, 0);
- // Ignore failure
- }
- pidl = (LPITEMIDLIST)NULL;
- hres = psl->GetIDList(&pidl);
+ if (resolve) {
+ hres = psl->Resolve(NULL, 0);
+ // Ignore failure
}
- ppf->Release();
+ pidl = (LPITEMIDLIST)NULL;
+ hres = psl->GetIDList(&pidl);
}
- psl->Release();
+ ppf->Release();
}
+ psl->Release();
}
if (doCoUninit) {
::CoUninitialize();
@@ -742,10 +724,10 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_parseDisplayName0
int nLength = env->GetStringLength(jname);
jchar* wszPath = new jchar[nLength + 1];
const jchar* strPath = env->GetStringChars(jname, NULL);
- wcsncpy(wszPath, strPath, nLength);
+ wcsncpy(reinterpret_cast<LPWSTR>(wszPath), reinterpret_cast<LPCWSTR>(strPath), nLength);
wszPath[nLength] = 0;
HRESULT res = pIShellFolder->ParseDisplayName(NULL, NULL,
- const_cast<jchar*>(wszPath), NULL, &pIDL, NULL);
+ reinterpret_cast<LPWSTR>(wszPath), NULL, &pIDL, NULL);
if (res != S_OK) {
JNU_ThrowIOException(env, "Could not parse name");
pIDL = 0;
@@ -804,7 +786,7 @@ JNIEXPORT jstring JNICALL Java_sun_awt_shell_Win32ShellFolder2_getExecutableType
(JNIEnv* env, jobject folder, jstring path)
{
TCHAR szBuf[MAX_PATH];
- LPCTSTR szPath = (LPCTSTR)JNU_GetStringPlatformChars(env, path, NULL);
+ LPCTSTR szPath = JNU_GetStringPlatformChars(env, path, NULL);
if (szPath == NULL) {
return NULL;
}
@@ -827,7 +809,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIcon
{
HICON hIcon = NULL;
SHFILEINFO fileInfo;
- LPCTSTR pathStr = (LPCTSTR)JNU_GetStringPlatformChars(env, absolutePath, NULL);
+ LPCTSTR pathStr = JNU_GetStringPlatformChars(env, absolutePath, NULL);
if (fn_SHGetFileInfo(pathStr, 0L, &fileInfo, sizeof(fileInfo),
SHGFI_ICON | (getLargeIcon ? 0 : SHGFI_SMALLICON)) != 0) {
hIcon = fileInfo.hIcon;
@@ -890,52 +872,27 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon
}
HRESULT hres;
- if (IS_NT) {
- IExtractIconW* pIcon;
- hres = pIShellFolder->GetUIObjectOf(NULL, 1, const_cast<LPCITEMIDLIST*>(&pidl),
+ IExtractIconW* pIcon;
+ hres = pIShellFolder->GetUIObjectOf(NULL, 1, const_cast<LPCITEMIDLIST*>(&pidl),
IID_IExtractIconW, NULL, (void**)&pIcon);
+ if (SUCCEEDED(hres)) {
+ WCHAR szBuf[MAX_PATH];
+ INT index;
+ UINT flags;
+ hres = pIcon->GetIconLocation(GIL_FORSHELL, szBuf, MAX_PATH, &index, &flags);
if (SUCCEEDED(hres)) {
- WCHAR szBuf[MAX_PATH];
- INT index;
- UINT flags;
- hres = pIcon->GetIconLocation(GIL_FORSHELL, szBuf, MAX_PATH, &index, &flags);
- if (SUCCEEDED(hres)) {
- HICON hIconLarge;
- hres = pIcon->Extract(szBuf, index, &hIconLarge, &hIcon, (16 << 16) + 32);
- if (SUCCEEDED(hres)) {
- if (getLargeIcon) {
- fn_DestroyIcon((HICON)hIcon);
- hIcon = hIconLarge;
- } else {
- fn_DestroyIcon((HICON)hIconLarge);
- }
- }
- }
- pIcon->Release();
- }
- } else {
- IExtractIconA* pIcon;
- hres = pIShellFolder->GetUIObjectOf(NULL, 1, const_cast<LPCITEMIDLIST*>(&pidl),
- IID_IExtractIconA, NULL, (void**)&pIcon);
- if (SUCCEEDED(hres)) {
- CHAR szBuf[MAX_PATH];
- INT index;
- UINT flags;
- hres = pIcon->GetIconLocation(GIL_FORSHELL, szBuf, MAX_PATH, &index, &flags);
+ HICON hIconLarge;
+ hres = pIcon->Extract(szBuf, index, &hIconLarge, &hIcon, (16 << 16) + 32);
if (SUCCEEDED(hres)) {
- HICON hIconLarge;
- hres = pIcon->Extract(szBuf, index, &hIconLarge, &hIcon, (16 << 16) + 32);
- if (SUCCEEDED(hres)) {
- if (getLargeIcon) {
- fn_DestroyIcon((HICON)hIcon);
- hIcon = hIconLarge;
- } else {
- fn_DestroyIcon((HICON)hIconLarge);
- }
+ if (getLargeIcon) {
+ fn_DestroyIcon((HICON)hIcon);
+ hIcon = hIconLarge;
+ } else {
+ fn_DestroyIcon((HICON)hIconLarge);
}
}
- pIcon->Release();
}
+ pIcon->Release();
}
if (doCoUninit) {
::CoUninitialize();
@@ -987,7 +944,7 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits
// XP supports alpha in some icons, and depending on device.
// This should take precedence over the icon mask bits.
BOOL hasAlpha = FALSE;
- if (isXP) {
+ if (IS_WINXP) {
for (int i = 0; i < nBits; i++) {
if ((colorBits[i] & 0xff000000) != 0) {
hasAlpha = TRUE;
@@ -1127,9 +1084,9 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconResource
(JNIEnv* env, jclass cls, jstring libName, jint iconID,
jint cxDesired, jint cyDesired, jboolean useVGAColors)
{
- HINSTANCE libHandle = LoadLibrary(env->GetStringChars(libName, NULL));
+ HINSTANCE libHandle = LoadLibrary(JNU_GetStringPlatformChars(env, libName, NULL));
if (libHandle != NULL) {
- UINT fuLoad = (useVGAColors && !isXP) ? LR_VGACOLOR : 0;
+ UINT fuLoad = (useVGAColors && !IS_WINXP) ? LR_VGACOLOR : 0;
return ptr_to_jlong(LoadImage(libHandle, MAKEINTRESOURCE(iconID),
IMAGE_ICON, cxDesired, cyDesired,
fuLoad));
diff --git a/src/windows/native/sun/windows/UnicowsLoader.cpp b/src/windows/native/sun/windows/UnicowsLoader.cpp
deleted file mode 100644
index 7e147e209..000000000
--- a/src/windows/native/sun/windows/UnicowsLoader.cpp
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright 2003-2005 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-#include <float.h>
-#include "alloc.h"
-#include "UnicowsLoader.h"
-
-/*
- * Support functions for the Microsoft Layer for Unicode (MSLU).
- *
- * The MSLU maps the wide char version of Windows APIs with strings
- * to their ANSI version equivalent on Win98/ME platforms.
- *
- * For more details on the MSLU, please refer to the MSDN webpage at:
- * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/microsoft_layer_for_unicode_on_windows_95_98_me_systems.asp
- */
-
-// The MSLU module handle. Only initialized on Win9x/ME.
-HMODULE UnicowsLoader::hmodUnicows = NULL;
-
-// MSLU loader entry point, which is called when the module
-// is initialized.
-extern "C" HMODULE (__stdcall *_PfnLoadUnicows)(void) =
- &UnicowsLoader::LoadUnicows;
-
-// Overriede APIs that are not supported by MSLU.
-extern "C" FARPROC Unicows_GetPrinterW =
- (FARPROC)&UnicowsLoader::GetPrinterWImpl;
-extern "C" FARPROC Unicows_EnumPrintersW =
- (FARPROC)&UnicowsLoader::EnumPrintersWImpl;
-
-HMODULE __stdcall UnicowsLoader::LoadUnicows(void)
-{
- if (hmodUnicows != NULL) {
- return hmodUnicows;
- }
-
- // Unfortunately, some DLLs that are loaded in conjunction with
- // unicows.dll may blow the FPU's control word. So save it here.
- unsigned int fpu_cw = _CW_DEFAULT;
- fpu_cw = _control87(0, 0);
-
- // Loads the DLL, assuming that the DLL resides in the same directory
- // as the AWT(_G).DLL. We cannot use "sun.boot.library.path" system
- // property since there is no way to issue JNI calls at this point
- // (JNI_OnLoad is not yet called so it cannot obtain JavaVM structure)
- //
- // To obtain the AWT module handle, call GetModuleHandleA() directly,
- // instead of AwtToolkit.GetModuleHandle(). Otherwise it could cause
- // an infinite loop if some W call were made inside AwtToolkit class
- // initialization.
- HMODULE hmodAWT = GetModuleHandleA("awt");
- LPSTR abspath = (LPSTR)safe_Malloc(MAX_PATH);
- if (abspath != NULL) {
- GetModuleFileNameA(hmodAWT, abspath, MAX_PATH);
- *strrchr(abspath, '\\') = '\0';
- strcat(abspath, "\\unicows.dll");
- hmodUnicows = LoadLibraryA(abspath);
- free(abspath);
- }
-
- // Restore the FPU control word if needed.
- if ( _control87(0, 0) != fpu_cw) {
- _control87(fpu_cw, 0xfffff);
- }
-
- return hmodUnicows;
-}
-
-HMODULE UnicowsLoader::GetModuleHandle(void)
-{
- return hmodUnicows;
-}
-
-
-// Convenient functions to convert DEVMODEA -> DEVMODEW
-void UnicowsLoader::DevModeA2DevModeW(
- const DEVMODEA * dma,
- DEVMODEW * dmw)
-{
- // convert string portions
- ::MultiByteToWideChar(CP_ACP, 0, (CHAR *)dma->dmDeviceName, CCHDEVICENAME,
- dmw->dmDeviceName, CCHDEVICENAME);
- ::MultiByteToWideChar(CP_ACP, 0, (CHAR *)dma->dmFormName, CCHDEVICENAME,
- dmw->dmFormName, CCHDEVICENAME);
-
- // copy driver specific data if exists
- if (dma->dmDriverExtra != 0) {
- PBYTE pExtraA = (PBYTE)(dma + 1);
- PBYTE pExtraW = (PBYTE)(dmw + 1);
- memcpy(pExtraW, pExtraA, dma->dmDriverExtra);
- }
-
- // copy normal struct members
- dmw->dmSpecVersion = dma->dmSpecVersion;
- dmw->dmDriverVersion = dma->dmDriverVersion;
- dmw->dmSize = dma->dmSize;
- dmw->dmDriverExtra = dma->dmDriverExtra;
- dmw->dmFields = dma->dmFields;
- dmw->dmPosition = dma->dmPosition;
- dmw->dmScale = dma->dmScale;
- dmw->dmCopies = dma->dmCopies;
- dmw->dmDefaultSource = dma->dmDefaultSource;
- dmw->dmPrintQuality = dma->dmPrintQuality;
- dmw->dmColor = dma->dmColor;
- dmw->dmDuplex = dma->dmDuplex;
- dmw->dmYResolution = dma->dmYResolution;
- dmw->dmTTOption = dma->dmTTOption;
- dmw->dmCollate = dma->dmCollate;
- dmw->dmLogPixels = dma->dmLogPixels;
- dmw->dmBitsPerPel = dma->dmBitsPerPel;
- dmw->dmPelsWidth = dma->dmPelsWidth;
- dmw->dmPelsHeight = dma->dmPelsHeight;
- dmw->dmDisplayFlags = dma->dmDisplayFlags;
- dmw->dmDisplayFrequency = dma->dmDisplayFrequency;
-#if(WINVER >= 0x0400)
- dmw->dmICMMethod = dma->dmICMMethod;
- dmw->dmICMIntent = dma->dmICMIntent;
- dmw->dmMediaType = dma->dmMediaType;
- dmw->dmDitherType = dma->dmDitherType;
- dmw->dmReserved1 = dma->dmReserved1;
- dmw->dmReserved2 = dma->dmReserved2;
-#if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400)
- dmw->dmPanningWidth = dma->dmPanningWidth;
- dmw->dmPanningHeight = dma->dmPanningHeight;
-#endif
-#endif /* WINVER >= 0x0400 */
-}
-
-// PRINTER_INFO_1 struct converter
-void UnicowsLoader::PrinterInfo1A2W(
- const LPPRINTER_INFO_1A pi1A,
- LPPRINTER_INFO_1W pi1W,
- const DWORD num)
-{
- LPWSTR pwstrbuf = (LPWSTR)(pi1W + num);
- DWORD current;
-
- // loop through all structures
- for (current = 0; current < num; current ++) {
- LPPRINTER_INFO_1A curPi1A = pi1A + current;
- LPPRINTER_INFO_1W curPi1W = pi1W + current;
-
- // copy the structure itself
- memcpy(curPi1W, curPi1A, sizeof(_PRINTER_INFO_1W));
-
- // copy string members
- StringA2W(curPi1A->pDescription, &(curPi1W->pDescription), &pwstrbuf);
- StringA2W(curPi1A->pName, &(curPi1W->pName), &pwstrbuf);
- StringA2W(curPi1A->pComment, &(curPi1W->pComment), &pwstrbuf);
- }
-}
-
-// PRINTER_INFO_2 struct converter
-void UnicowsLoader::PrinterInfo2A2W(
- const LPPRINTER_INFO_2A pi2A,
- LPPRINTER_INFO_2W pi2W,
- const DWORD num)
-{
- PBYTE pbytebuf = (PBYTE)(pi2W + num);
- DWORD current;
-
- // loop through all structures
- for (current = 0; current < num; current ++) {
- LPPRINTER_INFO_2A curPi2A = pi2A + current;
- LPPRINTER_INFO_2W curPi2W = pi2W + current;
- // copy the structure itself
- memcpy(curPi2W, curPi2A, sizeof(_PRINTER_INFO_2W));
-
- // copy string members
- StringA2W(curPi2A->pServerName, &(curPi2W->pServerName), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pPrinterName, &(curPi2W->pPrinterName), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pShareName, &(curPi2W->pShareName), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pPortName, &(curPi2W->pPortName), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pDriverName, &(curPi2W->pDriverName), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pComment, &(curPi2W->pComment), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pLocation, &(curPi2W->pLocation), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pSepFile, &(curPi2W->pSepFile), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pPrintProcessor, &(curPi2W->pPrintProcessor), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pDatatype, &(curPi2W->pDatatype), (LPWSTR *)&pbytebuf);
- StringA2W(curPi2A->pParameters, &(curPi2W->pParameters), (LPWSTR *)&pbytebuf);
-
- // copy DEVMODE structure
- if (curPi2A->pDevMode != NULL) {
- curPi2W->pDevMode = (LPDEVMODEW)pbytebuf;
- DevModeA2DevModeW(curPi2A->pDevMode, curPi2W->pDevMode);
- pbytebuf += sizeof(DEVMODEW) + curPi2A->pDevMode->dmDriverExtra;
- }
- }
-}
-
-// PRINTER_INFO_5 struct converter
-void UnicowsLoader::PrinterInfo5A2W(
- const LPPRINTER_INFO_5A pi5A,
- LPPRINTER_INFO_5W pi5W,
- const DWORD num)
-{
- LPWSTR pbuf = (LPWSTR)(pi5W + num);
- DWORD current;
-
- // loop through all structures
- for (current = 0; current < num; current ++) {
- LPPRINTER_INFO_5A curPi5A = pi5A + current;
- LPPRINTER_INFO_5W curPi5W = pi5W + current;
-
- // copy the structure itself
- memcpy(curPi5W, curPi5A, sizeof(_PRINTER_INFO_5W));
-
- // copy string members
- StringA2W(curPi5A->pPrinterName, &(curPi5W->pPrinterName), &pbuf);
- StringA2W(curPi5A->pPortName, &(curPi5W->pPortName), &pbuf);
- }
-}
-
-// PRINTER_INFO_* struct converter. Supported levels are 1, 2, and 5.
-void UnicowsLoader::PrinterInfoA2W(
- const PVOID piA,
- PVOID piW,
- const DWORD Level,
- const DWORD num)
-{
- switch (Level) {
- case 1:
- PrinterInfo1A2W((LPPRINTER_INFO_1A)piA, (LPPRINTER_INFO_1W)piW, num);
- break;
-
- case 2:
- PrinterInfo2A2W((LPPRINTER_INFO_2A)piA, (LPPRINTER_INFO_2W)piW, num);
- break;
-
- case 5:
- PrinterInfo5A2W((LPPRINTER_INFO_5A)piA, (LPPRINTER_INFO_5W)piW, num);
- break;
- }
-}
-
-// converts string members in PRINTER_INFO_* struct.
-void UnicowsLoader::StringA2W(
- LPCSTR pSrcA,
- LPWSTR * ppwstrDest,
- LPWSTR * ppwstrbuf)
-{
- if (pSrcA != NULL) {
- DWORD cchWideChar = ::MultiByteToWideChar(CP_ACP, 0, pSrcA, -1, NULL, 0);
- *ppwstrDest = *ppwstrbuf;
- ::MultiByteToWideChar(CP_ACP, 0, pSrcA, -1, *ppwstrbuf, cchWideChar);
- *ppwstrbuf += cchWideChar;
- } else {
- *ppwstrDest = NULL;
- }
-}
-
-// GetPrinterW implementation. Level 1, 2, and 5 are the only supported levels.
-BOOL __stdcall UnicowsLoader::GetPrinterWImpl(
- HANDLE hPrinter,
- DWORD Level,
- LPBYTE pPrinter,
- DWORD cbBuf,
- LPDWORD pcbNeeded)
-{
- if ((Level != 1) && (Level != 2) && (Level != 5)) {
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
- }
-
- DWORD cbBufA = (cbBuf != 0 ? cbBuf / 2 : 0); // dirty estimation...
- LPBYTE pPrinterA = NULL;
- DWORD cbNeededA = 0;
- BOOL ret;
-
- if (cbBufA != 0) {
- pPrinterA = (LPBYTE)safe_Malloc(cbBufA);
- memset(pPrinterA, 0, cbBufA);
- }
-
- ret = ::GetPrinterA(hPrinter, Level, pPrinterA, cbBufA, &cbNeededA);
- *pcbNeeded = cbNeededA * 2; // dirty estimation...
-
- if (pPrinterA != NULL) {
- if (ret) {
- PrinterInfoA2W(pPrinterA, pPrinter, Level, 1);
- }
- free(pPrinterA);
- }
-
- return ret;
-}
-
-// EnumPrintersW implementation. Level 1, 2, and 5 are the only supported levels.
-BOOL __stdcall UnicowsLoader::EnumPrintersWImpl(
- DWORD Flags,
- LPWSTR Name,
- DWORD Level,
- LPBYTE pPrinterEnum,
- DWORD cbBuf,
- LPDWORD pcbNeeded,
- LPDWORD pcReturned)
-{
- if ((Level != 1) && (Level != 2) && (Level != 5)) {
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
- }
-
- LPSTR pNameA = NULL;
- DWORD cbBufA = (cbBuf != 0 ? cbBuf / 2 : 0); // dirty estimation...
- LPBYTE pPrinterEnumA = NULL;
- DWORD cbNeededA = 0;
- BOOL ret;
-
- if (Name != NULL) {
- DWORD len = static_cast<DWORD>(wcslen(Name)) + 1;
- pNameA = (LPSTR)safe_Malloc(len);
- ::WideCharToMultiByte(CP_ACP, 0, Name, -1, pNameA, len, NULL, NULL);
- }
-
- if (cbBufA != 0) {
- pPrinterEnumA = (LPBYTE)safe_Malloc(cbBufA);
- memset(pPrinterEnumA, 0, cbBufA);
- }
-
- ret = ::EnumPrintersA(Flags, pNameA, Level, pPrinterEnumA,
- cbBufA, &cbNeededA, pcReturned);
- *pcbNeeded = cbNeededA * 2; // dirty estimation...
-
- if (pPrinterEnumA != NULL) {
- if (ret) {
- PrinterInfoA2W(pPrinterEnumA, pPrinterEnum, Level, *pcReturned);
- }
- free(pPrinterEnumA);
- }
-
- if (pNameA != NULL) {
- free(pNameA);
- }
-
- return ret;
-}
-
-// wchar CRT implementations that VC6 does not support on Win9x.
-// These implementations are used on both Win9x/ME *and* WinNT/2K/XP.
-#undef _waccess
-#undef _wchmod
-#undef _wfullpath
-#undef _wremove
-#undef _wrename
-#undef _wstat
-#undef _wstati64
-#undef _wstat64
-#undef _wunlink
-#undef _wfopen
-#undef _wfreopen
-#undef _wfsopen
-#undef _wcreat
-#undef _wopen
-#undef _wsopen
-#undef _wfindfirst
-#undef _wfindfirst64
-#undef _wfindnext
-#undef _wfindnext64
-#undef _wsystem
-#undef _wexcel
-#undef _wexcele
-#undef _wexelp
-#undef _wexelpe
-#undef _wexecv
-#undef _wexecve
-#undef _wexecvp
-#undef _wexecvpe
-#undef _wpopen
-#undef _wputenv
-#undef _wspawnl
-#undef _wspawnle
-#undef _wspawnlp
-#undef _wspawnlpe
-#undef _wspawnv
-#undef _wspawnve
-#undef _wspawnvp
-#undef _wspawnvpe
-
-// _wfullpath implementation
-wchar_t * __cdecl UnicowsLoader::_wfullpathImpl(
- wchar_t * absPath,
- const wchar_t * relPath,
- size_t maxLength)
-{
- if (IS_NT) {
- return _wfullpath(absPath, relPath, maxLength);
- } else {
- wchar_t * ret = NULL;
- char * absPathA = (char *)safe_Malloc(maxLength);
- char * relPathA = (char *)safe_Malloc(maxLength);
- ::WideCharToMultiByte(CP_ACP, 0, relPath, -1, relPathA,
- static_cast<DWORD>(maxLength), NULL, NULL);
-
- char * retA = _fullpath(absPathA, relPathA, maxLength);
-
- if (retA != NULL) {
- ::MultiByteToWideChar(CP_ACP, 0, absPathA, -1,
- absPath, static_cast<DWORD>(maxLength));
- ret = absPath;
- }
-
- free(absPathA);
- free(relPathA);
-
- return ret;
- }
-}
diff --git a/src/windows/native/sun/windows/UnicowsLoader.h b/src/windows/native/sun/windows/UnicowsLoader.h
deleted file mode 100644
index 50c48ca91..000000000
--- a/src/windows/native/sun/windows/UnicowsLoader.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2003-2004 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-#ifndef UNICOWSLOADER_H
-#define UNICOWSLOADER_H
-
-#if !defined(UNICODE) || !defined(_UNICODE)
-#error UnicowsLoader module needs UNICODE and _UNICODE flags to be set on compiling
-#endif
-
-#include <winspool.h>
-
-// A class to load the Microsoft Layer for Unicode (unicows.dll)
-class UnicowsLoader {
-public:
- // this is called when the client DLL (this case, AWT) is loaded
- static HMODULE __stdcall LoadUnicows(void);
-
- // this is provided to pass the MSLU module handle
- static HMODULE GetModuleHandle(void);
-
- // member functions that implements functions that MSLU does not support
- static BOOL __stdcall GetPrinterWImpl(HANDLE, DWORD, LPBYTE, DWORD, LPDWORD);
- static BOOL __stdcall EnumPrintersWImpl(DWORD, LPWSTR, DWORD, LPBYTE,
- DWORD, LPDWORD, LPDWORD);
-
- // member functions that implements functions that VC6 CRT does not support
- // on Win9x
- static wchar_t * __cdecl _wfullpathImpl(wchar_t *, const wchar_t *, size_t);
-
-private:
- // The module handle
- static HMODULE hmodUnicows;
-
- // utility member functions
- static void DevModeA2DevModeW(const DEVMODEA *, DEVMODEW *);
- static void PrinterInfo1A2W(const LPPRINTER_INFO_1A, LPPRINTER_INFO_1W, const DWORD);
- static void PrinterInfo2A2W(const LPPRINTER_INFO_2A, LPPRINTER_INFO_2W, const DWORD);
- static void PrinterInfo5A2W(const LPPRINTER_INFO_5A, LPPRINTER_INFO_5W, const DWORD);
- static void PrinterInfoA2W(const PVOID, PVOID, DWORD, DWORD);
- static void StringA2W(LPCSTR, LPWSTR *, LPWSTR *);
-};
-
-#ifndef AWT_H
-// copied from awt.h
-#if defined (WIN32)
- #define IS_WIN32 TRUE
-#else
- #define IS_WIN32 FALSE
-#endif
-#define IS_NT (IS_WIN32 && !(::GetVersion() & 0x80000000))
-#endif // AWT_H
-
-// Now the platform encoding is Unicode (UTF-16), re-define JNU_ functions
-// to proper JNI functions.
-#define JNU_NewStringPlatform(env, x) env->NewString(x, static_cast<jsize>(_tcslen(x)))
-#define JNU_GetStringPlatformChars(env, x, y) (LPWSTR)env->GetStringChars(x, y)
-#define JNU_ReleaseStringPlatformChars(env, x, y) env->ReleaseStringChars(x, y)
-
-// The following Windows W-APIs are not supported by the MSLU.
-// You need to implement a stub to use these APIs. Or, if it is
-// apparent that the API is used only on WindowsNT/2K/XP, wrap
-// the call site with #undef - #define, e.g:
-//
-// #undef SomeFunctionW
-// call SomeFunctionW
-// #define SomeFunctionW NotSupportedByMSLU
-
-#define AcquireCredentialsHandleW NotSupportedByMSLU
-#define CreateNamedPipeW NotSupportedByMSLU
-#define CryptAcquireContextW NotSupportedByMSLU
-#define CryptEnumProvidersW NotSupportedByMSLU
-#define CryptEnumProviderTypesW NotSupportedByMSLU
-#define CryptGetDefaultProviderW NotSupportedByMSLU
-#define CryptSetProviderW NotSupportedByMSLU
-#define CryptSetProviderExW NotSupportedByMSLU
-#define CryptSignHashW NotSupportedByMSLU
-#define CryptVerifySignatureW NotSupportedByMSLU
-#define EnumerateSecurityPackagesW NotSupportedByMSLU
-#define EnumMonitorsW NotSupportedByMSLU
-#define EnumPortsW NotSupportedByMSLU
-#define EnumPrinterDriversW NotSupportedByMSLU
-//#define EnumPrintersW NotSupportedByMSLU
-#define EnumPrintProcessorDatatypesW NotSupportedByMSLU
-#define EnumPrintProcessorsW NotSupportedByMSLU
-#define FreeContextBufferW NotSupportedByMSLU
-#define GetCharABCWidthsFloatW NotSupportedByMSLU
-#define GetJobW NotSupportedByMSLU
-#define GetOpenFileNamePreviewW NotSupportedByMSLU
-//#define GetPrinterW NotSupportedByMSLU
-#define GetPrinterDataW NotSupportedByMSLU
-#define GetPrinterDriverW NotSupportedByMSLU
-#define GetSaveFileNamePreviewW NotSupportedByMSLU
-#define InitializeSecurityContextW NotSupportedByMSLU
-#define mciSendCommandW NotSupportedByMSLU
-#define mixerGetControlDetailsW NotSupportedByMSLU
-#define mixerGetLineControlsW NotSupportedByMSLU
-#define mixerGetLineInfoW NotSupportedByMSLU
-#define mmioInstallIOProcW NotSupportedByMSLU
-#define OleUIChangeSourceW NotSupportedByMSLU
-#define OleUIConvertW NotSupportedByMSLU
-#define OleUIEditLinksW NotSupportedByMSLU
-#define OleUIInsertObjectW NotSupportedByMSLU
-#define OleUIObjectPropertiesW NotSupportedByMSLU
-#define OleUIPasteSpecialW NotSupportedByMSLU
-#define OleUIPromptUserW NotSupportedByMSLU
-#define OleUIUpdateLinksW NotSupportedByMSLU
-#define PolyTextOutW NotSupportedByMSLU
-#define QueryContextAttributesW NotSupportedByMSLU
-#define QueryCredentialsAttributesW NotSupportedByMSLU
-#define QuerySecurityPackageInfoW NotSupportedByMSLU
-#define RasDeleteSubEntryW NotSupportedByMSLU
-#define RasSetSubEntryPropertiesW NotSupportedByMSLU
-#define ResetPrinterW NotSupportedByMSLU
-
-// The following Shell COM interfaces are not supported by the MSLU.
-// See ShellFolder2.cpp
-#define IID_IFileViewerW NotSupportedByMSLU
-#define IID_IShellLinkW NotSupportedByMSLU
-#define IID_IExtractIconW NotSupportedByMSLU
-#define IID_IShellCopyHookW NotSupportedByMSLU
-#define IID_IShellExecuteHookW NotSupportedByMSLU
-#define IID_INewShortcutHookW NotSupportedByMSLU
-
-// The following CRT functions should fail on compiling, as it does not work on
-// Win9x/ME platform. If you need these CRTs, write a wrapper for ANSI version
-// equivalents, in which it converts to/from Unicode using WideCharToMultiByte.
-//
-// Or, if it is apparent that the function is used only on WindowsNT/2K/XP, wrap
-// the call site with #undef - #define, e.g:
-//
-// #undef _wsomefunc
-// call _wsomefunc
-// #define _wsomefunc NotSupportedOnWin9X
-
-#define _waccess NotSupportedOnWin9X
-#define _wchmod NotSupportedOnWin9X
-#define _wfullpath UnicowsLoader::_wfullpathImpl
-#define _wremove NotSupportedOnWin9X
-#define _wrename NotSupportedOnWin9X
-#define _wstat NotSupportedOnWin9X
-#define _wstati64 NotSupportedOnWin9X
-#define _wstat64 NotSupportedOnWin9X
-#define _wunlink NotSupportedOnWin9X
-#define _wfopen NotSupportedOnWin9X
-#define _wfreopen NotSupportedOnWin9X
-#define _wfsopen NotSupportedOnWin9X
-#define _wcreat NotSupportedOnWin9X
-#define _wopen NotSupportedOnWin9X
-#define _wsopen NotSupportedOnWin9X
-#define _wfindfirst NotSupportedOnWin9X
-#define _wfindfirst64 NotSupportedOnWin9X
-#define _wfindnext NotSupportedOnWin9X
-#define _wfindnext64 NotSupportedOnWin9X
-#define _wsystem NotSupportedOnWin9X
-#define _wexcel NotSupportedOnWin9X
-#define _wexcele NotSupportedOnWin9X
-#define _wexelp NotSupportedOnWin9X
-#define _wexelpe NotSupportedOnWin9X
-#define _wexecv NotSupportedOnWin9X
-#define _wexecve NotSupportedOnWin9X
-#define _wexecvp NotSupportedOnWin9X
-#define _wexecvpe NotSupportedOnWin9X
-#define _wpopen NotSupportedOnWin9X
-#define _wputenv NotSupportedOnWin9X
-#define _wspawnl NotSupportedOnWin9X
-#define _wspawnle NotSupportedOnWin9X
-#define _wspawnlp NotSupportedOnWin9X
-#define _wspawnlpe NotSupportedOnWin9X
-#define _wspawnv NotSupportedOnWin9X
-#define _wspawnve NotSupportedOnWin9X
-#define _wspawnvp NotSupportedOnWin9X
-#define _wspawnvpe NotSupportedOnWin9X
-
-
-#endif // UNICOWSLOADER_H
diff --git a/src/windows/native/sun/windows/WPrinterJob.cpp b/src/windows/native/sun/windows/WPrinterJob.cpp
index 33395e43a..d64a8e314 100644
--- a/src/windows/native/sun/windows/WPrinterJob.cpp
+++ b/src/windows/native/sun/windows/WPrinterJob.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2008 Sun Microsystems, Inc. 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
@@ -23,14 +23,14 @@
* have any questions.
*/
+#include "awt.h"
+
#include "stdhdrs.h"
#include <commdlg.h>
#include <winspool.h>
#include <limits.h>
#include <float.h>
-#include "awt.h"
-#include "awt_dlls.h"
#include "awt_Toolkit.h"
#include "awt_PrintControl.h"
@@ -74,7 +74,6 @@ Java_sun_print_Win32PrintServiceLookup_getDefaultPrinterName(JNIEnv *env,
TRY;
TCHAR cBuffer[250];
- BOOL bFlag;
OSVERSIONINFO osv;
PRINTER_INFO_2 *ppi2 = NULL;
DWORD dwNeeded = 0;
@@ -86,39 +85,8 @@ Java_sun_print_Win32PrintServiceLookup_getDefaultPrinterName(JNIEnv *env,
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osv);
- // If Windows 95 or 98, use EnumPrinters...
- if (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
-
- // The first EnumPrinters() tells you how big our buffer should
- // be in order to hold ALL of PRINTER_INFO_2. Note that this will
- // usually return FALSE. This only means that the buffer (the 4th
- // parameter) was not filled in. You don't want it filled in here...
-
- EnumPrinters(PRINTER_ENUM_DEFAULT, NULL, 2,
- NULL, 0, &dwNeeded, &dwReturned);
- if (dwNeeded == 0) {
- return NULL;
- }
-
- // Allocate enough space for PRINTER_INFO_2...
- ppi2 = (PRINTER_INFO_2 *)GlobalAlloc(GPTR, dwNeeded);
- if (!ppi2) {
- return NULL;
- }
-
- // The second EnumPrinters() will fill in all the current information.
- bFlag = EnumPrinters(PRINTER_ENUM_DEFAULT, NULL, 2,
- (LPBYTE)ppi2, dwNeeded, &dwNeeded, &dwReturned);
- if (!bFlag) {
- GlobalFree(ppi2);
- return NULL;
- }
-
- jPrinterName = JNU_NewStringPlatform(env, ppi2->pPrinterName);
- GlobalFree(ppi2);
- return jPrinterName;
-
- } else if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ // If Windows 2000, XP, Vista
+ if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT) {
// Retrieve the default string from Win.ini (the registry).
// String will be in form "printername,drivername,portname".
@@ -165,62 +133,32 @@ Java_sun_print_Win32PrintServiceLookup_getAllPrinterNames(JNIEnv *env,
jobjectArray nameArray;
try {
- if (IS_NT) {
- ::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
- NULL, 4, NULL, 0, &cbNeeded, &cReturned);
- pPrinterEnum = new BYTE[cbNeeded];
- ::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
- NULL, 4, pPrinterEnum, cbNeeded, &cbNeeded,
- &cReturned);
-
- if (cReturned > 0) {
- nameArray = env->NewObjectArray(cReturned, clazz, NULL);
- if (nameArray == NULL) {
- throw std::bad_alloc();
- }
- } else {
- nameArray = NULL;
- }
-
-
- for (DWORD i = 0; i < cReturned; i++) {
- PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *)
- (pPrinterEnum + i * sizeof(PRINTER_INFO_4));
- utf_str = JNU_NewStringPlatform(env, info4->pPrinterName);
- if (utf_str == NULL) {
- throw std::bad_alloc();
- }
- env->SetObjectArrayElement(nameArray, i, utf_str);
- env->DeleteLocalRef(utf_str);
+ ::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
+ NULL, 4, NULL, 0, &cbNeeded, &cReturned);
+ pPrinterEnum = new BYTE[cbNeeded];
+ ::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
+ NULL, 4, pPrinterEnum, cbNeeded, &cbNeeded,
+ &cReturned);
+
+ if (cReturned > 0) {
+ nameArray = env->NewObjectArray(cReturned, clazz, NULL);
+ if (nameArray == NULL) {
+ throw std::bad_alloc();
}
} else {
- ::EnumPrinters(PRINTER_ENUM_LOCAL,
- NULL, 5, NULL, 0, &cbNeeded, &cReturned);
- pPrinterEnum = new BYTE[cbNeeded];
- ::EnumPrinters(PRINTER_ENUM_LOCAL,
- NULL, 5, pPrinterEnum, cbNeeded, &cbNeeded,
- &cReturned);
-
- if (cReturned > 0) {
- nameArray = env->NewObjectArray(cReturned, clazz, NULL);
- if (nameArray == NULL) {
- throw std::bad_alloc();
- }
- } else {
- nameArray = NULL;
- }
+ nameArray = NULL;
+ }
- for (DWORD i = 0; i < cReturned; i++) {
- PRINTER_INFO_5 *info5 = (PRINTER_INFO_5 *)
- (pPrinterEnum + i * sizeof(PRINTER_INFO_5));
- utf_str = JNU_NewStringPlatform(env, info5->pPrinterName);
- if (utf_str == NULL) {
- throw std::bad_alloc();
- }
- env->SetObjectArrayElement(nameArray, i, utf_str);
- env->DeleteLocalRef(utf_str);
+ for (DWORD i = 0; i < cReturned; i++) {
+ PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *)
+ (pPrinterEnum + i * sizeof(PRINTER_INFO_4));
+ utf_str = JNU_NewStringPlatform(env, info4->pPrinterName);
+ if (utf_str == NULL) {
+ throw std::bad_alloc();
}
+ env->SetObjectArrayElement(nameArray, i, utf_str);
+ env->DeleteLocalRef(utf_str);
}
} catch (std::bad_alloc&) {
delete [] pPrinterEnum;
@@ -872,7 +810,7 @@ Java_sun_print_Win32PrintService_getDefaultSettings(JNIEnv *env,
int numSizes = ::DeviceCapabilities(printerName, printerPort,
DC_PAPERS, NULL, NULL);
if (numSizes > 0) {
- LPWORD papers = (LPWORD)safe_Malloc(numSizes * sizeof(WORD));
+ LPTSTR papers = (LPTSTR)safe_Malloc(numSizes * sizeof(WORD));
if (papers != NULL &&
::DeviceCapabilities(printerName, printerPort,
DC_PAPERS, papers, NULL) != -1) {
diff --git a/src/windows/native/sun/windows/awt.h b/src/windows/native/sun/windows/awt.h
index 389aba67d..cd4791fd2 100644
--- a/src/windows/native/sun/windows/awt.h
+++ b/src/windows/native/sun/windows/awt.h
@@ -26,10 +26,21 @@
#ifndef _AWT_H_
#define _AWT_H_
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0600
+#endif
+
+//#ifndef NTDDI_VERSION
+//#define NTDDI_VERSION NTDDI_LONGHORN
+//#endif
+
#include "stdhdrs.h"
#include "alloc.h"
#include "awt_Debug.h"
-#include "UnicowsLoader.h"
extern COLORREF DesktopColor2RGB(int colorIndex);
@@ -129,40 +140,23 @@ typedef AwtObject* PDATA;
/* /NEW JNI */
/*
- * IS_NT returns TRUE on NT, 2000, XP
- * IS_WIN2000 returns TRUE on 2000, XP
- * IS_WINXP returns TRUE on XP
- * IS_WIN95 returns TRUE on 95, 98, ME
- * IS_WIN98 returns TRUE on 98, ME
- * IS_WINME returns TRUE on ME
- * IS_WIN32 returns TRUE on 32-bit Pentium and
- * 64-bit Itanium.
* IS_WIN64 returns TRUE on 64-bit Itanium
- *
- * uname -s returns Windows_95 on 95
- * uname -s returns Windows_98 on 98 and ME
- * uname -s returns Windows_NT on NT and 2000 and XP
*/
-#if defined (WIN32)
- #define IS_WIN32 TRUE
-#else
- #define IS_WIN32 FALSE
-#endif
#if defined (_WIN64)
#define IS_WIN64 TRUE
#else
#define IS_WIN64 FALSE
#endif
-#define IS_NT (IS_WIN32 && !(::GetVersion() & 0x80000000))
-#define IS_WIN2000 (IS_NT && LOBYTE(LOWORD(::GetVersion())) >= 5)
-#define IS_WIN2003 (IS_NT && LOBYTE(LOWORD(::GetVersion())) == 5 && HIBYTE(LOWORD(::GetVersion())) >= 2)
-#define IS_WINXP (IS_NT && (IS_WIN2000 && HIBYTE(LOWORD(::GetVersion())) >= 1) || LOBYTE(LOWORD(::GetVersion())) > 5)
-#define IS_WINVISTA (IS_NT && LOBYTE(LOWORD(::GetVersion())) >= 6)
-#define IS_WIN32S (IS_WIN32 && !IS_NT && LOBYTE(LOWORD(::GetVersion())) < 4)
-#define IS_WIN95 (IS_WIN32 && !IS_NT && LOBYTE(LOWORD(::GetVersion())) >= 4)
-#define IS_WIN98 (IS_WIN95 && HIBYTE(LOWORD(::GetVersion())) >= 10)
-#define IS_WINME (IS_WIN95 && HIBYTE(LOWORD(::GetVersion())) >= 90)
-#define IS_WIN4X (IS_WIN32 && LOBYTE(::GetVersion()) >= 4)
+
+/*
+ * IS_WIN2000 returns TRUE on 2000, XP and Vista
+ * IS_WINXP returns TRUE on XP and Vista
+ * IS_WINVISTA returns TRUE on Vista
+ */
+#define IS_WIN2000 (LOBYTE(LOWORD(::GetVersion())) >= 5)
+#define IS_WINXP ((IS_WIN2000 && HIBYTE(LOWORD(::GetVersion())) >= 1) || LOBYTE(LOWORD(::GetVersion())) > 5)
+#define IS_WINVISTA (LOBYTE(LOWORD(::GetVersion())) >= 6)
+
#define IS_WINVER_ATLEAST(maj, min) \
((maj) < LOBYTE(LOWORD(::GetVersion())) || \
(maj) == LOBYTE(LOWORD(::GetVersion())) && \
@@ -177,6 +171,12 @@ typedef AwtObject* PDATA;
extern JavaVM *jvm;
+// Platform encoding is Unicode (UTF-16), re-define JNU_ functions
+// to proper JNI functions.
+#define JNU_NewStringPlatform(env, x) env->NewString(reinterpret_cast<jchar*>(x), static_cast<jsize>(_tcslen(x)))
+#define JNU_GetStringPlatformChars(env, x, y) reinterpret_cast<LPCWSTR>(env->GetStringChars(x, y))
+#define JNU_ReleaseStringPlatformChars(env, x, y) env->ReleaseStringChars(x, reinterpret_cast<const jchar*>(y))
+
/*
* Itanium symbols needed for 64-bit compilation.
* These are defined in winuser.h in the August 2001 MSDN update.
@@ -211,17 +211,12 @@ extern JavaVM *jvm;
* NOTE: float.h must be defined if using these macros
*/
#define SAVE_CONTROLWORD \
- unsigned int fpu_cw = _CW_DEFAULT; \
- if (IS_WIN95) { \
- fpu_cw = _control87(0, 0); \
- }
-
-#define RESTORE_CONTROLWORD \
- if (IS_WIN95) { \
- if ( _control87(0, 0) != fpu_cw) { \
- _control87(fpu_cw, 0xfffff); \
- } \
- }
+ unsigned int fpu_cw = _control87(0, 0);
+
+#define RESTORE_CONTROLWORD \
+ if (_control87(0, 0) != fpu_cw) { \
+ _control87(fpu_cw, 0xffffffff); \
+ }
/*
* checks if the current thread is/isn't the toolkit thread
diff --git a/src/windows/native/sun/windows/awt_Button.cpp b/src/windows/native/sun/windows/awt_Button.cpp
index e4895915d..3764655cb 100644
--- a/src/windows/native/sun/windows/awt_Button.cpp
+++ b/src/windows/native/sun/windows/awt_Button.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -23,7 +23,7 @@
* have any questions.
*/
-#include <jni.h>
+#include "awt.h"
#include "awt_Object.h" /* wop_pDataID */
#include "awt_Toolkit.h"
@@ -106,7 +106,7 @@ AwtButton* AwtButton::Create(jobject self, jobject parent)
if (label == NULL) {
labelStr = L"";
} else {
- labelStr = env->GetStringChars(label, JNI_FALSE);
+ labelStr = JNU_GetStringPlatformChars(env, label, JNI_FALSE);
}
style = 0;
@@ -128,7 +128,7 @@ AwtButton* AwtButton::Create(jobject self, jobject parent)
c->m_backgroundColorSet = TRUE; // suppress inheriting parent's color
c->UpdateBackground(env, target);
if (label != NULL)
- env->ReleaseStringChars(label, labelStr);
+ JNU_ReleaseStringPlatformChars(env, label, labelStr);
} catch (...) {
env->DeleteLocalRef(target);
if (label != NULL)
diff --git a/src/windows/native/sun/windows/awt_Checkbox.cpp b/src/windows/native/sun/windows/awt_Checkbox.cpp
index b0a73679d..2e8cf9923 100644
--- a/src/windows/native/sun/windows/awt_Checkbox.cpp
+++ b/src/windows/native/sun/windows/awt_Checkbox.cpp
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include "awt_Toolkit.h"
#include "awt_Checkbox.h"
#include "awt_KeyboardFocusManager.h"
@@ -106,7 +107,7 @@ AwtCheckbox* AwtCheckbox::Create(jobject peer, jobject parent)
label = (jstring)env->GetObjectField(target, AwtCheckbox::labelID);
if (label != NULL) {
- labelStr = env->GetStringChars(label, 0);
+ labelStr = JNU_GetStringPlatformChars(env, label, 0);
}
if (labelStr != 0) {
jint x = env->GetIntField(target, AwtComponent::xID);
@@ -123,7 +124,7 @@ AwtCheckbox* AwtCheckbox::Create(jobject peer, jobject parent)
peer);
if (labelStr != defaultLabelStr) {
- env->ReleaseStringChars(label, labelStr);
+ JNU_ReleaseStringPlatformChars(env, label, labelStr);
}
} else {
throw std::bad_alloc();
diff --git a/src/windows/native/sun/windows/awt_Choice.cpp b/src/windows/native/sun/windows/awt_Choice.cpp
index 27145ea6e..ca7adb0ed 100644
--- a/src/windows/native/sun/windows/awt_Choice.cpp
+++ b/src/windows/native/sun/windows/awt_Choice.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -206,7 +206,7 @@ int AwtChoice::GetFieldHeight()
fieldHeight =(int)::SendMessage(GetHWnd(), CB_GETITEMHEIGHT, (UINT)-1, 0);
// add top and bottom border lines; border size is different for
// Win 4.x (3d edge) vs 3.x (1 pixel line)
- borderHeight = ::GetSystemMetrics(IS_WIN4X ? SM_CYEDGE : SM_CYBORDER);
+ borderHeight = ::GetSystemMetrics(SM_CYEDGE);
fieldHeight += borderHeight*2;
return fieldHeight;
}
@@ -424,6 +424,9 @@ AwtChoice::WmKillFocus(HWND hWndGotFocus)
case mrPassAlong:
return AwtComponent::WmKillFocus(hWndGotFocus);
}
+
+ DASSERT(false); // must never reach here
+ return mrDoDefault;
}
MsgRouting
diff --git a/src/windows/native/sun/windows/awt_Color.cpp b/src/windows/native/sun/windows/awt_Color.cpp
index b1c6cb905..bc9b2f13f 100644
--- a/src/windows/native/sun/windows/awt_Color.cpp
+++ b/src/windows/native/sun/windows/awt_Color.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-1999 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -97,7 +97,7 @@ Java_sun_awt_windows_WColor_getDefaultColor(JNIEnv *env, jclass cls,
iColor = COLOR_MENUTEXT;
break;
case sun_awt_windows_WColor_BUTTON_BKGND:
- iColor = (IS_NT) ? COLOR_BTNFACE : COLOR_3DFACE;
+ iColor = COLOR_BTNFACE;
break;
case sun_awt_windows_WColor_BUTTON_TEXT:
iColor = COLOR_BTNTEXT;
diff --git a/src/windows/native/sun/windows/awt_Component.cpp b/src/windows/native/sun/windows/awt_Component.cpp
index 5b017c978..6f0df2e44 100644
--- a/src/windows/native/sun/windows/awt_Component.cpp
+++ b/src/windows/native/sun/windows/awt_Component.cpp
@@ -23,7 +23,8 @@
* have any questions.
*/
-#include "windows.h"
+#include "awt.h"
+
#include <windowsx.h>
#include <zmouse.h>
@@ -42,7 +43,6 @@
#include "awt_MouseEvent.h"
#include "awt_Palette.h"
#include "awt_Toolkit.h"
-#include "awt_Unicode.h"
#include "awt_Window.h"
#include "awt_Win32GraphicsDevice.h"
#include "Hashtable.h"
@@ -67,30 +67,10 @@
#include <java_awt_event_MouseWheelEvent.h>
// Begin -- Win32 SDK include files
-#include <tchar.h>
#include <imm.h>
#include <ime.h>
// End -- Win32 SDK include files
-#ifndef GET_KEYSTATE_WPARAM // defined for (_WIN32_WINNT >= 0x0400)
-#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam))
-#endif
-
-#ifndef GET_WHEEL_DELTA_WPARAM // defined for (_WIN32_WINNT >= 0x0500)
-#define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam))
-#endif
-
-// <XXX> <!-- TEMPORARY HACK TO TEST AGAINST OLD VC INLCUDES -->
-#if !defined(__int3264)
-#define GetWindowLongPtr GetWindowLong
-#define SetWindowLongPtr SetWindowLong
-#define GWLP_USERDATA GWL_USERDATA
-#define GWLP_WNDPROC GWL_WNDPROC
-typedef __int32 LONG_PTR;
-typedef unsigned __int32 ULONG_PTR;
-#endif // __int3264
-// </XXX>
-
#include <awt_DnDDT.h>
LPCTSTR szAwtComponentClassName = TEXT("SunAwtComponent");
@@ -207,9 +187,7 @@ LANGID AwtComponent::m_idLang = LOWORD(::GetKeyboardLayout(0));
UINT AwtComponent::m_CodePage
= AwtComponent::LangToCodePage(m_idLang);
-BOOL AwtComponent::m_isWin95 = IS_WIN95;
-BOOL AwtComponent::m_isWin2000 = IS_WIN2000;
-BOOL AwtComponent::m_isWinNT = IS_NT;
+jint *AwtComponent::masks;
static BOOL bLeftShiftIsDown = false;
static BOOL bRightShiftIsDown = false;
@@ -544,7 +522,7 @@ AwtComponent::CreateHWnd(JNIEnv *env, LPCWSTR title,
jobject createError = NULL;
if (dw == ERROR_OUTOFMEMORY)
{
- jstring errorMsg = env->NewStringUTF("too many window handles");
+ jstring errorMsg = JNU_NewStringPlatform(env, L"too many window handles");
createError = JNU_NewObjectByName(env, "java/lang/OutOfMemoryError",
"(Ljava/lang/String;)V",
errorMsg);
@@ -1201,6 +1179,9 @@ void SpyWinMessage(HWND hwnd, UINT message, LPCTSTR szComment) {
WIN_MSG(WM_MBUTTONDOWN)
WIN_MSG(WM_MBUTTONUP)
WIN_MSG(WM_MBUTTONDBLCLK)
+ WIN_MSG(WM_XBUTTONDBLCLK)
+ WIN_MSG(WM_XBUTTONDOWN)
+ WIN_MSG(WM_XBUTTONUP)
WIN_MSG(WM_MOUSEWHEEL)
WIN_MSG(WM_PARENTNOTIFY)
WIN_MSG(WM_ENTERMENULOOP)
@@ -1347,17 +1328,9 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
return (LRESULT)TRUE;
}
- UINT switchMessage;
- if (IS_WIN95 && !IS_WIN98 && message == Wheel95GetMsg()) {
- // Wheel message is generated dynamically on 95. A quick swap and
- // we're good to go.
- DTRACE_PRINTLN1("got wheel event on 95. msg is %i\n", message);
- switchMessage = WM_MOUSEWHEEL;
- }
- else {
- switchMessage = message;
- }
+ DWORD curPos = 0;
+ UINT switchMessage = message;
switch (switchMessage) {
case WM_AWT_GETDC:
{
@@ -1644,67 +1617,87 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
case WM_MBUTTONUP:
+ case WM_XBUTTONDBLCLK:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
case WM_AWT_MOUSEENTER:
case WM_AWT_MOUSEEXIT:
- {
- DWORD curPos = ::GetMessagePos();
+ curPos = ::GetMessagePos();
POINT myPos;
myPos.x = GET_X_LPARAM(curPos);
myPos.y = GET_Y_LPARAM(curPos);
::ScreenToClient(GetHWnd(), &myPos);
switch(switchMessage) {
case WM_AWT_MOUSEENTER:
- mr = WmMouseEnter(static_cast<UINT>(wParam), myPos.x, myPos.y); break;
+ mr = WmMouseEnter(static_cast<UINT>(wParam), myPos.x, myPos.y);
+ break;
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
- mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
- LEFT_BUTTON); break;
- case WM_LBUTTONUP:
- mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
- LEFT_BUTTON); break;
- case WM_MOUSEMOVE:
- mr = WmMouseMove(static_cast<UINT>(wParam), myPos.x, myPos.y); break;
- case WM_MBUTTONDOWN:
- case WM_MBUTTONDBLCLK:
- mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
- MIDDLE_BUTTON); break;
- case WM_RBUTTONDOWN:
- case WM_RBUTTONDBLCLK:
- mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
- RIGHT_BUTTON); break;
- case WM_RBUTTONUP:
- mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
- RIGHT_BUTTON);
- break;
- case WM_MBUTTONUP:
- mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
- MIDDLE_BUTTON);
- break;
- case WM_AWT_MOUSEEXIT:
- mr = WmMouseExit(static_cast<UINT>(wParam), myPos.x, myPos.y);
- break;
- case WM_MOUSEWHEEL:
- if (IS_WIN95 && !IS_WIN98) {
- // On 95, the wParam doesn't contain the keystate flags, just
- // the wheel rotation. The keystates are fetched in WmMouseWheel
- // using GetJavaModifiers().
- mr = WmMouseWheel(0,
- GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
- (int)wParam);
- return FALSE;
- }
- else {
+ mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ LEFT_BUTTON);
+ break;
+ case WM_LBUTTONUP:
+ mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ LEFT_BUTTON);
+ break;
+ case WM_MOUSEMOVE:
+ mr = WmMouseMove(static_cast<UINT>(wParam), myPos.x, myPos.y);
+ break;
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONDBLCLK:
+ mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ MIDDLE_BUTTON);
+ break;
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONDBLCLK:
+ if (AwtToolkit::GetInstance().areExtraMouseButtonsEnabled()) {
+ if (HIWORD(wParam) == 1) {
+ mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ X1_BUTTON);
+ }
+ if (HIWORD(wParam) == 2) {
+ mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ X2_BUTTON);
+ }
+ }
+ break;
+ case WM_XBUTTONUP:
+ if (AwtToolkit::GetInstance().areExtraMouseButtonsEnabled()) {
+ if (HIWORD(wParam) == 1) {
+ mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ X1_BUTTON);
+ }
+ if (HIWORD(wParam) == 2) {
+ mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ X2_BUTTON);
+ }
+ }
+ break;
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONDBLCLK:
+ mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ RIGHT_BUTTON);
+ break;
+ case WM_RBUTTONUP:
+ mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ RIGHT_BUTTON);
+ break;
+ case WM_MBUTTONUP:
+ mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y,
+ MIDDLE_BUTTON);
+ break;
+ case WM_AWT_MOUSEEXIT:
+ mr = WmMouseExit(static_cast<UINT>(wParam), myPos.x, myPos.y);
+ break;
+ case WM_MOUSEWHEEL:
mr = WmMouseWheel(GET_KEYSTATE_WPARAM(wParam),
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
GET_WHEEL_DELTA_WPARAM(wParam));
+ break;
}
break;
- }
- }
- break;
-
case WM_SETCURSOR:
mr = mrDoDefault;
if (LOWORD(lParam) == HTCLIENT) {
@@ -2589,8 +2582,12 @@ MsgRouting AwtComponent::WmMouseMove(UINT flags, int x, int y)
lastComp = this;
lastX = x;
lastY = y;
-
- if ( (flags & ALL_MK_BUTTONS) != 0 ) {
+ BOOL extraButtonsEnabled = AwtToolkit::GetInstance().areExtraMouseButtonsEnabled();
+ if (((flags & (ALL_MK_BUTTONS)) != 0) ||
+ (extraButtonsEnabled && (flags & (X_BUTTONS)) != 0))
+// if (( extraButtonsEnabled && ( (flags & (ALL_MK_BUTTONS | X_BUTTONS)) != 0 )) ||
+// ( !extraButtonsEnabled && (((flags & (ALL_MK_BUTTONS)) != 0 )) && ((flags & (X_BUTTONS)) == 0) ))
+ {
// 6404008 : if Dragged event fired we shouldn't fire
// Clicked event: m_firstDragSent set to TRUE.
// This is a partial backout of 5039416 fix.
@@ -2649,21 +2646,10 @@ MsgRouting AwtComponent::WmMouseWheel(UINT flags, int x, int y,
jdouble preciseWheelRotation = (jdouble) wheelRotation / (-1 * WHEEL_DELTA);
MSG msg;
-
- if (IS_WIN95 && !IS_WIN98) {
- // 95 doesn't understand the SPI_GETWHEELSCROLLLINES - get the user
- // preference by other means
- DTRACE_PRINTLN("WmMouseWheel: using 95 branch");
- platformLines = Wheel95GetScrLines();
- result = true;
- InitMessage(&msg, lastMessage, wheelRotation, MAKELPARAM(x, y));
- }
- else {
- result = ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
- &platformLines, 0);
- InitMessage(&msg, lastMessage, MAKEWPARAM(flags, wheelRotation),
- MAKELPARAM(x, y));
- }
+ result = ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
+ &platformLines, 0);
+ InitMessage(&msg, lastMessage, MAKEWPARAM(flags, wheelRotation),
+ MAKELPARAM(x, y));
if (result) {
if (platformLines == WHEEL_PAGESCROLL) {
@@ -2743,156 +2729,21 @@ jint AwtComponent::GetShiftKeyLocation(UINT vkey, UINT flags)
"AwtComponent::GetShiftKeyLocation vkey = %d = 0x%x scan = %d",
vkey, vkey, keyScanCode);
- if (m_isWinNT) {
- leftShiftScancode = ::MapVirtualKey(VK_LSHIFT, 0);
- rightShiftScancode = ::MapVirtualKey(VK_RSHIFT, 0);
-
- if (keyScanCode == leftShiftScancode) {
- return java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- }
- if (keyScanCode == rightShiftScancode) {
- return java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- }
-
- DASSERT(false);
- // Note: the above should not fail on NT (or 2000),
- // but just in case it does, try the more complicated method
- // we use for Win9x below.
- }
-
- // "Transition" bit = 0 if keyPressed, 1 if keyReleased
- BOOL released = ((1<<15) & flags);
-
- DTRACE_PRINTLN2(
- "AwtComponent::GetShiftKeyLocation bLeftShiftIsDown = %d bRightShiftIsDown == %d",
- bLeftShiftIsDown, bRightShiftIsDown);
- DTRACE_PRINTLN2(
- "AwtComponent::GetShiftKeyLocation lastShiftKeyPressed = %d released = %d",
- lastShiftKeyPressed, released);
-
- jint keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
-
- // It is possible for somebody to hold down one or both
- // Shift keys, causing repeat key events. We need to
- // handle all the cases.
- //
- // Just a side-note: if two or more keys are being held down,
- // and then one key is released, whether more key presses are
- // generated for the keys that are still held down depends on
- // which keys they are, and whether you released the right or
- // the left shift/ctrl/etc. key first. This also differs
- // between Win9x and NT. Just plain screwy.
- //
- // Note: on my PC, the repeat count is always 1. Yup, we need
- // 16 bits to handle that, all right.
-
- // Handle the case where only one of the Shift keys
- // was down before this event took place
- if (bLeftShiftIsDown && !bRightShiftIsDown) {
- if (released) {
- // This is a left Shift release
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- } else {
- // This is a right Shift press
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- }
- } else if (!bLeftShiftIsDown && bRightShiftIsDown) {
- if (released) {
- // This is a right Shift release
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- } else {
- // This is a left Shift press
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- }
- }
-
- // Handle the case where neither of the Shift keys
- // were down before this event took place
- if (!bLeftShiftIsDown && !bRightShiftIsDown) {
- DASSERT(!released);
- if (HIBYTE(::GetKeyState(VK_LSHIFT)) != 0) {
- // This is a left Shift press
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- } else if (HIBYTE(::GetKeyState(VK_RSHIFT)) != 0) {
- // This is a right Shift press
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- } else {
- DASSERT(false);
- }
- }
+ leftShiftScancode = ::MapVirtualKey(VK_LSHIFT, 0);
+ rightShiftScancode = ::MapVirtualKey(VK_RSHIFT, 0);
- // Handle the case where both Shift keys were down before
- // this event took place
- if (bLeftShiftIsDown && bRightShiftIsDown) {
- // If this is a key release event, we can just check to see
- // what the keyboard state is after the event
- if (released) {
- if (HIBYTE(::GetKeyState(VK_RSHIFT)) == 0) {
- // This is a right Shift release
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- } else if (HIBYTE(::GetKeyState(VK_LSHIFT)) == 0) {
- // This is a left Shift release
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- } else {
- DASSERT(false);
- }
- } else {
- // If this is a key press event, and both Shift keys were
- // already down, this is going to be a repeat of the last
- // Shift press
- if (lastShiftKeyPressed == VK_LSHIFT) {
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- } else if (lastShiftKeyPressed == VK_RSHIFT) {
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- } else {
- DASSERT(false);
- }
- }
+ if (keyScanCode == leftShiftScancode) {
+ return java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
}
-
- if (keyLocation == java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN) {
- // Nothing we tried above worked for some reason. Sigh.
- // Make a last-ditch effort to guess what happened:
- // guess that the Shift scancodes are usually the same
- // from system to system, even though this isn't guaranteed.
- DTRACE_PRINTLN("Last-ditch effort at guessing Shift keyLocation");
-
- // Tested on a couple of Windows keyboards: these are standard values
- leftShiftScancode = 42;
- rightShiftScancode = 54;
-
- if (keyScanCode == leftShiftScancode) {
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
- } else if (keyScanCode == rightShiftScancode) {
- keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- }
+ if (keyScanCode == rightShiftScancode) {
+ return java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
}
- // Set the Shift flags with the new key state.
- bLeftShiftIsDown = (HIBYTE(::GetKeyState(VK_LSHIFT)) != 0);
- bRightShiftIsDown = (HIBYTE(::GetKeyState(VK_RSHIFT)) != 0);
-
- // Update lastShiftKeyPressed
- if (released) {
- // At most one shift key is down now, so just check which one
- if (bLeftShiftIsDown) {
- lastShiftKeyPressed = VK_LSHIFT;
- DASSERT(!bRightShiftIsDown);
- } else if (bRightShiftIsDown) {
- lastShiftKeyPressed = VK_RSHIFT;
- } else {
- lastShiftKeyPressed = 0;
- }
- } else {
- // It was a press, so at least one shift key is down now
- if (keyLocation == java_awt_event_KeyEvent_KEY_LOCATION_LEFT) {
- lastShiftKeyPressed = VK_LSHIFT;
- } else if (keyLocation == java_awt_event_KeyEvent_KEY_LOCATION_RIGHT) {
- lastShiftKeyPressed = VK_RSHIFT;
- }
- }
+ DASSERT(false);
+ // Note: the above should not fail on NT (or 2000)
- return keyLocation;
+ // default value
+ return java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
}
/* Returns Java extended InputEvent modifieres.
@@ -2914,7 +2765,7 @@ AwtComponent::GetJavaModifiers()
modifiers |= java_awt_event_InputEvent_ALT_DOWN_MASK;
}
if (HIBYTE(::GetKeyState(VK_MBUTTON)) != 0) {
- modifiers |= java_awt_event_InputEvent_BUTTON2_DOWN_MASK;
+ modifiers |= java_awt_event_InputEvent_BUTTON2_DOWN_MASK;
}
if (HIBYTE(::GetKeyState(VK_RBUTTON)) != 0) {
modifiers |= java_awt_event_InputEvent_BUTTON3_DOWN_MASK;
@@ -2922,6 +2773,13 @@ AwtComponent::GetJavaModifiers()
if (HIBYTE(::GetKeyState(VK_LBUTTON)) != 0) {
modifiers |= java_awt_event_InputEvent_BUTTON1_DOWN_MASK;
}
+
+ if (HIBYTE(::GetKeyState(VK_XBUTTON1)) != 0) {
+ modifiers |= masks[3];
+ }
+ if (HIBYTE(::GetKeyState(VK_XBUTTON2)) != 0) {
+ modifiers |= masks[4];
+ }
return modifiers;
}
@@ -2936,6 +2794,11 @@ AwtComponent::GetButton(int mouseButton)
return java_awt_event_MouseEvent_BUTTON2;
case RIGHT_BUTTON:
return java_awt_event_MouseEvent_BUTTON3;
+ case X1_BUTTON: //16 :
+ //just assign 4 and 5 numbers because MouseEvent class doesn't contain const identifier for them now
+ return 4;
+ case X2_BUTTON: //32
+ return 5;
}
return java_awt_event_MouseEvent_NOBUTTON;
}
@@ -2950,6 +2813,10 @@ AwtComponent::GetButtonMK(int mouseButton)
return MK_MBUTTON;
case RIGHT_BUTTON:
return MK_RBUTTON;
+ case X1_BUTTON:
+ return MK_XBUTTON1;
+ case X2_BUTTON:
+ return MK_XBUTTON2;
}
return 0;
}
@@ -2967,6 +2834,14 @@ AwtComponent::GetButtonMK(int mouseButton)
#define VK_NONCONVERT 0x1D
#endif
+#ifndef VK_XBUTTON1
+#define VK_XBUTTON1 0x05
+#endif
+
+#ifndef VK_XBUTTON2
+#define VK_XBUTTON2 0x06
+#endif
+
typedef struct {
UINT javaKey;
UINT windowsKey;
@@ -3786,22 +3661,6 @@ MsgRouting AwtComponent::WmChar(UINT character, UINT repCnt, UINT flags,
// via WM_AWT_FORWARD_BYTE, but the Edit classes don't seem to
// like that.
- // Begin pollution
- if (!m_isWinNT && IsDBCSLeadByteEx(GetCodePage(), BYTE(character))) {
- if (GetDBCSEditHandle() != NULL) {
- return mrDoDefault;
- } else {
- // Kludge: Some Chinese IMEs, e.g. QuanPin, sends two WM_CHAR
- // messages for some punctuations (e.g. full stop) without sending
- // WM_IME_CHAR message beforehand.
- if (m_PendingLeadByte == 0) {
- m_PendingLeadByte = character;
- return mrConsume;
- }
- }
- }
- // End pollution
-
// We will simply create Java events here.
UINT message = system ? WM_SYSCHAR : WM_CHAR;
@@ -3861,43 +3720,8 @@ MsgRouting AwtComponent::WmChar(UINT character, UINT repCnt, UINT flags,
MsgRouting AwtComponent::WmForwardChar(WCHAR character, LPARAM lParam,
BOOL synthetic)
{
- if (m_isWinNT) {
- // just post WM_CHAR with unicode key value
- DefWindowProc(WM_CHAR, (WPARAM)character, lParam);
- return mrConsume;
- }
-
- // This message is sent from the Java key event handler.
- CHAR mbChar[2] = {'\0', '\0'};
-
- int cBytes = ::WideCharToMultiByte(GetCodePage(), 0, &character, 1, mbChar, 2, NULL, NULL);
- if (cBytes!=1 && cBytes!=2) return mrConsume;
-
- HWND hDBCSEditHandle = GetDBCSEditHandle();
-
- if (hDBCSEditHandle != NULL && cBytes==2)
- {
- // The first WM_CHAR message will get handled by the WmChar, but
- // the second WM_CHAR message will get picked off by the Edit class.
- // WmChar will never see it.
- // If an Edit class gets a lead byte, it immediately calls PeekMessage
- // and pulls the trail byte out of the message queue.
- ::PostMessage(hDBCSEditHandle, WM_CHAR, mbChar[0] & 0x00ff, lParam);
- ::PostMessage(hDBCSEditHandle, WM_CHAR, mbChar[1] & 0x00ff, lParam);
- }
- else
- {
- MSG* pMsg;
- pMsg = CreateMessage(WM_CHAR, mbChar[0] & 0x00ff, lParam);
- ::PostMessage(GetHWnd(), WM_AWT_FORWARD_BYTE, (WPARAM)synthetic,
- (LPARAM)pMsg);
- if (mbChar[1])
- {
- pMsg = CreateMessage(WM_CHAR, mbChar[1] & 0x00ff, lParam);
- ::PostMessage(GetHWnd(), WM_AWT_FORWARD_BYTE, (WPARAM)synthetic,
- (LPARAM)pMsg);
- }
- }
+ // just post WM_CHAR with unicode key value
+ DefWindowProc(WM_CHAR, (WPARAM)character, lParam);
return mrConsume;
}
@@ -3929,7 +3753,7 @@ void AwtComponent::OpenCandidateWindow(int x, int y)
SetCandidateWindow(iCandType, x-rc.left, y-rc.top);
}
if (m_bitsCandType != 0) {
- DefWindowProc(WM_IME_NOTIFY, IMN_OPENCANDIDATE, m_bitsCandType);
+ ::DefWindowProc(GetHWnd(), WM_IME_NOTIFY, IMN_OPENCANDIDATE, m_bitsCandType);
}
}
@@ -4543,7 +4367,7 @@ MsgRouting AwtComponent::WmPrint(HDC hDC, LPARAM flags)
// Special case for components with a sunken border. Windows does not
// print the border correctly on PCL printers, so we have to do it ourselves.
- if (IS_WIN4X && (GetStyleEx() & WS_EX_CLIENTEDGE)) {
+ if (GetStyleEx() & WS_EX_CLIENTEDGE) {
RECT r;
VERIFY(::GetWindowRect(GetHWnd(), &r));
VERIFY(::OffsetRect(&r, -r.left, -r.top));
@@ -4559,7 +4383,7 @@ MsgRouting AwtComponent::WmPrint(HDC hDC, LPARAM flags)
* We will first print the non-client area with the original offset,
* then the client area with a corrected offset.
*/
- if (IS_WIN4X && (GetStyleEx() & WS_EX_CLIENTEDGE)) {
+ if (GetStyleEx() & WS_EX_CLIENTEDGE) {
int nEdgeWidth = ::GetSystemMetrics(SM_CXEDGE);
int nEdgeHeight = ::GetSystemMetrics(SM_CYEDGE);
@@ -5305,7 +5129,12 @@ void AwtComponent::SynthesizeMouseMessage(JNIEnv *env, jobject mouseEvent)
if (modifiers & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) {
wLow |= MK_MBUTTON;
}
-
+ if (modifiers & X1_BUTTON) {
+ wLow |= GetButtonMK(X1_BUTTON);
+ }
+ if (modifiers & X2_BUTTON) {
+ wLow |= GetButtonMK(X2_BUTTON);
+ }
wheelAmt = (jint)JNU_CallMethodByName(env,
NULL,
@@ -5319,18 +5148,8 @@ void AwtComponent::SynthesizeMouseMessage(JNIEnv *env, jobject mouseEvent)
// convert Java wheel amount value to Win32
wheelAmt *= -1 * WHEEL_DELTA;
- if (IS_WIN95 && !IS_WIN98) {
- // 95 doesn't understand WM_MOUSEWHEEL, so plug in value of
- // mouse wheel event on 95
- DTRACE_PRINTLN("awt_C::synthmm - 95 case");
- DASSERT(Wheel95GetMsg() != NULL);
- message = Wheel95GetMsg();
- wParam = wheelAmt;
- }
- else {
- message = WM_MOUSEWHEEL;
- wParam = MAKEWPARAM(wLow, wheelAmt);
- }
+ message = WM_MOUSEWHEEL;
+ wParam = MAKEWPARAM(wLow, wheelAmt);
break;
default:
@@ -5445,45 +5264,6 @@ void AwtComponent::Enable(BOOL bEnable)
VerifyState();
}
-/* Initialization of MouseWheel support on Windows 95 */
-void AwtComponent::Wheel95Init() {
- DASSERT(IS_WIN95 && !IS_WIN98);
-
- HWND mwHWND = NULL;
- UINT wheelMSG = WM_NULL;
- UINT suppMSG = WM_NULL;
- UINT linesMSG = WM_NULL;
- BOOL wheelActive;
- INT lines;
-
- mwHWND = HwndMSWheel(&wheelMSG, &suppMSG, &linesMSG, &wheelActive, &lines);
- if (mwHWND != WM_NULL) {
- sm_95WheelMessage = wheelMSG;
- sm_95WheelSupport = suppMSG;
- }
-}
-
-/* Win95 only
- * Return the user's preferred number of lines of test to scroll when the
- * mouse wheel is rotated.
- */
-UINT AwtComponent::Wheel95GetScrLines() {
- DASSERT(IS_WIN95 && !IS_WIN98);
- DASSERT(sm_95WheelSupport != NULL);
-
- HWND mwHWND = NULL;
- UINT linesMSG = WM_NULL;
- INT numLines = 3;
-
- linesMSG = RegisterWindowMessage(MSH_SCROLL_LINES);
- mwHWND = FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE);
-
- if (mwHWND && linesMSG) {
- numLines = (INT)::SendMessage(mwHWND, linesMSG, 0, 0);
- }
- return numLines;
-}
-
/*
* associate an AwtDropTarget with this AwtComponent
*/
@@ -5983,7 +5763,7 @@ void AwtComponent::_SetFont(void *param)
{
AwtFont *awtFont = (AwtFont *)env->GetLongField(font, AwtFont::pDataID);
if (awtFont == NULL) {
- /*arguments of AwtFont::Create are changed for multifont component */
+ /*arguments of AwtFont::Create are changed for multifont component */
awtFont = AwtFont::Create(env, font);
}
env->SetLongField(font, AwtFont::pDataID, (jlong)awtFont);
@@ -6334,30 +6114,36 @@ void AwtComponent::_SetRectangularShape(void *param)
c = (AwtComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
- RGNDATA *pRgnData = NULL;
- RGNDATAHEADER *pRgnHdr;
-
- /* reserving memory for the worst case */
- size_t worstBufferSize = size_t(((x2 - x1) / 2 + 1) * (y2 - y1));
- pRgnData = (RGNDATA *) safe_Malloc(sizeof(RGNDATAHEADER) +
- sizeof(RECT_T) * worstBufferSize);
- pRgnHdr = (RGNDATAHEADER *) pRgnData;
-
- pRgnHdr->dwSize = sizeof(RGNDATAHEADER);
- pRgnHdr->iType = RDH_RECTANGLES;
- pRgnHdr->nRgnSize = 0;
- pRgnHdr->rcBound.top = 0;
- pRgnHdr->rcBound.left = 0;
- pRgnHdr->rcBound.bottom = LONG(y2 - y1);
- pRgnHdr->rcBound.right = LONG(x2 - x1);
-
- RECT_T * pRect = (RECT_T *) (((BYTE *) pRgnData) + sizeof(RGNDATAHEADER));
- pRgnHdr->nCount = RegionToYXBandedRectangles(env, x1, y1, x2, y2, region, &pRect, worstBufferSize);
-
- HRGN hRgn = ::ExtCreateRegion(NULL,
- sizeof(RGNDATAHEADER) + sizeof(RECT_T) * pRgnHdr->nCount, pRgnData);
-
- free(pRgnData);
+ HRGN hRgn = NULL;
+
+ if (region || x1 || x2 || y1 || y2) {
+ // If all the params are zeros, the shape must be simply reset.
+ // Otherwise, convert it into a region.
+ RGNDATA *pRgnData = NULL;
+ RGNDATAHEADER *pRgnHdr;
+
+ /* reserving memory for the worst case */
+ size_t worstBufferSize = size_t(((x2 - x1) / 2 + 1) * (y2 - y1));
+ pRgnData = (RGNDATA *) safe_Malloc(sizeof(RGNDATAHEADER) +
+ sizeof(RECT_T) * worstBufferSize);
+ pRgnHdr = (RGNDATAHEADER *) pRgnData;
+
+ pRgnHdr->dwSize = sizeof(RGNDATAHEADER);
+ pRgnHdr->iType = RDH_RECTANGLES;
+ pRgnHdr->nRgnSize = 0;
+ pRgnHdr->rcBound.top = 0;
+ pRgnHdr->rcBound.left = 0;
+ pRgnHdr->rcBound.bottom = LONG(y2 - y1);
+ pRgnHdr->rcBound.right = LONG(x2 - x1);
+
+ RECT_T * pRect = (RECT_T *) (((BYTE *) pRgnData) + sizeof(RGNDATAHEADER));
+ pRgnHdr->nCount = RegionToYXBandedRectangles(env, x1, y1, x2, y2, region, &pRect, worstBufferSize);
+
+ hRgn = ::ExtCreateRegion(NULL,
+ sizeof(RGNDATAHEADER) + sizeof(RECT_T) * pRgnHdr->nCount, pRgnData);
+
+ free(pRgnData);
+ }
::SetWindowRgn(c->GetHWnd(), hRgn, TRUE);
}
@@ -6451,6 +6237,18 @@ JNIEXPORT void JNICALL
Java_java_awt_Component_initIDs(JNIEnv *env, jclass cls)
{
TRY;
+ jclass inputEventClazz = env->FindClass("java/awt/event/InputEvent");
+ jmethodID getButtonDownMasksID = env->GetStaticMethodID(inputEventClazz, "getButtonDownMasks", "()[I");
+ jintArray obj = (jintArray)env->CallStaticObjectMethod(inputEventClazz, getButtonDownMasksID);
+ jint * tmp = env->GetIntArrayElements(obj, JNI_FALSE);
+
+ jsize len = env->GetArrayLength(obj);
+ AwtComponent::masks = new jint[len];
+ for (int i = 0; i < len; i++) {
+ AwtComponent::masks[i] = tmp[i];
+ }
+ env->ReleaseIntArrayElements(obj, tmp, 0);
+ env->DeleteLocalRef(obj);
/* class ids */
jclass peerCls = env->FindClass("sun/awt/windows/WComponentPeer");
@@ -7025,20 +6823,6 @@ Java_sun_awt_windows_WComponentPeer_isObscured(JNIEnv* env,
CATCH_BAD_ALLOC_RET(NULL);
}
-/*
- * Class: sun_awt_windows_WComponentPeer
- * Method: wheelInit
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_sun_awt_windows_WComponentPeer_wheelInit(JNIEnv *env, jclass cls)
-{
- // Only necessary on Win95
- if (IS_WIN95 && !IS_WIN98) {
- AwtComponent::Wheel95Init();
- }
-}
-
JNIEXPORT jboolean JNICALL
Java_sun_awt_windows_WComponentPeer_processSynchronousLightweightTransfer(JNIEnv *env, jclass cls,
jobject heavyweight,
@@ -7217,7 +7001,9 @@ void AwtComponent::VerifyState()
"getName",
"()Ljava/lang/String;").l;
DASSERT(!safe_ExceptionOccurred(env));
- printf("\t%S\n", TO_WSTRING(targetStr));
+ LPCWSTR targetStrW = JNU_GetStringPlatformChars(env, targetStr, NULL);
+ printf("\t%S\n", targetStrW);
+ JNU_ReleaseStringPlatformChars(env, targetStr, targetStrW);
}
printf("\twas: [%d,%d,%dx%d]\n", x, y, width, height);
if (!fSizeValid) {
diff --git a/src/windows/native/sun/windows/awt_Component.h b/src/windows/native/sun/windows/awt_Component.h
index f73980c28..0c18961ed 100644
--- a/src/windows/native/sun/windows/awt_Component.h
+++ b/src/windows/native/sun/windows/awt_Component.h
@@ -53,6 +53,22 @@ const UINT MAX_ACP_STR_LEN = 7; // ANSI CP identifiers are no longer than this
#define MIDDLE_BUTTON 2
#define RIGHT_BUTTON 4
#define DBL_CLICK 8
+#define X1_BUTTON 16
+#define X2_BUTTON 32
+
+#ifndef MK_XBUTTON1
+#define MK_XBUTTON1 0x0020
+#endif
+
+#ifndef MK_XBUTTON2
+#define MK_XBUTTON2 0x0040
+#endif
+
+// combination of standard mouse button flags
+const int ALL_MK_BUTTONS = MK_LBUTTON|MK_MBUTTON|MK_RBUTTON;
+const int X_BUTTONS = MK_XBUTTON1|MK_XBUTTON2;
+
+
// Whether to check for embedded frame and adjust location
#define CHECK_EMBEDDED 0
@@ -81,11 +97,6 @@ enum MsgRouting {
class AwtComponent : public AwtObject {
public:
- enum {
- // combination of all mouse button flags
- ALL_MK_BUTTONS = MK_LBUTTON|MK_MBUTTON|MK_RBUTTON
- };
-
/* java.awt.Component fields and method IDs */
static jfieldID peerID;
static jfieldID xID;
@@ -112,6 +123,7 @@ public:
static jmethodID replaceSurfaceDataLaterMID;
static const UINT WmAwtIsComponent;
+ static jint * masks; //InputEvent mask array
AwtComponent();
virtual ~AwtComponent();
@@ -675,10 +687,6 @@ public:
static HWND sm_focusOwner;
static HWND sm_focusedWindow;
- static BOOL m_isWin95;
- static BOOL m_isWin2000;
- static BOOL m_isWinNT;
-
static BOOL sm_bMenuLoop;
static INLINE BOOL isMenuLoopActive() {
return sm_bMenuLoop;
diff --git a/src/windows/native/sun/windows/awt_Cursor.cpp b/src/windows/native/sun/windows/awt_Cursor.cpp
index f524a465c..c1f3d84e0 100644
--- a/src/windows/native/sun/windows/awt_Cursor.cpp
+++ b/src/windows/native/sun/windows/awt_Cursor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. 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
@@ -79,10 +79,6 @@ AwtCursor::AwtCursor(JNIEnv *env, HCURSOR hCur, jobject jCur, int xH, int yH,
custom = TRUE;
dirty = FALSE;
-
- if (IsWin95Cursor()) {
- customCursors.Add(this);
- }
}
AwtCursor::~AwtCursor()
@@ -96,9 +92,6 @@ void AwtCursor::Dispose()
if (custom) {
::DestroyIcon(hCursor);
- if (IsWin95Cursor()) {
- customCursors.Remove(this);
- }
}
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
@@ -256,16 +249,6 @@ void AwtCursor::UpdateCursor(AwtComponent *comp) {
env->DeleteLocalRef(jcomp);
}
-void AwtCursor::DirtyAllCustomCursors() {
- if (IsWin95Cursor()) {
- AwtObjectListItem *cur = customCursors.m_head;
- while (cur != NULL) {
- ((AwtCursor *)(cur->obj))->dirty = TRUE;
- cur = cur->next;
- }
- }
-}
-
void AwtCursor::Rebuild() {
if (!dirty) {
return;
@@ -294,23 +277,6 @@ void AwtCursor::Rebuild() {
dirty = FALSE;
}
-/* Bug fix for 4205805:
- Custom cursor on WIN95 needs more effort, the same API works fine on NT
- and WIN98. On Win95, DDB has to be passed in when calling createIconIndirect
- Since DDB depends on the DISPLAY, we have to rebuild all the custom cursors
- when user changes the display settings.
-*/
-BOOL AwtCursor::IsWin95Cursor() {
- static BOOL val;
- static BOOL known = FALSE;
- if (!known) {
- val = (IS_WIN32 && !IS_NT && LOBYTE(LOWORD(::GetVersion())) == 4 &&
- HIBYTE(LOWORD(::GetVersion())) == 0);
- known = TRUE;
- }
- return val;
-}
-
extern "C" {
/************************************************************************
diff --git a/src/windows/native/sun/windows/awt_Cursor.h b/src/windows/native/sun/windows/awt_Cursor.h
index 7e59a899d..69c2931a2 100644
--- a/src/windows/native/sun/windows/awt_Cursor.h
+++ b/src/windows/native/sun/windows/awt_Cursor.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. 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
@@ -66,7 +66,6 @@ public:
}
static AwtCursor * CreateSystemCursor(jobject jCursor);
static void UpdateCursor(AwtComponent *comp);
- static void DirtyAllCustomCursors();
static HCURSOR GetCursor(JNIEnv *env, AwtComponent *comp);
static void setPData(jobject cursor, jlong pdata) {
@@ -76,7 +75,6 @@ public:
private:
void Rebuild();
- static BOOL IsWin95Cursor();
HCURSOR hCursor;
jweak jCursor;
diff --git a/src/windows/native/sun/windows/awt_DataTransferer.cpp b/src/windows/native/sun/windows/awt_DataTransferer.cpp
index 8cd5be454..b7225d115 100644
--- a/src/windows/native/sun/windows/awt_DataTransferer.cpp
+++ b/src/windows/native/sun/windows/awt_DataTransferer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2008 Sun Microsystems, Inc. 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
@@ -24,12 +24,11 @@
*/
#include "awt.h"
-#include "awt_dlls.h"
#include "awt_DataTransferer.h"
#include "awt_DnDDT.h"
#include "awt_TextComponent.h"
-#include "awt_Unicode.h"
#include <shlobj.h>
+#include <shellapi.h>
#include <sun_awt_datatransfer_DataTransferer.h>
#include <sun_awt_windows_WDataTransferer.h>
@@ -271,9 +270,7 @@ Java_sun_awt_windows_WDataTransferer_dragQueryFile
hdrop = (HDROP)bBytes;
- load_shell_procs();
-
- UINT nFilenames = (*do_drag_query_file)(hdrop, 0xFFFFFFFF, NULL, 0);
+ UINT nFilenames = ::DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
jclass str_clazz = env->FindClass("java/lang/String");
DASSERT(str_clazz != NULL);
@@ -287,12 +284,12 @@ Java_sun_awt_windows_WDataTransferer_dragQueryFile
buffer = (LPTSTR)safe_Malloc(bufsize*sizeof(TCHAR));
for (UINT i = 0; i < nFilenames; i++) {
- UINT size = (*do_drag_query_file)(hdrop, i, NULL, 0);
+ UINT size = ::DragQueryFile(hdrop, i, NULL, 0);
if (size > bufsize) {
bufsize = size;
buffer = (LPTSTR)safe_Realloc(buffer, bufsize*sizeof(TCHAR));
}
- (*do_drag_query_file)(hdrop, i, buffer, bufsize);
+ ::DragQueryFile(hdrop, i, buffer, bufsize);
jstring name = JNU_NewStringPlatform(env, buffer);
if (name == NULL) {
@@ -401,7 +398,7 @@ Java_sun_awt_windows_WDataTransferer_platformImageBytesToImageData(
case 4:
case 8:
nColorEntries = (pSrcBmih->biClrUsed != 0) ?
- pSrcBmih->biClrUsed : 1 << (pSrcBmih->biBitCount - 1);
+ pSrcBmih->biClrUsed : (1 << pSrcBmih->biBitCount);
break;
case 16:
case 24:
@@ -454,11 +451,6 @@ Java_sun_awt_windows_WDataTransferer_platformImageBytesToImageData(
width = p.x;
height = -p.y;
- // Win9X supports only 16-bit signed coordinates.
- if (IS_WIN95) {
- if (width > 0x7FFF) { width = 0x7FFF; }
- if (height > 0x7FFF) { height = 0x7FFF; }
- }
free(lpemh);
}
break;
diff --git a/src/windows/native/sun/windows/awt_Desktop.cpp b/src/windows/native/sun/windows/awt_Desktop.cpp
index f232a7282..4462ca12e 100644
--- a/src/windows/native/sun/windows/awt_Desktop.cpp
+++ b/src/windows/native/sun/windows/awt_Desktop.cpp
@@ -23,8 +23,8 @@
* have any questions.
*/
+#include "awt.h"
#include <jni.h>
-#include <Windows.h>
#include <shellapi.h>
#include <float.h>
@@ -40,32 +40,32 @@ extern "C" {
JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute
(JNIEnv *env, jclass cls, jstring uri_j, jstring verb_j)
{
- const WCHAR* uri_c = env->GetStringChars(uri_j, JNI_FALSE);
- const WCHAR* verb_c = env->GetStringChars(verb_j, JNI_FALSE);
+ LPCWSTR uri_c = JNU_GetStringPlatformChars(env, uri_j, JNI_FALSE);
+ LPCWSTR verb_c = JNU_GetStringPlatformChars(env, verb_j, JNI_FALSE);
// 6457572: ShellExecute possibly changes FPU control word - saving it here
unsigned oldcontrol87 = _control87(0, 0);
- HINSTANCE retval = ShellExecuteW(NULL, verb_c, uri_c, NULL, NULL, SW_SHOWNORMAL);
+ HINSTANCE retval = ::ShellExecute(NULL, verb_c, uri_c, NULL, NULL, SW_SHOWNORMAL);
_control87(oldcontrol87, 0xffffffff);
- env->ReleaseStringChars(uri_j, uri_c);
- env->ReleaseStringChars(verb_j, verb_c);
+ JNU_ReleaseStringPlatformChars(env, uri_j, uri_c);
+ JNU_ReleaseStringPlatformChars(env, verb_j, verb_c);
if ((int)retval <= 32) {
// ShellExecute failed.
- LPVOID buffer;
- int len = FormatMessageW(
+ LPTSTR buffer = NULL;
+ int len = ::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
- (LPWSTR) &buffer,
+ (LPTSTR)&buffer,
0,
NULL );
- jstring errmsg = env->NewString((LPCWSTR)buffer, len);
+ jstring errmsg = JNU_NewStringPlatform(env, buffer, len);
LocalFree(buffer);
return errmsg;
}
diff --git a/src/windows/native/sun/windows/awt_DesktopProperties.cpp b/src/windows/native/sun/windows/awt_DesktopProperties.cpp
index 10c00fe2c..1475dee38 100644
--- a/src/windows/native/sun/windows/awt_DesktopProperties.cpp
+++ b/src/windows/native/sun/windows/awt_DesktopProperties.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -23,17 +23,17 @@
* have any questions.
*/
-#include "stdhdrs.h"
+#include "awt.h"
#include "mmsystem.h"
#include "jlong.h"
-#include "awt.h"
#include "awt_DesktopProperties.h"
-#include "awt_dlls.h"
+#include "awt_Toolkit.h"
#include "sun_awt_windows_WDesktopProperties.h"
#include "java_awt_Font.h"
#include "awtmsg.h"
-#include "Zmouse.h"
-#include "shellapi.h"
+#include "zmouse.h"
+#include <shellapi.h>
+#include <shlobj.h>
// WDesktopProperties fields
jfieldID AwtDesktopProperties::pDataID = 0;
@@ -44,10 +44,6 @@ jmethodID AwtDesktopProperties::setColorPropertyID = 0;
jmethodID AwtDesktopProperties::setFontPropertyID = 0;
jmethodID AwtDesktopProperties::setSoundPropertyID = 0;
-typedef VOID (WINAPI *SHGetSettingsType)(LPSHELLFLAGSTATE, DWORD);
-static HMODULE libShell32 = NULL;
-static SHGetSettingsType fn_SHGetSettings;
-
AwtDesktopProperties::AwtDesktopProperties(jobject self) {
this->self = GetEnv()->NewGlobalRef(self);
GetEnv()->SetLongField( self, AwtDesktopProperties::pDataID,
@@ -431,14 +427,12 @@ void CheckFontSmoothingSettings(HWND hWnd) {
void AwtDesktopProperties::GetColorParameters() {
- if (IS_WIN98 || IS_WIN2000) {
- SetColorProperty(TEXT("win.frame.activeCaptionGradientColor"),
- GetSysColor(COLOR_GRADIENTACTIVECAPTION));
- SetColorProperty(TEXT("win.frame.inactiveCaptionGradientColor"),
- GetSysColor(COLOR_GRADIENTINACTIVECAPTION));
- SetColorProperty(TEXT("win.item.hotTrackedColor"),
- GetSysColor(COLOR_HOTLIGHT));
- }
+ SetColorProperty(TEXT("win.frame.activeCaptionGradientColor"),
+ GetSysColor(COLOR_GRADIENTACTIVECAPTION));
+ SetColorProperty(TEXT("win.frame.inactiveCaptionGradientColor"),
+ GetSysColor(COLOR_GRADIENTINACTIVECAPTION));
+ SetColorProperty(TEXT("win.item.hotTrackedColor"),
+ GetSysColor(COLOR_HOTLIGHT));
SetColorProperty(TEXT("win.3d.darkShadowColor"), GetSysColor(COLOR_3DDKSHADOW));
SetColorProperty(TEXT("win.3d.backgroundColor"), GetSysColor(COLOR_3DFACE));
SetColorProperty(TEXT("win.3d.highlightColor"), GetSysColor(COLOR_3DHIGHLIGHT));
@@ -510,40 +504,18 @@ void AwtDesktopProperties::GetOtherParameters() {
// This property is called "win.frame.fullWindowDragsOn" above
// This is one of the properties that don't trigger WM_SETTINGCHANGE
SetBooleanProperty(TEXT("awt.dynamicLayoutSupported"), GetBooleanParameter(SPI_GETDRAGFULLWINDOWS));
-
- // 95 MouseWheel support
- // More or less copied from the MSH_MOUSEWHEEL MSDN entry
- if (IS_WIN95 && !IS_WIN98) {
- HWND hdlMSHWHEEL = NULL;
- UINT msgMSHWheelSupported = NULL;
- BOOL wheelSupported = FALSE;
-
- msgMSHWheelSupported = RegisterWindowMessage(MSH_WHEELSUPPORT);
- hdlMSHWHEEL = FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE);
- if (hdlMSHWHEEL && msgMSHWheelSupported) {
- wheelSupported = (BOOL)::SendMessage(hdlMSHWHEEL,
- msgMSHWheelSupported, 0, 0);
- }
- SetBooleanProperty(TEXT("awt.wheelMousePresent"), wheelSupported);
- }
- else {
- SetBooleanProperty(TEXT("awt.wheelMousePresent"),
- ::GetSystemMetrics(SM_MOUSEWHEELPRESENT));
- }
+ SetBooleanProperty(TEXT("awt.wheelMousePresent"),
+ ::GetSystemMetrics(SM_MOUSEWHEELPRESENT));
// END cross-platform properties
- if (IS_WIN98 || IS_WIN2000) {
- //DWORD menuShowDelay;
- //SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &menuShowDelay, 0);
- // SetIntegerProperty(TEXT("win.menu.showDelay"), menuShowDelay);
- SetBooleanProperty(TEXT("win.frame.captionGradientsOn"), GetBooleanParameter(SPI_GETGRADIENTCAPTIONS));
- SetBooleanProperty(TEXT("win.item.hotTrackingOn"), GetBooleanParameter(SPI_GETHOTTRACKING));
- }
+ //DWORD menuShowDelay;
+ //SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &menuShowDelay, 0);
+ // SetIntegerProperty(TEXT("win.menu.showDelay"), menuShowDelay);
+ SetBooleanProperty(TEXT("win.frame.captionGradientsOn"), GetBooleanParameter(SPI_GETGRADIENTCAPTIONS));
+ SetBooleanProperty(TEXT("win.item.hotTrackingOn"), GetBooleanParameter(SPI_GETHOTTRACKING));
- if (IS_WIN2000) {
- SetBooleanProperty(TEXT("win.menu.keyboardCuesOn"), GetBooleanParameter(SPI_GETKEYBOARDCUES));
- }
+ SetBooleanProperty(TEXT("win.menu.keyboardCuesOn"), GetBooleanParameter(SPI_GETKEYBOARDCUES));
// High contrast accessibility property
HIGHCONTRAST contrast;
@@ -557,21 +529,19 @@ void AwtDesktopProperties::GetOtherParameters() {
SetBooleanProperty(TEXT("win.highContrast.on"), FALSE);
}
- if (fn_SHGetSettings != NULL) {
- SHELLFLAGSTATE sfs;
- fn_SHGetSettings(&sfs, SSF_SHOWALLOBJECTS | SSF_SHOWATTRIBCOL);
- if (sfs.fShowAllObjects) {
- SetBooleanProperty(TEXT("awt.file.showHiddenFiles"), TRUE);
- }
- else {
- SetBooleanProperty(TEXT("awt.file.showHiddenFiles"), FALSE);
- }
- if (sfs.fShowAttribCol) {
- SetBooleanProperty(TEXT("awt.file.showAttribCol"), TRUE);
- }
- else {
- SetBooleanProperty(TEXT("awt.file.showAttribCol"), FALSE);
- }
+ SHELLFLAGSTATE sfs;
+ ::SHGetSettings(&sfs, SSF_SHOWALLOBJECTS | SSF_SHOWATTRIBCOL);
+ if (sfs.fShowAllObjects) {
+ SetBooleanProperty(TEXT("awt.file.showHiddenFiles"), TRUE);
+ }
+ else {
+ SetBooleanProperty(TEXT("awt.file.showHiddenFiles"), FALSE);
+ }
+ if (sfs.fShowAttribCol) {
+ SetBooleanProperty(TEXT("awt.file.showAttribCol"), TRUE);
+ }
+ else {
+ SetBooleanProperty(TEXT("awt.file.showAttribCol"), FALSE);
}
LPTSTR value;
@@ -667,7 +637,7 @@ UINT AwtDesktopProperties::GetIntegerParameter(UINT spi) {
}
void AwtDesktopProperties::SetStringProperty(LPCTSTR propName, LPTSTR value) {
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
GetEnv()->CallVoidMethod(self,
AwtDesktopProperties::setStringPropertyID,
key, JNU_NewStringPlatform(GetEnv(), value));
@@ -675,7 +645,7 @@ void AwtDesktopProperties::SetStringProperty(LPCTSTR propName, LPTSTR value) {
}
void AwtDesktopProperties::SetIntegerProperty(LPCTSTR propName, int value) {
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
GetEnv()->CallVoidMethod(self,
AwtDesktopProperties::setIntegerPropertyID,
key, (jint)value);
@@ -683,7 +653,7 @@ void AwtDesktopProperties::SetIntegerProperty(LPCTSTR propName, int value) {
}
void AwtDesktopProperties::SetBooleanProperty(LPCTSTR propName, BOOL value) {
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
GetEnv()->CallVoidMethod(self,
AwtDesktopProperties::setBooleanPropertyID,
key, value ? JNI_TRUE : JNI_FALSE);
@@ -691,7 +661,7 @@ void AwtDesktopProperties::SetBooleanProperty(LPCTSTR propName, BOOL value) {
}
void AwtDesktopProperties::SetColorProperty(LPCTSTR propName, DWORD value) {
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
GetEnv()->CallVoidMethod(self,
AwtDesktopProperties::setColorPropertyID,
key, GetRValue(value), GetGValue(value),
@@ -743,7 +713,7 @@ void AwtDesktopProperties::SetFontProperty(HDC dc, int fontID,
style |= java_awt_Font_ITALIC;
}
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
GetEnv()->CallVoidMethod(self,
AwtDesktopProperties::setFontPropertyID,
key, fontName, style, pointSize);
@@ -761,7 +731,7 @@ void AwtDesktopProperties::SetFontProperty(LPCTSTR propName, const LOGFONT & fon
jint pointSize;
jint style;
- fontName = JNU_NewStringPlatform(GetEnv(), font.lfFaceName);
+ fontName = JNU_NewStringPlatform(GetEnv(), const_cast<LPWSTR>(font.lfFaceName));
#if 0
HDC hdc;
@@ -784,7 +754,7 @@ void AwtDesktopProperties::SetFontProperty(LPCTSTR propName, const LOGFONT & fon
style |= java_awt_Font_ITALIC;
}
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
GetEnv()->CallVoidMethod(self, AwtDesktopProperties::setFontPropertyID,
key, fontName, style, pointSize);
@@ -793,8 +763,8 @@ void AwtDesktopProperties::SetFontProperty(LPCTSTR propName, const LOGFONT & fon
}
void AwtDesktopProperties::SetSoundProperty(LPCTSTR propName, LPCTSTR winEventName) {
- jstring key = JNU_NewStringPlatform(GetEnv(), propName);
- jstring event = JNU_NewStringPlatform(GetEnv(), winEventName);
+ jstring key = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(propName));
+ jstring event = JNU_NewStringPlatform(GetEnv(), const_cast<LPTSTR>(winEventName));
GetEnv()->CallVoidMethod(self,
AwtDesktopProperties::setSoundPropertyID,
key, event);
@@ -805,9 +775,9 @@ void AwtDesktopProperties::SetSoundProperty(LPCTSTR propName, LPCTSTR winEventNa
void AwtDesktopProperties::PlayWindowsSound(LPCTSTR event) {
// stop any currently playing sounds
- AwtWinMM::PlaySoundWrapper(NULL, NULL, SND_PURGE);
+ ::PlaySound(NULL, NULL, SND_PURGE);
// play the sound for the given event name
- AwtWinMM::PlaySoundWrapper(event, NULL, SND_ASYNC|SND_ALIAS|SND_NODEFAULT);
+ ::PlaySound(event, NULL, SND_ASYNC|SND_ALIAS|SND_NODEFAULT);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -852,16 +822,6 @@ JNIEXPORT void JNICALL
Java_sun_awt_windows_WDesktopProperties_init(JNIEnv *env, jobject self) {
TRY;
- // Open shell32.dll, get the symbol for SHGetSettings
- libShell32 = LoadLibrary(TEXT("shell32.dll"));
- if (libShell32 == NULL) {
- fn_SHGetSettings = NULL;
- }
- else {
- fn_SHGetSettings = (SHGetSettingsType)GetProcAddress(
- libShell32, "SHGetSettings");
- }
-
new AwtDesktopProperties(self);
CATCH_BAD_ALLOC;
diff --git a/src/windows/native/sun/windows/awt_Dialog.cpp b/src/windows/native/sun/windows/awt_Dialog.cpp
index 948fc0883..ecf74549d 100644
--- a/src/windows/native/sun/windows/awt_Dialog.cpp
+++ b/src/windows/native/sun/windows/awt_Dialog.cpp
@@ -132,14 +132,13 @@ AwtDialog* AwtDialog::Create(jobject peer, jobject parent)
dialog = new AwtDialog();
{
- int colorId = IS_WIN4X ? COLOR_3DFACE : COLOR_WINDOW;
+ int colorId = COLOR_3DFACE;
DWORD style = WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN;
if (hwndParent != NULL) {
style |= WS_POPUP;
}
style &= ~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX);
- DWORD exStyle = IS_WIN4X ? WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME
- : 0;
+ DWORD exStyle = WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
if (GetRTL()) {
exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR;
@@ -663,7 +662,7 @@ void AwtDialog::_SetIMMOption(void *param)
int badAlloc = 0;
LPCTSTR coption;
- LPTSTR empty = TEXT("InputMethod");
+ LPCTSTR empty = TEXT("InputMethod");
AwtDialog *d = NULL;
PDATA pData;
diff --git a/src/windows/native/sun/windows/awt_DnDDS.cpp b/src/windows/native/sun/windows/awt_DnDDS.cpp
index 31ee7edcb..bb2ac5664 100644
--- a/src/windows/native/sun/windows/awt_DnDDS.cpp
+++ b/src/windows/native/sun/windows/awt_DnDDS.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. 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
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include "jlong.h"
#include "awt_DataTransferer.h"
#include "awt_DnDDS.h"
@@ -36,8 +37,6 @@
#include "sun_awt_dnd_SunDragSourceContextPeer.h"
#include "sun_awt_windows_WDragSourceContextPeer.h"
-#include <stdio.h>
-#include <stdlib.h>
#include <memory.h>
#include <shlobj.h>
diff --git a/src/windows/native/sun/windows/awt_DnDDT.cpp b/src/windows/native/sun/windows/awt_DnDDT.cpp
index 8940a3026..fce80f468 100644
--- a/src/windows/native/sun/windows/awt_DnDDT.cpp
+++ b/src/windows/native/sun/windows/awt_DnDDT.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. 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
@@ -30,11 +30,8 @@
#include "awt_Toolkit.h"
#include "java_awt_dnd_DnDConstants.h"
#include "sun_awt_windows_WDropTargetContextPeer.h"
-#include "awt_dlls.h"
#include "awt_Container.h"
-#include <stdio.h>
-#include <stdlib.h>
#include <memory.h>
#include <shellapi.h>
@@ -555,8 +552,7 @@ jobject AwtDropTarget::GetData(jlong fmt) {
break;
}
case TYMED_FILE: {
- jobject local = JNU_NewStringPlatform(env, (LPCTSTR)
- stgmedium.lpszFileName);
+ jobject local = JNU_NewStringPlatform(env, stgmedium.lpszFileName);
jstring fileName = (jstring)env->NewGlobalRef(local);
env->DeleteLocalRef(local);
diff --git a/src/windows/native/sun/windows/awt_DrawingSurface.cpp b/src/windows/native/sun/windows/awt_DrawingSurface.cpp
index d1ef05684..b7b7913b7 100644
--- a/src/windows/native/sun/windows/awt_DrawingSurface.cpp
+++ b/src/windows/native/sun/windows/awt_DrawingSurface.cpp
@@ -24,6 +24,8 @@
*/
#define _JNI_IMPLEMENTATION_
+
+#include "awt.h"
#include "awt_DrawingSurface.h"
#include "awt_Component.h"
diff --git a/src/windows/native/sun/windows/awt_FileDialog.cpp b/src/windows/native/sun/windows/awt_FileDialog.cpp
index 08d839fc4..84339e40e 100644
--- a/src/windows/native/sun/windows/awt_FileDialog.cpp
+++ b/src/windows/native/sun/windows/awt_FileDialog.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -27,7 +27,6 @@
#include "awt_FileDialog.h"
#include "awt_Dialog.h"
#include "awt_Toolkit.h"
-#include "awt_dlls.h"
#include "ComCtl32Util.h"
#include <commdlg.h>
#include <cderr.h>
@@ -65,7 +64,7 @@ AwtFileDialog::Initialize(JNIEnv *env, jstring filterDescription)
{
int length = env->GetStringLength(filterDescription);
DASSERT(length + 1 < MAX_FILTER_STRING);
- LPCTSTR tmp = (LPTSTR)JNU_GetStringPlatformChars(env, filterDescription, NULL);
+ LPCTSTR tmp = JNU_GetStringPlatformChars(env, filterDescription, NULL);
_tcscpy(s_fileFilterString, tmp);
JNU_ReleaseStringPlatformChars(env, filterDescription, tmp);
@@ -156,7 +155,7 @@ FileDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
LPITEMIDLIST pidl = (LPITEMIDLIST)notifyEx->pidl;
// Get the filename and directory
TCHAR szPath[MAX_PATH];
- if (!get_path_from_idlist(pidl,szPath)) {
+ if (!::SHGetPathFromIDList(pidl, szPath)) {
return TRUE;
}
jstring strPath = JNU_NewStringPlatform(env, szPath);
@@ -190,7 +189,7 @@ AwtFileDialog::Show(void *p)
WCHAR unicodeChar = L' ';
LPTSTR fileBuffer = NULL;
LPTSTR currentDirectory = NULL;
- AWTOPENFILENAME ofn;
+ OPENFILENAME ofn;
jint mode = 0;
BOOL result = FALSE;
DWORD dlgerr;
@@ -222,7 +221,7 @@ AwtFileDialog::Show(void *p)
HWND hwndOwner = awtParent ? awtParent->GetHWnd() : NULL;
if (title == NULL || env->GetStringLength(title)==0) {
- title = env->NewString(&unicodeChar, 1);
+ title = JNU_NewStringPlatform(env, &unicodeChar);
}
JavaStringBuffer titleBuffer(env, title);
@@ -243,14 +242,7 @@ AwtFileDialog::Show(void *p)
memset(&ofn, 0, sizeof(ofn));
- // According to the MSDN docs, lStructSize must be set to
- // OPENFILENAME_SIZE_VERSION_400 on NT4.0.
- if (IS_NT && !(IS_WIN2000)) {
- ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
- }
- else {
ofn.lStructSize = sizeof(ofn);
- }
ofn.lpstrFilter = s_fileFilterString;
ofn.nFilterIndex = 1;
/*
@@ -289,9 +281,6 @@ AwtFileDialog::Show(void *p)
mode = env->GetIntField(target, AwtFileDialog::modeID);
- // Fix for 4364256 : call load_shell_procs()
- load_shell_procs();
-
AwtDialog::CheckInstallModalHook();
// show the Win32 file dialog
@@ -304,7 +293,7 @@ AwtFileDialog::Show(void *p)
// If the dialog is not shown because of invalid file name
// replace the file name by empty string.
if (!result) {
- dlgerr = AwtCommDialog::CommDlgExtendedError();
+ dlgerr = ::CommDlgExtendedError();
if (dlgerr == FNERR_INVALIDFILENAME) {
_tcscpy(fileBuffer, TEXT(""));
if (mode == java_awt_FileDialog_LOAD) {
@@ -326,7 +315,7 @@ AwtFileDialog::Show(void *p)
// Report result to peer.
if (result) {
jstring tmpJString = (_tcslen(ofn.lpstrFile) == 0 ?
- env->NewStringUTF("") :
+ JNU_NewStringPlatform(env, L"") :
JNU_NewStringPlatform(env, ofn.lpstrFile));
env->CallVoidMethod(peer, AwtFileDialog::handleSelectedMID, tmpJString);
env->DeleteLocalRef(tmpJString);
@@ -362,20 +351,18 @@ AwtFileDialog::Show(void *p)
}
BOOL
-AwtFileDialog::GetOpenFileName(LPAWTOPENFILENAME data) {
- AwtCommDialog::load_comdlg_procs();
+AwtFileDialog::GetOpenFileName(LPOPENFILENAME data) {
return static_cast<BOOL>(reinterpret_cast<INT_PTR>(
AwtToolkit::GetInstance().InvokeFunction((void*(*)(void*))
- AwtCommDialog::GetOpenFileNameWrapper, data)));
+ ::GetOpenFileName, data)));
}
BOOL
-AwtFileDialog::GetSaveFileName(LPAWTOPENFILENAME data) {
- AwtCommDialog::load_comdlg_procs();
+AwtFileDialog::GetSaveFileName(LPOPENFILENAME data) {
return static_cast<BOOL>(reinterpret_cast<INT_PTR>(
AwtToolkit::GetInstance().InvokeFunction((void *(*)(void *))
- AwtCommDialog::GetSaveFileNameWrapper, data)));
+ ::GetSaveFileName, data)));
}
diff --git a/src/windows/native/sun/windows/awt_FileDialog.h b/src/windows/native/sun/windows/awt_FileDialog.h
index 9a324f68f..82638d307 100644
--- a/src/windows/native/sun/windows/awt_FileDialog.h
+++ b/src/windows/native/sun/windows/awt_FileDialog.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. 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
@@ -36,43 +36,6 @@
#include "java_awt_FileDialog.h"
#include "sun_awt_windows_WFileDialogPeer.h"
-// The VC6 headers don't include this, but it's necessary for
-// backward-compatibility with NT4.0, so we fake it.
-#ifndef OPENFILENAME_SIZE_VERSION_400
- // Determined via sizeof(OPENFILENAME)
- #define OPENFILENAME_SIZE_VERSION_400 76
-#endif
-
-// 4859390
-// For the Places Bar to show up, we need the "full" OPENFILENAME struct
-typedef struct tagAWTOFN {
- DWORD lStructSize;
- HWND hwndOwner;
- HINSTANCE hInstance;
- LPCTSTR lpstrFilter;
- LPTSTR lpstrCustomFilter;
- DWORD nMaxCustFilter;
- DWORD nFilterIndex;
- LPTSTR lpstrFile;
- DWORD nMaxFile;
- LPTSTR lpstrFileTitle;
- DWORD nMaxFileTitle;
- LPCTSTR lpstrInitialDir;
- LPCTSTR lpstrTitle;
- DWORD Flags;
- WORD nFileOffset;
- WORD nFileExtension;
- LPCTSTR lpstrDefExt;
- LPARAM lCustData;
- LPOFNHOOKPROC lpfnHook;
- LPCTSTR lpTemplateName;
-//#if (_WIN32_WINNT >= 0x0500)
- void * pvReserved;
- DWORD dwReserved;
- DWORD FlagsEx;
-//#endif // (_WIN32_WINNT >= 0x0500)
-} AWTOPENFILENAME, *LPAWTOPENFILENAME;
-
/************************************************************************
* AwtFileDialog class
*/
@@ -96,8 +59,8 @@ public:
static void Initialize(JNIEnv *env, jstring filterDescription);
static void Show(void *peer);
- static BOOL GetOpenFileName(LPAWTOPENFILENAME);
- static BOOL GetSaveFileName(LPAWTOPENFILENAME);
+ static BOOL GetOpenFileName(LPOPENFILENAME);
+ static BOOL GetSaveFileName(LPOPENFILENAME);
virtual BOOL InheritsNativeMouseWheelBehavior();
diff --git a/src/windows/native/sun/windows/awt_Font.cpp b/src/windows/native/sun/windows/awt_Font.cpp
index 8dc9d5a67..6815b1afa 100644
--- a/src/windows/native/sun/windows/awt_Font.cpp
+++ b/src/windows/native/sun/windows/awt_Font.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include <math.h>
#include "jlong.h"
#include "awt_Font.h"
@@ -195,7 +196,7 @@ AwtFont* AwtFont::GetFont(JNIEnv *env, jobject font,
}
// Get suitable CHARSET from charset string provided by font configuration.
-static int GetNativeCharset(WCHAR* name)
+static int GetNativeCharset(LPCWSTR name)
{
if (wcsstr(name, L"ANSI_CHARSET"))
return ANSI_CHARSET;
@@ -259,7 +260,7 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
cfnum = 0;
}
- WCHAR* wName;
+ LPCWSTR wName;
awtFont = new AwtFont(cfnum, env, font);
@@ -269,9 +270,7 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
if (cfnum > 0) {
// Ask peer class for the text component font name
jstring jTextComponentFontName = GetTextComponentFontName(env, font);
- WCHAR* textComponentFontName = TO_WSTRING(jTextComponentFontName);
-
- env->DeleteLocalRef(jTextComponentFontName);
+ LPCWSTR textComponentFontName = JNU_GetStringPlatformChars(env, jTextComponentFontName, NULL);
awtFont->m_textInput = -1;
for (int i = 0; i < cfnum; i++) {
@@ -282,13 +281,13 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
jstring nativeName =
(jstring)env->GetObjectField(fontDescriptor,
AwtFont::nativeNameID);
- wName = TO_WSTRING(nativeName);
+ wName = JNU_GetStringPlatformChars(env, nativeName, NULL);
DASSERT(wName);
//On NT platforms, if the font is not Symbol or Dingbats
//use "W" version of Win32 APIs directly, info the FontDescription
//no need to convert characters from Unicode to locale encodings.
- if (IS_NT && GetNativeCharset(wName) != SYMBOL_CHARSET) {
+ if (GetNativeCharset(wName) != SYMBOL_CHARSET) {
env->SetBooleanField(fontDescriptor, AwtFont::useUnicodeID, TRUE);
}
@@ -299,10 +298,12 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
(wcscmp(wName, textComponentFontName) == 0)) {
awtFont->m_textInput = i;
}
- HFONT hfonttmp = CreateHFont(wName, fontStyle, fontSize,
+ HFONT hfonttmp = CreateHFont(const_cast<LPWSTR>(wName), fontStyle, fontSize,
angle, awScale);
awtFont->m_hFont[i] = hfonttmp;
+ JNU_ReleaseStringPlatformChars(env, nativeName, wName);
+
env->DeleteLocalRef(fontDescriptor);
env->DeleteLocalRef(nativeName);
}
@@ -311,11 +312,14 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
// to first component
awtFont->m_textInput = 0;
}
+
+ JNU_ReleaseStringPlatformChars(env, jTextComponentFontName, textComponentFontName);
+ env->DeleteLocalRef(jTextComponentFontName);
} else {
// Instantiation for English version.
jstring fontName = (jstring)env->GetObjectField(font,
AwtFont::nameID);
- wName = TO_WSTRING(fontName);
+ wName = JNU_GetStringPlatformChars(env, fontName, NULL);
WCHAR* wEName;
if (!wcscmp(wName, L"Helvetica") || !wcscmp(wName, L"SansSerif")) {
@@ -338,6 +342,9 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
awtFont->m_textInput = 0;
awtFont->m_hFont[0] = CreateHFont(wEName, fontStyle, fontSize,
angle, awScale);
+
+ JNU_ReleaseStringPlatformChars(env, fontName, wName);
+
env->DeleteLocalRef(fontName);
}
/* The several callers of this method also set the pData field.
@@ -381,7 +388,7 @@ static void strip_tail(wchar_t* text, wchar_t* tail) { // strips tail and any po
}
-static HFONT CreateHFont_sub(WCHAR* name, int style, int height,
+static HFONT CreateHFont_sub(LPCWSTR name, int style, int height,
int angle=0, float awScale=1.0f)
{
LOGFONTW logFont;
@@ -420,18 +427,7 @@ static HFONT CreateHFont_sub(WCHAR* name, int style, int height,
strip_tail(tmpname,L"Italic");
strip_tail(tmpname,L"Bold");
wcscpy(&(logFont.lfFaceName[0]), tmpname);
- HFONT hFont;
- if (IS_WIN95) {
-#ifdef WIN32
- DASSERT(IS_WIN95);
-#endif
- HDC hdc = ::GetDC(NULL);
- ::EnumFontFamiliesEx(hdc, &logFont, (FONTENUMPROC)FindFamilyName,
- (LPARAM)tmpname, 0L);
- ::ReleaseDC(NULL, hdc);
- wcscpy(&logFont.lfFaceName[0], tmpname);
- }
- hFont = ::CreateFontIndirectW(&logFont);
+ HFONT hFont = ::CreateFontIndirect(&logFont);
DASSERT(hFont != NULL);
// get a expanded or condensed version if its specified.
if (awScale != 1.0f) {
@@ -446,7 +442,7 @@ static HFONT CreateHFont_sub(WCHAR* name, int style, int height,
}
avgWidth = tm.tmAveCharWidth;
logFont.lfWidth = (LONG)((fabs)(avgWidth*awScale));
- hFont = CreateFontIndirectW(&logFont);
+ hFont = ::CreateFontIndirect(&logFont);
DASSERT(hFont != NULL);
VERIFY(::ReleaseDC(0, hDC));
}
@@ -460,15 +456,8 @@ HFONT AwtFont::CreateHFont(WCHAR* name, int style, int height,
WCHAR longName[80];
// 80 > (max face name(=30) + strlen("CHINESEBIG5_CHARSET"))
// longName doesn't have to be printable. So, it is OK not to convert.
- if (IS_NT) {
- //wsprintfW only works on NT. See bugid 4123362
- wsprintfW(longName, L"%ls-%d-%d", name, style, height);
- } else {
-#ifdef WIN32
- DASSERT(IS_WIN95);
-#endif
- swprintf(longName, L"%ls-%d-%d", name, style, height);
- }
+
+ wsprintf(longName, L"%ls-%d-%d", name, style, height);
HFONT hFont = NULL;
@@ -682,28 +671,16 @@ SIZE AwtFont::DrawStringSize_sub(jstring str, HDC hDC,
if (arrayLength == 0) {
int length = env->GetStringLength(str);
- WCHAR* string = TO_WSTRING(str);
+ LPCWSTR strW = JNU_GetStringPlatformChars(env, str, NULL);
VERIFY(::SelectObject(hDC, awtFont->GetHFont()));
if (AwtComponent::GetRTLReadingOrder()){
- if (IS_WIN95) {
- // Start of conversion Code to fix arabic shaping problems
- // with unicode support in win 95
- LPSTR buffer = (LPSTR) alloca((wcslen(string) + 1) * 2);
- int count = ::WideCharToMultiByte(codePage, 0, string, length,
- buffer,
- static_cast<int>((wcslen(string) + 1) * 2),
- NULL, NULL);
- VERIFY(!draw || ::ExtTextOutA(hDC, x, y, ETO_RTLREADING, NULL,
- buffer, count, NULL));
- // End Of Conversion Code
- } else {
- VERIFY(!draw || ::ExtTextOutW(hDC, x, y, ETO_RTLREADING, NULL,
- string, length, NULL));
- }
+ VERIFY(!draw || ::ExtTextOut(hDC, x, y, ETO_RTLREADING, NULL,
+ strW, length, NULL));
} else {
- VERIFY(!draw || ::TextOutW(hDC, x, y, string, length));
+ VERIFY(!draw || ::TextOut(hDC, x, y, strW, length));
}
- VERIFY(::GetTextExtentPoint32W(hDC, string, length, &size));
+ VERIFY(::GetTextExtentPoint32(hDC, strW, length, &size));
+ JNU_ReleaseStringPlatformChars(env, str, strW);
} else {
for (int i = 0; i < arrayLength; i = i + 2) {
jobject fontDescriptor = env->GetObjectArrayElement(array, i);
@@ -732,7 +709,7 @@ SIZE AwtFont::DrawStringSize_sub(jstring str, HDC hDC,
* extend buflen and bad things will happen.
*/
unsigned char* buffer = NULL;
- jboolean unicodeUsed = env->GetBooleanField(fontDescriptor,AwtFont::useUnicodeID);
+ jboolean unicodeUsed = env->GetBooleanField(fontDescriptor, AwtFont::useUnicodeID);
try {
buffer = (unsigned char *)
env->GetPrimitiveArrayCritical(convertedBytes, 0);
@@ -1231,7 +1208,7 @@ class CSegTableComponent
public:
CSegTableComponent();
virtual ~CSegTableComponent();
- virtual void Create(LPWSTR name);
+ virtual void Create(LPCWSTR name);
virtual BOOL In(USHORT iChar) { DASSERT(FALSE); return FALSE; };
LPWSTR GetFontName(){
DASSERT(m_lpszFontName != NULL); return m_lpszFontName;
@@ -1254,7 +1231,7 @@ CSegTableComponent::~CSegTableComponent()
}
}
-void CSegTableComponent::Create(LPWSTR name)
+void CSegTableComponent::Create(LPCWSTR name)
{
if (m_lpszFontName != NULL) {
free(m_lpszFontName);
@@ -1453,7 +1430,7 @@ public:
CStdSegTable();
virtual ~CStdSegTable();
BOOL IsEUDC() { return FALSE; };
- virtual void Create(LPWSTR name);
+ virtual void Create(LPCWSTR name);
protected:
void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData);
@@ -1481,7 +1458,7 @@ inline void CStdSegTable::GetData(DWORD dwOffset,
DASSERT(nBytes != GDI_ERROR);
}
-void CStdSegTable::Create(LPWSTR name)
+void CStdSegTable::Create(LPCWSTR name)
{
CSegTableComponent::Create(name);
@@ -1509,7 +1486,7 @@ public:
CEUDCSegTable();
virtual ~CEUDCSegTable();
BOOL IsEUDC() { return TRUE; };
- virtual void Create(LPWSTR name);
+ virtual void Create(LPCWSTR name);
protected:
void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData);
@@ -1543,7 +1520,7 @@ inline void CEUDCSegTable::GetData(DWORD dwOffset,
DASSERT(dwRead == cbData);
}
-void CEUDCSegTable::Create(LPWSTR name)
+void CEUDCSegTable::Create(LPCWSTR name)
{
typedef struct tagHEAD{
FIXED sfnt_version;
@@ -1564,19 +1541,8 @@ typedef struct tagENTRY{
// create EUDC font file and make EUDCSegTable
// after wrapper function for CreateFileW, we use only CreateFileW
- if (IS_NT) {
- m_hTmpFile = ::CreateFileW(name, GENERIC_READ,
- FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- } else {
-#ifdef WIN32
- DASSERT(IS_WIN95);
-#endif
- char szFileName[_MAX_PATH];
- ::WideCharToMultiByte(CP_ACP, 0, name, -1,
- szFileName, sizeof(szFileName), NULL, NULL);
- m_hTmpFile = ::CreateFileA(szFileName, GENERIC_READ,
- FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- }
+ m_hTmpFile = ::CreateFile(name, GENERIC_READ,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (m_hTmpFile == INVALID_HANDLE_VALUE){
m_hTmpFile = NULL;
return;
@@ -1654,10 +1620,10 @@ void CSegTableManagerComponent::MakeBiggerTable()
class CSegTableManager : public CSegTableManagerComponent
{
public:
- CSegTable* GetTable(LPWSTR lpszFontName, BOOL fEUDC);
+ CSegTable* GetTable(LPCWSTR lpszFontName, BOOL fEUDC);
};
-CSegTable* CSegTableManager::GetTable(LPWSTR lpszFontName, BOOL fEUDC)
+CSegTable* CSegTableManager::GetTable(LPCWSTR lpszFontName, BOOL fEUDC)
{
for (int i = 0; i < m_nTable; i++) {
if ((((CSegTable*)m_tables[i])->IsEUDC() == fEUDC) &&
@@ -1685,7 +1651,7 @@ class CCombinedSegTable : public CSegTableComponent
{
public:
CCombinedSegTable();
- void Create(LPWSTR name);
+ void Create(LPCWSTR name);
BOOL In(USHORT iChar);
private:
@@ -1807,7 +1773,7 @@ void CCombinedSegTable::GetEUDCFileName(LPWSTR lpszFileName, int cchFileName)
wcscpy(m_szDefaultEUDCFile, lpszFileName);
}
-void CCombinedSegTable::Create(LPWSTR name)
+void CCombinedSegTable::Create(LPCWSTR name)
{
CSegTableComponent::Create(name);
@@ -1840,10 +1806,10 @@ BOOL CCombinedSegTable::In(USHORT iChar)
class CCombinedSegTableManager : public CSegTableManagerComponent
{
public:
- CCombinedSegTable* GetTable(LPWSTR lpszFontName);
+ CCombinedSegTable* GetTable(LPCWSTR lpszFontName);
};
-CCombinedSegTable* CCombinedSegTableManager::GetTable(LPWSTR lpszFontName)
+CCombinedSegTable* CCombinedSegTableManager::GetTable(LPCWSTR lpszFontName)
{
for (int i = 0; i < m_nTable; i++) {
if (wcscmp(m_tables[i]->GetFontName(),lpszFontName) == 0)
@@ -1901,8 +1867,9 @@ Java_sun_awt_windows_WDefaultFontCharset_canConvert(JNIEnv *env, jobject self,
jstring fontName = (jstring)env->GetObjectField(self, AwtFont::fontNameID);
DASSERT(fontName != NULL);
- LPWSTR fontNameWStr = TO_WSTRING(fontName);
- CCombinedSegTable* pTable = tableManager.GetTable(fontNameWStr);
+ LPCWSTR fontNameW = JNU_GetStringPlatformChars(env, fontName, NULL);
+ CCombinedSegTable* pTable = tableManager.GetTable(fontNameW);
+ JNU_ReleaseStringPlatformChars(env, fontName, fontNameW);
return (pTable->In((USHORT) ch) ? JNI_TRUE : JNI_FALSE);
CATCH_BAD_ALLOC_RET(FALSE);
diff --git a/src/windows/native/sun/windows/awt_Font.h b/src/windows/native/sun/windows/awt_Font.h
index c1edd9d0e..ac537a122 100644
--- a/src/windows/native/sun/windows/awt_Font.h
+++ b/src/windows/native/sun/windows/awt_Font.h
@@ -28,7 +28,6 @@
#include "awt.h"
#include "awt_Object.h"
-#include "awt_Unicode.h"
#include "java_awt_Font.h"
#include "sun_awt_windows_WFontMetrics.h"
diff --git a/src/windows/native/sun/windows/awt_Frame.cpp b/src/windows/native/sun/windows/awt_Frame.cpp
index 37e8546ed..60738e693 100644
--- a/src/windows/native/sun/windows/awt_Frame.cpp
+++ b/src/windows/native/sun/windows/awt_Frame.cpp
@@ -1472,7 +1472,7 @@ void AwtFrame::_SetIMMOption(void *param)
int badAlloc = 0;
LPCTSTR coption;
- LPTSTR empty = TEXT("InputMethod");
+ LPCTSTR empty = TEXT("InputMethod");
AwtFrame *f = NULL;
PDATA pData;
diff --git a/src/windows/native/sun/windows/awt_InputMethod.cpp b/src/windows/native/sun/windows/awt_InputMethod.cpp
index ff944b0e8..bd78cdaf3 100644
--- a/src/windows/native/sun/windows/awt_InputMethod.cpp
+++ b/src/windows/native/sun/windows/awt_InputMethod.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. 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
@@ -580,18 +580,10 @@ HKL getDefaultKeyboardLayout() {
DWORD cbHKL = 16;
LPTSTR end;
- if (IS_NT) {
- ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey);
- } else {
- ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("keyboard layout\\preload\\1"), NULL, KEY_READ, &hKey);
- }
+ ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey);
if (ret == ERROR_SUCCESS) {
- if (IS_NT) {
- ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL);
- } else {
- ret = ::RegQueryValueEx(hKey, NULL, 0, 0, szHKL, &cbHKL);
- }
+ ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL);
if (ret == ERROR_SUCCESS) {
hkl = reinterpret_cast<HKL>(static_cast<INT_PTR>(
diff --git a/src/windows/native/sun/windows/awt_InputTextInfor.cpp b/src/windows/native/sun/windows/awt_InputTextInfor.cpp
index 82f54a4ea..3c26fbb95 100644
--- a/src/windows/native/sun/windows/awt_InputTextInfor.cpp
+++ b/src/windows/native/sun/windows/awt_InputTextInfor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2005-2008 Sun Microsystems, Inc. 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,10 +22,9 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
-#include <windows.h>
-#include <jni.h>
-#include <imm.h>
+
#include "awt.h"
+#include <imm.h>
#include "awt_Component.h"
#include "awt_InputTextInfor.h"
@@ -91,12 +90,12 @@ AwtInputTextInfor::GetContextData(HIMC hIMC, const LPARAM flags) {
LONG cbData[5] = {0};
LPVOID lpData[5] = {NULL};
for (int i = startIndex, j = 0; i <= endIndex; i++, j++) {
- cbData[j] = ImmGetCompositionStringW(hIMC, GCS_INDEX[i], NULL, 0);
+ cbData[j] = ::ImmGetCompositionString(hIMC, GCS_INDEX[i], NULL, 0);
if (cbData[j] == 0) {
lpData[j] = NULL;
} else {
LPBYTE lpTemp = new BYTE[cbData[j]];
- cbData[j] = ImmGetCompositionStringW(hIMC, GCS_INDEX[i], lpTemp, cbData[j]);
+ cbData[j] = ::ImmGetCompositionString(hIMC, GCS_INDEX[i], lpTemp, cbData[j]);
if (IMM_ERROR_GENERAL != cbData[j]) {
lpData[j] = (LPVOID)lpTemp;
} else {
@@ -126,7 +125,7 @@ AwtInputTextInfor::GetContextData(HIMC hIMC, const LPARAM flags) {
// Get the cursor position
if (flags & GCS_COMPSTR) {
- m_cursorPosW = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS,
+ m_cursorPosW = ::ImmGetCompositionString(hIMC, GCS_CURSORPOS,
NULL, 0);
}
@@ -185,9 +184,11 @@ AwtInputTextInfor::~AwtInputTextInfor() {
jstring AwtInputTextInfor::MakeJavaString(JNIEnv* env, LPWSTR lpStrW, int cStrW) {
- if (env == NULL || lpStrW == NULL || cStrW == 0) return NULL;
-
- return env->NewString(lpStrW, cStrW);
+ if (env == NULL || lpStrW == NULL || cStrW == 0) {
+ return NULL;
+ } else {
+ return env->NewString(reinterpret_cast<jchar*>(lpStrW), cStrW);
+ }
}
//
@@ -232,7 +233,7 @@ int AwtInputTextInfor::GetClauseInfor(int*& lpBndClauseW, jstring*& lpReadingCla
LCID lcJPN = MAKELCID(MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT),SORT_DEFAULT);
// Reading string is given in half width katakana in Japanese Windows
// Convert it to full width katakana.
- int cFWStrW = LCMapStringW( lcJPN, LCMAP_FULLWIDTH, lpHWStrW, cHWStrW, NULL, 0 );
+ int cFWStrW = ::LCMapString(lcJPN, LCMAP_FULLWIDTH, lpHWStrW, cHWStrW, NULL, 0);
LPWSTR lpFWStrW;
try {
lpFWStrW = new WCHAR[cFWStrW];
@@ -244,7 +245,7 @@ int AwtInputTextInfor::GetClauseInfor(int*& lpBndClauseW, jstring*& lpReadingCla
throw;
}
- LCMapStringW( lcJPN, LCMAP_FULLWIDTH, lpHWStrW, cHWStrW, lpFWStrW, cFWStrW );
+ ::LCMapString(lcJPN, LCMAP_FULLWIDTH, lpHWStrW, cHWStrW, lpFWStrW, cFWStrW);
readingClauseW[cls] = MakeJavaString(env, lpFWStrW, cFWStrW);
delete [] lpFWStrW;
} else {
@@ -252,7 +253,7 @@ int AwtInputTextInfor::GetClauseInfor(int*& lpBndClauseW, jstring*& lpReadingCla
}
}
else {
- readingClauseW[cls] = MakeJavaString(env, (LPWSTR)NULL, 0);
+ readingClauseW[cls] = NULL;
}
}
diff --git a/src/windows/native/sun/windows/awt_List.cpp b/src/windows/native/sun/windows/awt_List.cpp
index 9000aaa68..20844de78 100644
--- a/src/windows/native/sun/windows/awt_List.cpp
+++ b/src/windows/native/sun/windows/awt_List.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -27,7 +27,6 @@
#include "awt_KeyboardFocusManager.h"
#include "awt_Canvas.h"
#include "awt_Dimension.h"
-#include "awt_Unicode.h"
#include "awt_Toolkit.h"
#include "awt_Window.h"
@@ -112,9 +111,8 @@ AwtList* AwtList::Create(jobject peer, jobject parent)
DWORD wrapExStyle = 0;
DWORD style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL |
- LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_OWNERDRAWFIXED |
- (IS_WIN4X ? 0 : WS_BORDER);
- DWORD exStyle = IS_WIN4X ? WS_EX_CLIENTEDGE : 0;
+ LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_OWNERDRAWFIXED;
+ DWORD exStyle = WS_EX_CLIENTEDGE;
/*
* NOTE: WS_VISIBLE is always set for the listbox. Listbox
@@ -571,10 +569,10 @@ MsgRouting AwtList::HandleEvent(MSG *msg, BOOL synthetic)
// operate WM_PRINT to be compatible with the "smooth scrolling" feature.
MsgRouting AwtList::WmPrint(HDC hDC, LPARAM flags)
{
- if (!isWrapperPrint && IS_WIN4X
- && (flags & PRF_CLIENT)
- && (GetStyleEx() & WS_EX_CLIENTEDGE)) {
-
+ if (!isWrapperPrint &&
+ (flags & PRF_CLIENT) &&
+ (GetStyleEx() & WS_EX_CLIENTEDGE))
+ {
int nOriginalDC = ::SaveDC(hDC);
DASSERT(nOriginalDC != 0);
// Save a copy of the DC for WmPrintClient
diff --git a/src/windows/native/sun/windows/awt_MMStub.cpp b/src/windows/native/sun/windows/awt_MMStub.cpp
deleted file mode 100644
index 675f48e78..000000000
--- a/src/windows/native/sun/windows/awt_MMStub.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Copyright 1999-2003 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-#include "awt_MMStub.h"
-
-//---------------------------------------------------------------------------
-// Basic API
-//---------------------------------------------------------------------------
-
-int (WINAPI* g_pfnGetSystemMetrics) (int);
-MHND (WINAPI* g_pfnMonitorFromWindow) (HWND,BOOL);
-MHND (WINAPI* g_pfnMonitorFromRect) (LPCRECT,BOOL);
-MHND (WINAPI* g_pfnMonitorFromPoint) (POINT,BOOL);
-BOOL (WINAPI* g_pfnGetMonitorInfo) (MHND,PMONITOR_INFO);
-BOOL (WINAPI* g_pfnEnumDisplayMonitors) (HDC,LPCRECT,MON_ENUM_CALLBACK_PROC,LPARAM);
-BOOL (WINAPI* g_pfnEnumDisplayDevices) (LPVOID,int,P_DISPLAY_DEVICE,DWORD);
-
-BOOL __initMultipleMonitorStubs(void);
-BOOL __initMultipleMonitorStubs(void)
-{
- static BOOL fInitDone;
- HMODULE hUser32;
- HMODULE hUnicows = UnicowsLoader::GetModuleHandle();
- BOOL retCode = FALSE;
-
- if (fInitDone)
- {
- retCode = g_pfnGetMonitorInfo != NULL;
- goto _RET_;
- }
-
- if ((hUser32 = GetModuleHandle(TEXT("USER32"))) &&
- (*(FARPROC*)&g_pfnGetSystemMetrics = GetProcAddress(hUser32,"GetSystemMetrics")) &&
- (*(FARPROC*)&g_pfnMonitorFromWindow = GetProcAddress(hUser32,"MonitorFromWindow")) &&
- (*(FARPROC*)&g_pfnMonitorFromRect = GetProcAddress(hUser32,"MonitorFromRect")) &&
- (*(FARPROC*)&g_pfnMonitorFromPoint = GetProcAddress(hUser32,"MonitorFromPoint")) &&
- (*(FARPROC*)&g_pfnEnumDisplayMonitors = GetProcAddress(hUser32,"EnumDisplayMonitors")) &&
- (*(FARPROC*)&g_pfnGetMonitorInfo = GetProcAddress(IS_WIN95 ? hUnicows : hUser32,"GetMonitorInfoW")) &&
- (*(FARPROC*)&g_pfnEnumDisplayDevices = GetProcAddress(IS_WIN95 ? hUnicows : hUser32,"EnumDisplayDevicesW")) &&
- (GetSystemMetrics(SM_CXVSCREEN) >= GetSystemMetrics(SM_CXSCREEN)) &&
- (GetSystemMetrics(SM_CYVSCREEN) >= GetSystemMetrics(SM_CYSCREEN)) )
- {
- fInitDone = TRUE;
- retCode = TRUE;
- goto _RET_;
- }
- g_pfnGetSystemMetrics = NULL;
- g_pfnMonitorFromWindow = NULL;
- g_pfnMonitorFromRect = NULL;
- g_pfnMonitorFromPoint = NULL;
- g_pfnGetMonitorInfo = NULL;
- g_pfnEnumDisplayMonitors = NULL;
- g_pfnEnumDisplayDevices = NULL;
-
- fInitDone = TRUE;
- retCode = FALSE;
-
-_RET_:
- return retCode;
-}
-
-int WINAPI _getSystemMetrics(int nCode)
-{
- int retCode;
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnGetSystemMetrics(nCode);
- goto _RET_;
- }
-
- switch( nCode )
- {
- case SM_CMONITORS:
- case SM_SAMEDSPLFORMAT:
- return 1;
-
- case SM_XVSCREEN:
- case SM_YVSCREEN:
- return 0;
-
- case SM_CXVSCREEN:
- nCode = SM_CXSCREEN;
- break;
-
- case SM_CYVSCREEN:
- nCode = SM_CYSCREEN;
- break;
- }
-
- retCode = GetSystemMetrics(nCode);
-_RET_:
- return retCode;
-}
-
-
-MHND WINAPI _monitorFromRect(LPCRECT prScreen, UINT nFlags)
-{
- MHND retCode = NULL;
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnMonitorFromRect(prScreen, nFlags);
- goto _RET_;
- }
-
- if( (prScreen->right < 0) || (prScreen->bottom < 0) )
- {
- goto _RET_;
- }
- {
- POINT pP = {0,0};
-
- pP.x = prScreen->left;
- pP.y = prScreen->top;
-
- retCode = _monitorFromPoint(pP,nFlags);
- }
-
-_RET_:
- return retCode;
-}
-
-MHND WINAPI _monitorFromWindow(HWND hwProbe, UINT nFlags)
-{
- RECT rR;
- MHND retCode = NULL;
-
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnMonitorFromWindow(hwProbe, nFlags);
- goto _RET_;
- }
-
- if( nFlags & (MONITOR_DEFAULT_TO_PRIMARY | MONITOR_DEFAULT_TO_NEAR) )
- {
- retCode = PRIMARY_MONITOR;
- goto _RET_;
- }
-
- if( GetWindowRect(hwProbe, &rR) )
- {
- retCode = _monitorFromRect(&rR, nFlags);
- goto _RET_;
- }
-
-_RET_:
- return retCode;
-}
-
-MHND WINAPI _monitorFromPoint(POINT ptProbe, UINT nFlags)
-{
- MHND retCode = NULL;
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnMonitorFromPoint(ptProbe,nFlags);
- goto _RET_;
- }
-
- if( nFlags & (MONITOR_DEFAULT_TO_PRIMARY | MONITOR_DEFAULT_TO_NEAR) )
- {
- goto _ASSIGN_;
- }
-
- if( (ptProbe.x <= 0) || (ptProbe.x > GetSystemMetrics(SM_CXSCREEN)) )
- {
- goto _RET_;
- }
-
- if( (ptProbe.y <= 0) || (ptProbe.y < GetSystemMetrics(SM_CYSCREEN)) )
- {
- goto _RET_;
- }
-_ASSIGN_:
- retCode = PRIMARY_MONITOR;
-
-_RET_:
- return retCode;
-}
-
-BOOL WINAPI _getMonitorInfo(MHND mhMon, PMONITOR_INFO pmMonInfo)
-{
- RECT rArea;
- BOOL retCode = FALSE;
-
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnGetMonitorInfo(mhMon, pmMonInfo);
- goto _RET_;
- }
-
- if( mhMon != PRIMARY_MONITOR )
- {
- goto _RET_;
- }
-
- if( NULL == pmMonInfo )
- {
- goto _RET_;
- }
-
- if( FALSE == SystemParametersInfo(SPI_GETWORKAREA,0,&rArea,0) )
- {
- goto _RET_;
- }
-
- if( pmMonInfo->dwSize >= sizeof(MONITOR_INFO) )
- {
- pmMonInfo->rMonitor.left = 0;
- pmMonInfo->rMonitor.top = 0;
- pmMonInfo->rMonitor.right = GetSystemMetrics(SM_CXSCREEN);
- pmMonInfo->rMonitor.bottom = GetSystemMetrics(SM_CYSCREEN);
- pmMonInfo->rWork = rArea;
- pmMonInfo->dwFlags = MONITOR_INFO_FLAG_PRIMARY;
-
- if( pmMonInfo->dwSize >= sizeof(MONITOR_INFO_EXTENDED))
- {
- lstrcpy(((PMONITOR_INFO_EXTENDED)pmMonInfo)->strDevice,
- TEXT("DISPLAY") );
- }
-
- retCode = TRUE;
- }
-
-_RET_:
- return retCode;
-}
-
-BOOL WINAPI _enumDisplayMonitors(
- HDC hDC,LPCRECT lrcSect,
- MON_ENUM_CALLBACK_PROC lpfnEnumProc,
- LPARAM lData
- )
-{
- BOOL retCode = FALSE;
- RECT rToPass = {0,0,0,0};
- RECT rBorder = {0,0,0,0};
-
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnEnumDisplayMonitors (
- hDC, lrcSect,
- lpfnEnumProc,lData
- );
- goto _RET_;
- }
-
- if( !lpfnEnumProc )
- {
- goto _RET_;
- }
-
- rBorder.left = 0;
- rBorder.top = 0;
- rBorder.right = GetSystemMetrics(SM_CXSCREEN);
- rBorder.bottom = GetSystemMetrics(SM_CYSCREEN);
-
- if( hDC )
- {
- RECT rSect = {0,0,0,0};
- HWND hWnd = NULL;
-
- if( NULL == (hWnd = WindowFromDC(hDC)) )
- {
- goto _RET_;
- }
-
- switch( GetClipBox(hDC,&rSect) )
- {
- case NULLREGION:
- goto _ASSIGN_;
- case ERROR:
- goto _RET_;
- default:
- MapWindowPoints(NULL, hWnd, (LPPOINT)&rBorder, 2);
- if( TRUE == IntersectRect(&rToPass,&rSect,&rBorder) )
- {
- break;
- }
- }
-
- rBorder = rToPass;
- }
-
- if( (NULL == lrcSect) || (TRUE == IntersectRect(&rToPass,lrcSect,&rBorder)) )
- {
- lpfnEnumProc(PRIMARY_MONITOR,hDC,&rToPass,lData);
- }
-_ASSIGN_:
- retCode = TRUE;
-_RET_:
- return retCode;
-}
-
-BOOL WINAPI _enumDisplayDevices (
- LPVOID lpReserved, int iDeviceNum,
- _DISPLAY_DEVICE * pDisplayDevice, DWORD dwFlags
- )
-{
- BOOL retCode = FALSE;
- if( __initMultipleMonitorStubs() )
- {
- retCode = g_pfnEnumDisplayDevices(lpReserved,iDeviceNum,pDisplayDevice,dwFlags);
- }
-
- return retCode;
-}
-
-
-//---------------------------------------------------------------------------
-// Extended API.
-//---------------------------------------------------------------------------
-// Globais
-int g_nMonitorCounter;
-int g_nMonitorLimit;
-MHND* g_hmpMonitors;
-// Callbacks
-BOOL WINAPI clb_fCountMonitors(MHND,HDC,LPRECT,LPARAM);
-BOOL WINAPI clb_fCountMonitors(MHND hMon,HDC hDC,LPRECT rRect,LPARAM lP)
-{
- g_nMonitorCounter ++;
- return TRUE;
-}
-BOOL WINAPI clb_fCollectMonitors(MHND,HDC,LPRECT,LPARAM);
-BOOL WINAPI clb_fCollectMonitors(MHND hMon,HDC hDC,LPRECT rRect,LPARAM lP)
-{
-
- if( (g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors) )
- {
- g_hmpMonitors[g_nMonitorCounter] = hMon;
- g_nMonitorCounter ++;
- }
-
- return TRUE;
-}
-// Tools
-void __normaRectPos(RECT*,RECT,RECT);
-HWND __createWindow0(MHND,LPCTSTR,LPCTSTR,DWORD,int,int,int,int,HWND,HMENU,HANDLE,LPVOID);
-HWND __createWindow1(MHND,LPCTSTR,LPCTSTR,DWORD,int,int,int,int,HWND,HMENU,HANDLE,LPVOID);
-void __normaRectPos(RECT* rDest,RECT rSrc,RECT rNorma)
-{
- int nDX = rSrc.right - rSrc.left;
- int nDY = rSrc.bottom - rSrc.top;
-
- rDest->left = rSrc.left + rNorma.left;
- rDest->top = rSrc.top + rNorma.top;
-
- rDest->right = rDest->left + nDX;
- rDest->bottom = rDest->top + nDY;
-}
-HWND __createWindow0( MHND hmMonitor,LPCTSTR lpClassName,LPCTSTR lpWindowName,
- DWORD dwStyle,int x,int y,int nWidth,
- int nHeight,HWND hWndParent,HMENU hMenu,
- HANDLE hInstance,LPVOID lpParam )
-{
- HWND retCode = NULL;
-
- if( (NULL != hmMonitor) && (NULL != lpClassName) &&
- (NULL != lpWindowName) && (NULL != hInstance) )
- {
- RECT rRW = {0,0,0,0};
- RECT rRM = {0,0,0,0};
- RECT rSect = {0,0,0,0};
-
- SetRect(&rRW,x,y,x+nWidth,y+nHeight);
-
- if( TRUE == _monitorBounds(hmMonitor,&rRM) )
- {
- __normaRectPos(&rRW,rRW,rRM);
-
- IntersectRect(&rSect,&rRM,&rRW);
-
- if( TRUE == EqualRect(&rSect,&rRW) )
- {
- x = rSect.left;
- y = rSect.top;
- nWidth = rSect.right - rSect.left;
- nHeight = rSect.bottom - rSect.top;
- retCode = CreateWindow(
- lpClassName,lpWindowName,
- dwStyle,x,y,nWidth,
- nHeight,hWndParent,hMenu,
- (HINSTANCE)hInstance,lpParam
- );
- } else {
- // A coisa indefinida. Nao tenho sabdoria o que
- // fazer aqui mesmo
- // E necessario perguntar Jeannette
- }
- }
- }
-
- return retCode;
-}
-HWND __createWindow1( MHND hmMonitor,LPCTSTR lpClassName,LPCTSTR lpWindowName,
- DWORD dwStyle,int x,int y,int nWidth,
- int nHeight,HWND hWndParent,HMENU hMenu,
- HANDLE hInstance,LPVOID lpParam )
-{
- HWND retCode = NULL;
-
- if( (NULL != hmMonitor) && (NULL != lpClassName) &&
- (NULL != lpWindowName) && (NULL != hInstance) )
- {
- RECT rRM = {0,0,0,0};
-
- if( TRUE == _monitorBounds(hmMonitor,&rRM) )
- {
- HWND wW = NULL;
- BOOL wasVisible = (0 != (dwStyle & WS_VISIBLE));
-
- if( TRUE == wasVisible )
- {
- dwStyle &= ~WS_VISIBLE;
- }
-
- if( NULL != (wW = CreateWindow(
- lpClassName,lpWindowName,
- dwStyle,x,y,nWidth,
- nHeight,hWndParent,hMenu,
- (HINSTANCE)hInstance,lpParam
- )) )
- {
- RECT rRW = {0,0,0,0};
- RECT rSect = {0,0,0,0};
-
- GetWindowRect(wW,&rRW);
-
- __normaRectPos(&rRW,rRW,rRM);
-
- IntersectRect(&rSect,&rRM,&rRW);
-
- if( TRUE == EqualRect(&rSect,&rRW) )
- {
- x = rSect.left;
- y = rSect.top;
- nWidth = rSect.right - rSect.left;
- nHeight = rSect.bottom - rSect.top;
-
- MoveWindow(wW,x,y,nWidth,nHeight,FALSE);
-
- if( TRUE == wasVisible )
- {
- UpdateWindow(wW);
- ShowWindow(wW,SW_SHOW);
- }
-
- retCode = wW;
- } else {
- // A coisa indefinida. Nao sei o que
- // fazer aqui. E necessario perguntar Jeannette
- DestroyWindow(wW);
- }
- }
- }
- }
-
- return retCode;
-}
-
-// Implementations
-int WINAPI _countMonitors(void)
-{
- g_nMonitorCounter = 0;
-
- _enumDisplayMonitors(NULL,NULL,clb_fCountMonitors,0L);
-
- return g_nMonitorCounter;
-
-}
-int WINAPI _collectMonitors(MHND* hmpMonitors,int nNum)
-{
- int retCode = 0;
-
- if( NULL != hmpMonitors )
- {
- g_nMonitorCounter = 0;
- g_nMonitorLimit = nNum;
- g_hmpMonitors = hmpMonitors;
-
- _enumDisplayMonitors(NULL,NULL,clb_fCollectMonitors,0L);
-
- retCode = g_nMonitorCounter;
-
- g_nMonitorCounter = 0;
- g_nMonitorLimit = 0;
- g_hmpMonitors = NULL;
-
- }
- return retCode;
-}
-BOOL WINAPI _monitorBounds(MHND hmMonitor,RECT* rpBounds)
-{
- BOOL retCode = FALSE;
-
- if( (NULL != hmMonitor) && (NULL != rpBounds) )
- {
- MONITOR_INFO miInfo;
-
- memset((void*)(&miInfo),0,sizeof(MONITOR_INFO));
- miInfo.dwSize = sizeof(MONITOR_INFO);
-
- if( TRUE == (retCode = _getMonitorInfo(hmMonitor,&(miInfo))) )
- {
- (*rpBounds) = miInfo.rMonitor;
- }
- }
- return retCode;
-}
-
-HDC WINAPI _makeDCFromMonitor(MHND hmMonitor) {
- HDC retCode = NULL;
-
- if( NULL != hmMonitor ) {
-
- MONITOR_INFO_EXTENDED mieInfo;
-
- memset((void*)(&mieInfo),0,sizeof(MONITOR_INFO_EXTENDED));
- mieInfo.dwSize = sizeof(MONITOR_INFO_EXTENDED);
-
- if( TRUE == _getMonitorInfo(hmMonitor,(PMONITOR_INFO)(&mieInfo)) ) {
- HDC hDC = CreateDC(mieInfo.strDevice,NULL,NULL,NULL);
-
- if( NULL != hDC ) {
- retCode = hDC;
- }
- }
- }
- return retCode;
-}
-
-HWND WINAPI _createWindowOM( MHND hmMonitor,LPCTSTR lpClassName,LPCTSTR lpWindowName,
- DWORD dwStyle,int x,int y,int nWidth,
- int nHeight,HWND hWndParent,HMENU hMenu,
- HANDLE hInstance,LPVOID lpParam )
-{
- if( (CW_USEDEFAULT == x) || (CW_USEDEFAULT == y) ||
- (CW_USEDEFAULT == nWidth) || (CW_USEDEFAULT == nHeight) )
- {
- return __createWindow1 (
- hmMonitor,lpClassName,lpWindowName,
- dwStyle,x,y,nWidth,
- nHeight,hWndParent,hMenu,
- hInstance,lpParam
- );
- }
- return __createWindow0 (
- hmMonitor,lpClassName,lpWindowName,
- dwStyle,x,y,nWidth,
- nHeight,hWndParent,hMenu,
- hInstance,lpParam
- );
-}
diff --git a/src/windows/native/sun/windows/awt_MMStub.h b/src/windows/native/sun/windows/awt_MMStub.h
deleted file mode 100644
index ec73dd74f..000000000
--- a/src/windows/native/sun/windows/awt_MMStub.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 1999-2003 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-#ifndef _INC_MMSTUB
-#define _INC_MMSTUB
-
-#ifndef _WINDOWS_
-#include "windows.h"
-#endif
-
-#ifndef _AWT_H_
-#include "awt.h"
-#endif
-
-#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
-
-/* Cdecl for C++ */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Constants */
-#define SM_XVSCREEN 76
-#define SM_YVSCREEN 77
-#define SM_CXVSCREEN 78
-#define SM_CYVSCREEN 79
-#define SM_CMONITORS 80
-#define SM_SAMEDSPLFORMAT 81
-
-#define MONITOR_DEFAULT_TO_NULL 0x00000000
-#define MONITOR_DEFAULT_TO_PRIMARY 0x00000001
-#define MONITOR_DEFAULT_TO_NEAR 0x00000002
-
-
-
-#define MONITOR_INFO_FLAG_PRIMARY 0x00000001
-
-#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001
-#define DISPLAY_DEVICE_MULTY_DRIVER 0x00000002
-#define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004
-#define DISPLAY_DEVICE_MIRRORING_DRIVER 0x00000008
-
-
-#define DISPLAY_DEVICE_VGA 0x00000010
-
-#define ENUM_CURRENT_SETTINGS ((DWORD)-1)
-#define ENUM_REGISTRY_SETTINGS ((DWORD)-2)
-
-#define PRIMARY_MONITOR ((MHND)0x42)
-
-
-#define DEV_NAME_LEN 32
-#define DEV_STR_LEN 128
-
-
-// Datatypes
-typedef HANDLE MHND;
-typedef BOOL (CALLBACK* MON_ENUM_CALLBACK_PROC)(MHND,HDC,LPRECT,LPARAM);
-
-typedef struct tagMONITOR_INFO
-{
- DWORD dwSize;
- RECT rMonitor;
- RECT rWork;
- DWORD dwFlags;
-} MONITOR_INFO, *PMONITOR_INFO;
-
-typedef struct tagMONITOR_INFO_EXTENDED
-{
- DWORD dwSize;
- RECT rMonitor;
- RECT rWork;
- DWORD dwFlags;
- TCHAR strDevice[DEV_NAME_LEN];
-} MONITOR_INFO_EXTENDED, *PMONITOR_INFO_EXTENDED;
-
-typedef struct tagDISPLAY_DEVICE
-{
- DWORD dwSize;
- WCHAR strDevName[DEV_NAME_LEN];
- WCHAR strDevString[DEV_STR_LEN];
- DWORD dwFlags;
- WCHAR deviceID[128];
- WCHAR deviceKey[128];
-} _DISPLAY_DEVICE, *P_DISPLAY_DEVICE;
-
-/* Basic API's */
-BOOL WINAPI _enumDisplayMonitors(HDC,LPCRECT,MON_ENUM_CALLBACK_PROC,LPARAM);
-BOOL WINAPI _enumDisplayDevices (LPVOID,int,P_DISPLAY_DEVICE,DWORD);
-BOOL WINAPI _getMonitorInfo (MHND,PMONITOR_INFO);
-MHND WINAPI _monitorFromPoint (POINT,UINT);
-MHND WINAPI _monitorFromWindow (HWND,UINT);
-MHND WINAPI _monitorFromRect (LPCRECT,UINT);
-int WINAPI _getSystemMetrics (int);
-
-/* Additional API's */
-int WINAPI _countMonitors (void);
-int WINAPI _collectMonitors (MHND*,int);
-BOOL WINAPI _monitorBounds (MHND,RECT*);
-HDC WINAPI _makeDCFromMonitor (MHND);
-HWND WINAPI _createWindowOM (MHND,LPCTSTR,LPCTSTR,DWORD,int,int,int,
- int,HWND,HMENU,HANDLE,LPVOID);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) */
-
-#endif /* _INC_MMSTUB */
diff --git a/src/windows/native/sun/windows/awt_MenuItem.cpp b/src/windows/native/sun/windows/awt_MenuItem.cpp
index 626b3a313..51fc47718 100644
--- a/src/windows/native/sun/windows/awt_MenuItem.cpp
+++ b/src/windows/native/sun/windows/awt_MenuItem.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include "awt_MenuItem.h"
#include "awt_Menu.h"
#include "awt_MenuBar.h"
@@ -70,7 +71,6 @@ jmethodID AwtMenuItem::getDefaultFontMID;
LANGID AwtMenuItem::m_idLang = LOWORD(GetKeyboardLayout(0));
UINT AwtMenuItem::m_CodePage =
AwtMenuItem::LangToCodePage(AwtMenuItem::m_idLang);
-BOOL AwtMenuItem::m_isWin95 = IS_WIN95;
BOOL AwtMenuItem::sm_rtl = PRIMARYLANGID(GetInputLanguage()) == LANG_ARABIC ||
PRIMARYLANGID(GetInputLanguage()) == LANG_HEBREW;
BOOL AwtMenuItem::sm_rtlReadingOrder =
@@ -150,7 +150,7 @@ BOOL AwtMenuItem::CheckMenuCreation(JNIEnv *env, jobject self, HMENU hMenu)
jobject createError = NULL;
if (dw == ERROR_OUTOFMEMORY)
{
- jstring errorMsg = env->NewStringUTF("too many menu handles");
+ jstring errorMsg = JNU_NewStringPlatform(env, L"too many menu handles");
createError = JNU_NewObjectByName(env, "java/lang/OutOfMemoryError",
"(Ljava/lang/String;)V",
errorMsg);
@@ -435,16 +435,7 @@ void AwtMenuItem::DrawItem(DRAWITEMSTRUCT& drawInfo)
if (drawInfo.itemID != m_Id)
return;
- /* Fixed bug 4349969. Since the problem occurs on Windows 98 and not on
- Windows NT, the fix is to check for Windows 95/98 and to check if the
- handle to the menu of the item to be drawn is the same as the handle to the
- menu of the menu object. If they're not the same, just return and don't do
- the drawing.
- */
- if ( IS_WIN95 && drawInfo.hwndItem != (HWND)this->m_menuContainer->GetHMenu()) {
- return;
- } else
- DrawSelf(drawInfo);
+ DrawSelf(drawInfo);
}
void AwtMenuItem::MeasureSelf(HDC hDC, MEASUREITEMSTRUCT& measureInfo)
@@ -802,8 +793,9 @@ BOOL AwtMenuItem::IsSeparator() {
jobject jitem = GetTarget(env);
jstring label =
(jstring)(env)->GetObjectField(jitem, AwtMenuItem::labelID);
- LPWSTR labelW = TO_WSTRING(label);
+ LPCWSTR labelW = JNU_GetStringPlatformChars(env, label, NULL);
BOOL isSeparator = (labelW && (wcscmp(labelW, L"-") == 0));
+ JNU_ReleaseStringPlatformChars(env, label, labelW);
env->DeleteLocalRef(label);
env->DeleteLocalRef(jitem);
diff --git a/src/windows/native/sun/windows/awt_Object.cpp b/src/windows/native/sun/windows/awt_Object.cpp
index aab7e98b5..b6b999be2 100644
--- a/src/windows/native/sun/windows/awt_Object.cpp
+++ b/src/windows/native/sun/windows/awt_Object.cpp
@@ -25,9 +25,6 @@
#include "awt_Object.h"
#include "ObjectList.h"
-#ifdef DEBUG
-#include "awt_Unicode.h"
-#endif
#ifdef DEBUG
static BOOL reportEvents = FALSE;
@@ -116,8 +113,9 @@ AwtObject::DoCallback(const char* methodName, const char* methodSig, ...)
"getName",
"()Ljava/lang/String;").l;
DASSERT(!safe_ExceptionOccurred(env));
- printf("Posting %s%s method to %S\n", methodName, methodSig,
- TO_WSTRING(targetStr));
+ LPCWSTR targetStrW = JNU_GetStringPlatformChars(env, targetStr, NULL);
+ printf("Posting %s%s method to %S\n", methodName, methodSig, targetStrW);
+ JNU_ReleaseStringPlatformChars(env, targetStr, targetStrW);
}
#endif
/* caching would do much good here */
@@ -148,8 +146,11 @@ void AwtObject::SendEvent(jobject event)
(jstring)JNU_CallMethodByName(env, NULL, GetTarget(env),"getName",
"()Ljava/lang/String;").l;
DASSERT(!safe_ExceptionOccurred(env));
- printf("Posting %S to %S\n", TO_WSTRING(eventStr),
- TO_WSTRING(targetStr));
+ LPCWSTR eventStrW = JNU_GetStringPlatformChars(env, eventStr, NULL);
+ LPCWSTR targetStrW = JNU_GetStringPlatformChars(env, targetStr, NULL);
+ printf("Posting %S to %S\n", eventStrW, targetStrW);
+ JNU_ReleaseStringPlatformChars(env, eventStr, eventStrW);
+ JNU_ReleaseStringPlatformChars(env, targetStr, targetStrW);
}
#endif
/* Post event to the system EventQueue. */
diff --git a/src/windows/native/sun/windows/awt_Palette.cpp b/src/windows/native/sun/windows/awt_Palette.cpp
index ea0488e81..a567c246d 100644
--- a/src/windows/native/sun/windows/awt_Palette.cpp
+++ b/src/windows/native/sun/windows/awt_Palette.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2001-2008 Sun Microsystems, Inc. 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
@@ -23,7 +23,7 @@
* have any questions.
*/
-#include <windows.h>
+#include "awt.h"
#include "awt_Palette.h"
#include "awt_Component.h"
#include "img_util_md.h"
diff --git a/src/windows/native/sun/windows/awt_PopupMenu.cpp b/src/windows/native/sun/windows/awt_PopupMenu.cpp
index 8862b86bd..794277eda 100644
--- a/src/windows/native/sun/windows/awt_PopupMenu.cpp
+++ b/src/windows/native/sun/windows/awt_PopupMenu.cpp
@@ -234,11 +234,13 @@ void AwtPopupMenu::Enable(BOOL isEnabled)
jobject jitem = item->GetTarget(env);
BOOL bItemEnabled = isEnabled && (jboolean)env->GetBooleanField(jitem,
AwtMenuItem::enabledID);
- LPWSTR labelW = TO_WSTRING((jstring)env->GetObjectField(jitem,
- AwtMenuItem::labelID));
- if (labelW != NULL && wcscmp(labelW,L"-") != 0) {
+ jstring labelStr = static_cast<jstring>(env->GetObjectField(jitem, AwtMenuItem::labelID));
+ LPCWSTR labelStrW = JNU_GetStringPlatformChars(env, labelStr, NULL);
+ if (labelStrW && wcscmp(labelStrW, L"-") != 0) {
item->Enable(bItemEnabled);
}
+ JNU_ReleaseStringPlatformChars(env, labelStr, labelStrW);
+ env->DeleteLocalRef(labelStr);
env->DeleteLocalRef(jitem);
}
env->DeleteLocalRef(target);
diff --git a/src/windows/native/sun/windows/awt_PrintControl.cpp b/src/windows/native/sun/windows/awt_PrintControl.cpp
index 486d9f669..cc562ec00 100644
--- a/src/windows/native/sun/windows/awt_PrintControl.cpp
+++ b/src/windows/native/sun/windows/awt_PrintControl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -108,16 +108,13 @@ BOOL AwtPrintControl::FindPrinter(jstring printerName, LPBYTE pPrinterEnum,
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
- BOOL nt = IS_NT;
DWORD cReturned = 0;
if (pPrinterEnum == NULL) {
// Compute size of buffer
DWORD cbNeeded = 0;
- if (nt) {
- ::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
+ ::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
NULL, 2, NULL, 0, &cbNeeded, &cReturned);
- }
::EnumPrinters(PRINTER_ENUM_LOCAL,
NULL, 5, NULL, 0, pcbBuf, &cReturned);
if (cbNeeded > (*pcbBuf)) {
@@ -139,65 +136,63 @@ BOOL AwtPrintControl::FindPrinter(jstring printerName, LPBYTE pPrinterEnum,
// doesn't support port searches. So, if the user has specified the
// printer name as "LPT1:" (even though this is actually a port
// name), we won't find the printer here.
- if (nt) {
- if (!::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
- NULL, 4, pPrinterEnum, cbBuf, &dummyWord, &cReturned)) {
- return FALSE;
- }
+ if (!::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
+ NULL, 4, pPrinterEnum, cbBuf, &dummyWord, &cReturned)) {
+ return FALSE;
+ }
- for (DWORD i = 0; i < cReturned; i++) {
- PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *)
- (pPrinterEnum + i * sizeof(PRINTER_INFO_4));
- if (info4->pPrinterName != NULL &&
- _tcsicmp(lpcPrinterName, info4->pPrinterName) == 0) {
-
- // Fix for BugTraq Id 4281380.
- // Get the port name since some drivers may require
- // this name to be passed to ::DeviceCapabilities().
- HANDLE hPrinter = NULL;
- if (::OpenPrinter(info4->pPrinterName, &hPrinter, NULL)) {
- // Fix for BugTraq Id 4286812.
- // Some drivers don't support PRINTER_INFO_5.
- // In this case we try PRINTER_INFO_2, and if that
- // isn't supported as well return NULL port name.
- try {
- if (AwtPrintControl::IsSupportedLevel(hPrinter, 5)) {
- VERIFY(::GetPrinter(hPrinter, 5, pPrinterEnum, cbBuf,
- &dummyWord));
- PRINTER_INFO_5 *info5 = (PRINTER_INFO_5 *)pPrinterEnum;
- *foundPrinter = info5->pPrinterName;
- // pPortName may specify multiple ports. We only want one.
- *foundPort = (info5->pPortName != NULL)
- ? _tcstok(info5->pPortName, TEXT(",")) : NULL;
- } else if (AwtPrintControl::IsSupportedLevel(hPrinter, 2)) {
- VERIFY(::GetPrinter(hPrinter, 2, pPrinterEnum, cbBuf,
- &dummyWord));
- PRINTER_INFO_2 *info2 = (PRINTER_INFO_2 *)pPrinterEnum;
- *foundPrinter = info2->pPrinterName;
- // pPortName may specify multiple ports. We only want one.
- *foundPort = (info2->pPortName != NULL)
- ? _tcstok(info2->pPortName, TEXT(",")) : NULL;
- } else {
- *foundPrinter = info4->pPrinterName;
- // We failed to determine port name for the found printer.
- *foundPort = NULL;
- }
- } catch (std::bad_alloc&) {
- VERIFY(::ClosePrinter(hPrinter));
- throw;
+ for (DWORD i = 0; i < cReturned; i++) {
+ PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *)
+ (pPrinterEnum + i * sizeof(PRINTER_INFO_4));
+ if (info4->pPrinterName != NULL &&
+ _tcsicmp(lpcPrinterName, info4->pPrinterName) == 0) {
+
+ // Fix for BugTraq Id 4281380.
+ // Get the port name since some drivers may require
+ // this name to be passed to ::DeviceCapabilities().
+ HANDLE hPrinter = NULL;
+ if (::OpenPrinter(info4->pPrinterName, &hPrinter, NULL)) {
+ // Fix for BugTraq Id 4286812.
+ // Some drivers don't support PRINTER_INFO_5.
+ // In this case we try PRINTER_INFO_2, and if that
+ // isn't supported as well return NULL port name.
+ try {
+ if (AwtPrintControl::IsSupportedLevel(hPrinter, 5)) {
+ VERIFY(::GetPrinter(hPrinter, 5, pPrinterEnum, cbBuf,
+ &dummyWord));
+ PRINTER_INFO_5 *info5 = (PRINTER_INFO_5 *)pPrinterEnum;
+ *foundPrinter = info5->pPrinterName;
+ // pPortName may specify multiple ports. We only want one.
+ *foundPort = (info5->pPortName != NULL)
+ ? _tcstok(info5->pPortName, TEXT(",")) : NULL;
+ } else if (AwtPrintControl::IsSupportedLevel(hPrinter, 2)) {
+ VERIFY(::GetPrinter(hPrinter, 2, pPrinterEnum, cbBuf,
+ &dummyWord));
+ PRINTER_INFO_2 *info2 = (PRINTER_INFO_2 *)pPrinterEnum;
+ *foundPrinter = info2->pPrinterName;
+ // pPortName may specify multiple ports. We only want one.
+ *foundPort = (info2->pPortName != NULL)
+ ? _tcstok(info2->pPortName, TEXT(",")) : NULL;
+ } else {
+ *foundPrinter = info4->pPrinterName;
+ // We failed to determine port name for the found printer.
+ *foundPort = NULL;
}
-
+ } catch (std::bad_alloc&) {
VERIFY(::ClosePrinter(hPrinter));
-
- return TRUE;
+ throw;
}
- return FALSE;
+ VERIFY(::ClosePrinter(hPrinter));
+
+ return TRUE;
}
+
+ return FALSE;
}
}
- // We still haven't found the printer, or we're using 95/98.
+ // We still haven't found the printer, /* or we're using 95/98. */
// PRINTER_INFO_5 supports both printer name and port name, so
// we'll test both. On NT, PRINTER_ENUM_LOCAL means just local
// printers. This is what we want, because we already tested all
@@ -213,28 +208,17 @@ BOOL AwtPrintControl::FindPrinter(jstring printerName, LPBYTE pPrinterEnum,
for (DWORD i = 0; i < cReturned; i++) {
PRINTER_INFO_5 *info5 = (PRINTER_INFO_5 *)
(pPrinterEnum + i * sizeof(PRINTER_INFO_5));
- if (nt) {
- // pPortName can specify multiple ports. Test them one at
- // a time.
- if (info5->pPortName != NULL) {
- LPTSTR port = _tcstok(info5->pPortName, TEXT(","));
- while (port != NULL) {
- if (_tcsicmp(lpcPrinterName, port) == 0) {
- *foundPrinter = info5->pPrinterName;
- *foundPort = port;
- return TRUE;
- }
- port = _tcstok(NULL, TEXT(","));
+ // pPortName can specify multiple ports. Test them one at
+ // a time.
+ if (info5->pPortName != NULL) {
+ LPTSTR port = _tcstok(info5->pPortName, TEXT(","));
+ while (port != NULL) {
+ if (_tcsicmp(lpcPrinterName, port) == 0) {
+ *foundPrinter = info5->pPrinterName;
+ *foundPort = port;
+ return TRUE;
}
- }
- } else {
- if ((info5->pPrinterName != NULL &&
- _tcsicmp(lpcPrinterName, info5->pPrinterName) == 0) ||
- (info5->pPortName != NULL &&
- _tcsicmp(lpcPrinterName, info5->pPortName) == 0)) {
- *foundPrinter = info5->pPrinterName;
- *foundPort = info5->pPortName;
- return TRUE;
+ port = _tcstok(NULL, TEXT(","));
}
}
}
@@ -400,13 +384,11 @@ BOOL AwtPrintControl::CreateDevModeAndDevNames(PRINTDLG *ppd,
}
// Create DEVNAMES.
- if (IS_NT) {
- if (pPortName != NULL) {
- info2->pPortName = pPortName;
- } else if (info2->pPortName != NULL) {
- // pPortName may specify multiple ports. We only want one.
- info2->pPortName = _tcstok(info2->pPortName, TEXT(","));
- }
+ if (pPortName != NULL) {
+ info2->pPortName = pPortName;
+ } else if (info2->pPortName != NULL) {
+ // pPortName may specify multiple ports. We only want one.
+ info2->pPortName = _tcstok(info2->pPortName, TEXT(","));
}
size_t lenDriverName = ((info2->pDriverName != NULL)
diff --git a/src/windows/native/sun/windows/awt_PrintDialog.cpp b/src/windows/native/sun/windows/awt_PrintDialog.cpp
index d9e738411..762ae2673 100644
--- a/src/windows/native/sun/windows/awt_PrintDialog.cpp
+++ b/src/windows/native/sun/windows/awt_PrintDialog.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -23,10 +23,10 @@
* have any questions.
*/
+#include "awt.h"
#include "awt_PrintDialog.h"
#include "awt_Dialog.h"
#include "awt_PrintControl.h"
-#include "awt_dlls.h"
#include "awt_Window.h"
#include "ComCtl32Util.h"
#include <sun_awt_windows_WPrintDialog.h>
@@ -39,11 +39,9 @@ jmethodID AwtPrintDialog::setHWndMID;
BOOL
AwtPrintDialog::PrintDlg(LPPRINTDLG data) {
- AwtCommDialog::load_comdlg_procs();
return static_cast<BOOL>(reinterpret_cast<INT_PTR>(
AwtToolkit::GetInstance().InvokeFunction(
- reinterpret_cast<void *(*)(void *)>(AwtCommDialog::PrintDlgWrapper),
- data)));
+ reinterpret_cast<void *(*)(void *)>(::PrintDlg), data)));
}
LRESULT CALLBACK PrintDialogWndProc(HWND hWnd, UINT message,
diff --git a/src/windows/native/sun/windows/awt_PrintJob.cpp b/src/windows/native/sun/windows/awt_PrintJob.cpp
index 0ecd0388a..9136b7864 100644
--- a/src/windows/native/sun/windows/awt_PrintJob.cpp
+++ b/src/windows/native/sun/windows/awt_PrintJob.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include <math.h>
#include <windef.h>
#include <wtypes.h>
@@ -30,10 +31,9 @@
#include <commdlg.h>
#include <winspool.h>
-#include "awt.h"
-#include "awt_dlls.h"
#include "awt_Toolkit.h"
#include "awt_Component.h"
+#include "awt_Dialog.h"
#include "awt_Font.h"
#include "awt_PrintDialog.h"
#include "awt_PrintControl.h"
@@ -422,7 +422,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
*/
if (AwtPrintControl::getPrintHDMode(env, self) == NULL ||
AwtPrintControl::getPrintHDName(env,self) == NULL) {
- (void)AwtCommDialog::PageSetupDlg(&setup);
+ (void)::PageSetupDlg(&setup);
/* check if hDevMode and hDevNames are set.
* If both are null, then there is no default printer.
*/
@@ -460,7 +460,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
AwtDialog::CheckInstallModalHook();
- BOOL ret = AwtCommDialog::PageSetupDlg(&setup);
+ BOOL ret = ::PageSetupDlg(&setup);
if (ret) {
jobject paper = getPaper(env, page);
@@ -733,7 +733,7 @@ Java_sun_awt_windows_WPrinterJob_validatePaper(JNIEnv *env, jobject self,
pd.lStructSize = sizeof(PRINTDLG);
pd.Flags = PD_RETURNDEFAULT | PD_RETURNDC;
- if (AwtCommDialog::PrintDlg(&pd)) {
+ if (::PrintDlg(&pd)) {
printDC = pd.hDC;
hDevMode = pd.hDevMode;
hDevNames = pd.hDevNames;
@@ -1838,10 +1838,6 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WPrinterJob_selectStylePen
(JNIEnv *env, jobject self, jlong printDC, jlong cap, jlong join, jfloat width,
jint red, jint green, jint blue) {
- /* End cap and line join styles are not supported in Win 9x. */
- if (IS_WIN95)
- return JNI_FALSE;
-
TRY;
LOGBRUSH logBrush;
@@ -1879,23 +1875,13 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WPrinterJob_setFont
{
jboolean didSetFont = JNI_FALSE;
- if (IS_NT) {
- didSetFont = jFontToWFontW(env, (HDC)printDC,
- fontName,
- fontSize,
- isBold,
- isItalic,
- rotation,
- awScale);
- } else {
- didSetFont = jFontToWFontA(env, (HDC)printDC,
+ didSetFont = jFontToWFontW(env, (HDC)printDC,
fontName,
fontSize,
isBold,
isItalic,
rotation,
awScale);
- }
return didSetFont;
}
@@ -1919,7 +1905,7 @@ static jboolean jFontToWFontA(JNIEnv *env, HDC printDC, jstring fontName,
memset(&matchedLogFont, 0, sizeof(matchedLogFont));
- WCHAR* name = TO_WSTRING(fontName);
+ LPCWSTR fontNameW = JNU_GetStringPlatformChars(env, fontName, NULL);
/* Some fontnames of Non-ASCII fonts like 'MS Minchou' are themselves
@@ -1928,14 +1914,14 @@ static jboolean jFontToWFontA(JNIEnv *env, HDC printDC, jstring fontName,
*/
int maxlen = static_cast<int>(sizeof(lf.lfFaceName)) - 1;
// maxlen is int due to cbMultiByte parameter is int
- int destLen = WideCharToMultiByte(CP_ACP, // convert to ASCII code page
- 0, // flags
- name, // Unicode string
- -1, // Unicode length is calculated automatically
+ int destLen = WideCharToMultiByte(CP_ACP, // convert to ASCII code page
+ 0, // flags
+ fontNameW, // Unicode string
+ -1, // Unicode length is calculated automatically
lf.lfFaceName, // Put ASCII string here
- maxlen, // max len
- NULL, // default handling of unmappables
- NULL);// do not care if def char is used
+ maxlen, // max len
+ NULL, // default handling of unmappables
+ NULL); // do not care if def char is used
/* If WideCharToMultiByte succeeded then the number
* of bytes it copied into the face name buffer will
@@ -2018,9 +2004,10 @@ static jboolean jFontToWFontA(JNIEnv *env, HDC printDC, jstring fontName,
} else {
foundFont = false;
}
-
}
+ JNU_ReleaseStringPlatformChars(env, fontName, fontNameW);
+
return foundFont ? JNI_TRUE : JNI_FALSE;
}
@@ -2043,27 +2030,29 @@ static jboolean jFontToWFontW(JNIEnv *env, HDC printDC, jstring fontName,
memset(&matchedLogFont, 0, sizeof(matchedLogFont));
+ LPCWSTR fontNameW = JNU_GetStringPlatformChars(env, fontName, NULL);
+
/* Describe the GDI fonts we want enumerated. We
* simply supply the java font name and let GDI
* do the matching. If the java font name is
* longer than the GDI maximum font lenght then
* we can't convert the font.
*/
- WCHAR* name = TO_WSTRING(fontName);
- size_t nameLen = wcslen(name);
-
+ size_t nameLen = wcslen(fontNameW);
if (nameLen < (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) {
- wcscpy(lf.lfFaceName, name);
+ wcscpy(lf.lfFaceName, fontNameW);
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfPitchAndFamily = 0;
- foundFont = !EnumFontFamiliesExW((HDC)printDC, &lf,
+ foundFont = !::EnumFontFamiliesEx((HDC)printDC, &lf,
(FONTENUMPROCW) fontEnumProcW,
(LPARAM) &matchedLogFont, 0);
}
+ JNU_ReleaseStringPlatformChars(env, fontName, fontNameW);
+
if (!foundFont) {
return JNI_FALSE;
}
@@ -2100,7 +2089,7 @@ static jboolean jFontToWFontW(JNIEnv *env, HDC printDC, jstring fontName,
//Debug: dumpLogFont(&matchedLogFont);
- HFONT font = CreateFontIndirectW(&matchedLogFont);
+ HFONT font = ::CreateFontIndirect(&matchedLogFont);
if (font == NULL) {
return JNI_FALSE;
}
@@ -2123,7 +2112,7 @@ static jboolean jFontToWFontW(JNIEnv *env, HDC printDC, jstring fontName,
GetTextMetrics(printDC, &tm);
avgWidth = tm.tmAveCharWidth;
matchedLogFont.lfWidth = (LONG)((fabs)(avgWidth*awScale));
- font = CreateFontIndirectW(&matchedLogFont);
+ font = ::CreateFontIndirect(&matchedLogFont);
if (font == NULL) {
return JNI_FALSE;
}
@@ -2230,14 +2219,11 @@ JNIEXPORT jint JNICALL Java_sun_awt_windows_WPrinterJob_getGDIAdvance
(JNIEnv *env, jobject self, jlong printDC, jstring text)
{
SIZE size;
- LPWSTR wText = TO_WSTRING(text);
+ LPCWSTR wText = JNU_GetStringPlatformChars(env, text, NULL);
size_t strLen = wcslen(wText);
BOOL ok = GetTextExtentPoint32((HDC)printDC, wText, (int)strLen, &size);
- if (ok) {
- return size.cx;
- } else {
- return 0;
- }
+ JNU_ReleaseStringPlatformChars(env, text, wText);
+ return ok ? size.cx : 0;
}
@@ -2288,7 +2274,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_WPrinterJob_textOut
long posX = ROUND_TO_LONG(x);
long posY = ROUND_TO_LONG(y);
int flags = (glyphCodes !=0) ? ETO_GLYPH_INDEX : 0;
- LPWSTR wText = TO_WSTRING(text);
+ LPCWSTR wText = JNU_GetStringPlatformChars(env, text, NULL);
int *advances = NULL, *xadvances = NULL, *xyadvances = NULL;
BOOL useYAdvances = FALSE;
@@ -2359,7 +2345,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_WPrinterJob_textOut
*inxyAdvances = 0;
}
- if (useYAdvances && IS_WIN2000) {
+ if (useYAdvances) {
advances = xyadvances;
flags |= J2D_ETO_PDY;
} else {
@@ -2371,7 +2357,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_WPrinterJob_textOut
env->ReleaseFloatArrayElements(positions, glyphPos, JNI_ABORT);
}
- BOOL drawn = ::ExtTextOutW( (HDC)printDC,
+ BOOL drawn = ::ExtTextOut((HDC)printDC,
posX, posY, // starting position for the text
flags, // glyphCodes?, y advances?
NULL, // optional clipping-opaquing rectangle
@@ -2385,6 +2371,8 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_WPrinterJob_textOut
if (xyadvances != NULL) {
free(xyadvances);
}
+
+ JNU_ReleaseStringPlatformChars(env, text, wText);
}
/**
@@ -2968,7 +2956,7 @@ static HDC getDefaultPrinterDC(JNIEnv *env, jobject printerJob) {
pd.lStructSize = sizeof(PRINTDLG);
pd.Flags = PD_RETURNDEFAULT | PD_RETURNDC;
- if (AwtCommDialog::PrintDlg(&pd)) {
+ if (::PrintDlg(&pd)) {
printDC = pd.hDC;
/* Find out how many copies the driver can do, and use driver's
diff --git a/src/windows/native/sun/windows/awt_Robot.cpp b/src/windows/native/sun/windows/awt_Robot.cpp
index 15a9a243f..e774f5160 100644
--- a/src/windows/native/sun/windows/awt_Robot.cpp
+++ b/src/windows/native/sun/windows/awt_Robot.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2008 Sun Microsystems, Inc. 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
@@ -23,6 +23,7 @@
* have any questions.
*/
+#include "awt.h"
#include "awt_Toolkit.h"
#include "awt_Component.h"
#include "awt_Robot.h"
@@ -30,8 +31,6 @@
#include "java_awt_event_InputEvent.h"
#include <winuser.h>
-static const int MOUSE_MAX = 65535;
-
AwtRobot::AwtRobot( jobject peer )
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
@@ -44,11 +43,11 @@ AwtRobot::~AwtRobot()
}
#ifndef SPI_GETMOUSESPEED
-#define SPI_GETMOUSESPEED 112
+#define SPI_GETMOUSESPEED 112
#endif
#ifndef SPI_SETMOUSESPEED
-#define SPI_SETMOUSESPEED 113
+#define SPI_SETMOUSESPEED 113
#endif
void AwtRobot::MouseMove( jint x, jint y)
@@ -102,19 +101,38 @@ void AwtRobot::MousePress( jint buttonMask )
// left handed mouse setup
BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
- if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ) {
+ if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK)
+ {
dwFlags |= !bSwap ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN;
}
- if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ) {
+ if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK)
+ {
dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN;
}
- if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ) {
+ if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK)
+ {
dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
}
- mouse_event(dwFlags, 0, 0, 0, 0 );
+ INPUT mouseInput = {0};
+ mouseInput.type = INPUT_MOUSE;
+ mouseInput.mi.time = 0;
+ mouseInput.mi.dwFlags = dwFlags;
+ if ( buttonMask & AwtComponent::masks[3] ) {
+ mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN;
+ mouseInput.mi.mouseData = XBUTTON1;
+ }
+
+ if ( buttonMask & AwtComponent::masks[4] ) {
+ mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN;
+ mouseInput.mi.mouseData = XBUTTON2;
+ }
+ ::SendInput(1, &mouseInput, sizeof(mouseInput));
}
void AwtRobot::MouseRelease( jint buttonMask )
@@ -125,61 +143,43 @@ void AwtRobot::MouseRelease( jint buttonMask )
// left handed mouse setup
BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
- if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ) {
+ if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK)
+ {
dwFlags |= !bSwap ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP;
}
- if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ) {
+ if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK)
+ {
dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP;
}
- if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ) {
+ if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
+ buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK)
+ {
dwFlags |= MOUSEEVENTF_MIDDLEUP;
}
- mouse_event(dwFlags, 0, 0, 0, 0 );
-}
+ INPUT mouseInput = {0};
+ mouseInput.type = INPUT_MOUSE;
+ mouseInput.mi.time = 0;
+ mouseInput.mi.dwFlags = dwFlags;
-void AwtRobot::MouseWheel (jint wheelAmt) {
- if (IS_WIN95 && !IS_WIN98) {
- // Other win32 platforms do nothing for mouse_event(0), so
- // do nothing on 95, too.
- if (wheelAmt == 0) {
- return;
- }
-
- // Win95 doesn't understand MOUSEEVENTF_WHEEL, so use PostEvent
- POINT curPos;
- HWND mouseOver = NULL;
- HWND topLevel = NULL;
- UINT wheelMsg = NULL;
-
- if (::GetCursorPos((LPPOINT)&curPos) == 0) {
- return;
- }
- // get hwnd of top-level container
- mouseOver = ::WindowFromPoint(curPos);
- DASSERT(mouseOver);
- topLevel = AwtComponent::GetTopLevelParentForWindow(mouseOver);
- DASSERT(topLevel);
-
- if (::ScreenToClient(topLevel, (LPPOINT)&curPos) == 0) {
- return;
- }
- wheelMsg = AwtComponent::Wheel95GetMsg();
-
- if (wheelMsg == NULL) {
- return;
- }
-
- ::PostMessage(topLevel,
- wheelMsg,
- wheelAmt * -1 * WHEEL_DELTA,
- MAKELPARAM((WORD)curPos.x, (WORD)curPos.y));
+ if ( buttonMask & AwtComponent::masks[3] ) {
+ mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP;
+ mouseInput.mi.mouseData = XBUTTON1;
}
- else {
- mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0);
+
+ if ( buttonMask & AwtComponent::masks[4] ) {
+ mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP;
+ mouseInput.mi.mouseData = XBUTTON2;
}
+ ::SendInput(1, &mouseInput, sizeof(mouseInput));
+}
+
+void AwtRobot::MouseWheel (jint wheelAmt) {
+ mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0);
}
inline jint AwtRobot::WinToJavaPixel(USHORT r, USHORT g, USHORT b)
@@ -437,3 +437,9 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyRelease(
CATCH_BAD_ALLOC;
}
+
+JNIEXPORT jint JNICALL Java_sun_awt_windows_WRobotPeer_getNumberOfButtons(
+ JNIEnv *, jobject self)
+{
+ return GetSystemMetrics(SM_CMOUSEBUTTONS);
+}
diff --git a/src/windows/native/sun/windows/awt_Robot.h b/src/windows/native/sun/windows/awt_Robot.h
index ec25746e7..ba532c748 100644
--- a/src/windows/native/sun/windows/awt_Robot.h
+++ b/src/windows/native/sun/windows/awt_Robot.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2008 Sun Microsystems, Inc. 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
@@ -42,13 +42,13 @@ class AwtRobot : public AwtObject
void MouseRelease( jint buttonMask );
void MouseWheel(jint wheelAmt);
+ jint getNumberOfButtons();
jint GetRGBPixel( jint x, jint y);
void GetRGBPixels(jint x, jint y, jint width, jint height, jintArray pixelArray);
void KeyPress( jint key );
void KeyRelease( jint key );
-
static AwtRobot * GetRobot( jobject self );
private:
diff --git a/src/windows/native/sun/windows/awt_ScrollPane.cpp b/src/windows/native/sun/windows/awt_ScrollPane.cpp
index 3975a3fd8..4d41e0b0c 100644
--- a/src/windows/native/sun/windows/awt_ScrollPane.cpp
+++ b/src/windows/native/sun/windows/awt_ScrollPane.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -108,13 +108,6 @@ AwtScrollPane* AwtScrollPane::Create(jobject self, jobject parent)
{
DWORD style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
- if (!IS_WIN4X) {
- /*
- * It's been decided by the UI folks that 3.X ScrollPanes
- * should have borders...
- */
- style |= WS_BORDER;
- }
jint scrollbarDisplayPolicy =
env->GetIntField(target, scrollbarDisplayPolicyID);
@@ -122,7 +115,7 @@ AwtScrollPane* AwtScrollPane::Create(jobject self, jobject parent)
== java_awt_ScrollPane_SCROLLBARS_ALWAYS) {
style |= WS_HSCROLL | WS_VSCROLL;
}
- DWORD exStyle = IS_WIN4X ? WS_EX_CLIENTEDGE : 0;
+ DWORD exStyle = WS_EX_CLIENTEDGE;
if (GetRTL()) {
exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR;
@@ -225,15 +218,8 @@ void AwtScrollPane::RecalcSizes(int parentWidth, int parentHeight,
}
/* Determine border width without scrollbars. */
- int horzBorder;
- int vertBorder;
- if (IS_WIN4X) {
- horzBorder = ::GetSystemMetrics(SM_CXEDGE);
- vertBorder = ::GetSystemMetrics(SM_CYEDGE);
- } else {
- horzBorder = ::GetSystemMetrics(SM_CXBORDER);
- vertBorder = ::GetSystemMetrics(SM_CYBORDER);
- }
+ int horzBorder = ::GetSystemMetrics(SM_CXEDGE);;
+ int vertBorder = ::GetSystemMetrics(SM_CYEDGE);;
parentWidth -= (horzBorder * 2);
parentHeight -= (vertBorder * 2);
diff --git a/src/windows/native/sun/windows/awt_TextArea.cpp b/src/windows/native/sun/windows/awt_TextArea.cpp
index 92a36b7a1..e0d4c6158 100644
--- a/src/windows/native/sun/windows/awt_TextArea.cpp
+++ b/src/windows/native/sun/windows/awt_TextArea.cpp
@@ -26,10 +26,8 @@
#include "awt_Toolkit.h"
#include "awt_TextArea.h"
#include "awt_TextComponent.h"
-#include "awt_dlls.h"
#include "awt_KeyboardFocusManager.h"
#include "awt_Canvas.h"
-#include "awt_Unicode.h"
#include "awt_Window.h"
/* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
@@ -50,7 +48,6 @@ struct ReplaceTextStruct {
jfieldID AwtTextArea::scrollbarVisibilityID;
WNDPROC AwtTextArea::sm_pDefWindowProc = NULL;
-BOOL AwtTextArea::sm_RichEdit20 = (IS_WIN98 || IS_NT);
/************************************************************************
* AwtTextArea methods
@@ -78,8 +75,12 @@ void AwtTextArea::Dispose()
}
LPCTSTR AwtTextArea::GetClassName() {
- load_rich_edit_library();
- return sm_RichEdit20 ? RICHEDIT_CLASS : TEXT("RICHEDIT");
+ static BOOL richedLibraryLoaded = FALSE;
+ if (!richedLibraryLoaded) {
+ ::LoadLibrary(TEXT("RICHED20.DLL"));
+ richedLibraryLoaded = TRUE;
+ }
+ return RICHEDIT_CLASS;
}
/* Create a new AwtTextArea object and window. */
@@ -134,9 +135,8 @@ AwtTextArea* AwtTextArea::Create(jobject peer, jobject parent)
* scrollbars instead of hiding them when not needed.
*/
DWORD style = WS_CHILD | WS_CLIPSIBLINGS | ES_LEFT | ES_MULTILINE |
- ES_WANTRETURN | scroll_style |
- (IS_WIN4X ? 0 : WS_BORDER) | ES_DISABLENOSCROLL;
- DWORD exStyle = IS_WIN4X ? WS_EX_CLIENTEDGE : 0;
+ ES_WANTRETURN | scroll_style | ES_DISABLENOSCROLL;
+ DWORD exStyle = WS_EX_CLIENTEDGE;
if (GetRTL()) {
exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR;
if (GetRTLReadingOrder())
@@ -169,9 +169,7 @@ AwtTextArea* AwtTextArea::Create(jobject peer, jobject parent)
// end-of-document marker or carriage return,
// to format paragraphs.
// kdm@sparc.spb.su
- if (sm_RichEdit20) {
- c->SendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0);
- }
+ c->SendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0);
c->m_backgroundColorSet = TRUE;
/* suppress inheriting parent's color. */
@@ -242,7 +240,7 @@ size_t AwtTextArea::CountNewLines(JNIEnv *env, jstring jStr, size_t maxlen)
*/
size_t length = env->GetStringLength(jStr) + 1;
WCHAR *string = new WCHAR[length];
- env->GetStringRegion(jStr, 0, static_cast<jsize>(length - 1), string);
+ env->GetStringRegion(jStr, 0, static_cast<jsize>(length - 1), reinterpret_cast<jchar*>(string));
string[length-1] = '\0';
for (size_t i = 0; i < maxlen && i < length - 1; i++) {
if (string[i] == L'\n') {
@@ -461,12 +459,7 @@ AwtTextArea::WmContextMenu(HWND hCtrl, UINT xPos, UINT yPos) {
/* Check if all the text is selected. */
if (cr.cpMin == 0) {
- int len = 0;
- if (m_isWin95) {
- len = ::GetWindowTextLengthA(GetHWnd());
- } else {
- len = ::GetWindowTextLengthW(GetHWnd());
- }
+ int len = ::GetWindowTextLength(GetHWnd());
if (cr.cpMin == 0 && cr.cpMax >= len) {
/*
* All the text is selected in RichEdit - select all the
@@ -738,14 +731,8 @@ AwtTextArea::HandleEvent(MSG *msg, BOOL synthetic)
// kdm@sparc.spb.su
UINT platfScrollLines = 3;
// Retrieve a number of scroll lines.
- if (!sm_RichEdit20) {
- // 95 doesn't understand the SPI_GETWHEELSCROLLLINES - get the user
- // preference by other means
- platfScrollLines = Wheel95GetScrLines();
- } else {
- ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
- &platfScrollLines, 0);
- }
+ ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
+ &platfScrollLines, 0);
if (platfScrollLines > 0) {
HWND hWnd = GetHWnd();
@@ -838,23 +825,6 @@ AwtTextArea::HandleEvent(MSG *msg, BOOL synthetic)
return returnVal;
}
-int AwtTextArea::GetText(LPTSTR buffer, int size)
-{
- // Due to a known limitation of the MSLU, GetWindowText cannot be
- // issued for the Unicode RichEdit control on Win9x. Use EM_GETTEXTEX instead.
- if (sm_RichEdit20 && !IS_NT) {
- GETTEXTEX gte;
- gte.cb = size * sizeof(TCHAR);
- gte.flags = GT_USECRLF;
- gte.codepage = 1200; // implies Unicode
- gte.lpDefaultChar = NULL;
- gte.lpUsedDefChar = NULL;
- return (int)SendMessage(EM_GETTEXTEX, (WPARAM)&gte, (LPARAM)buffer);
- } else {
- return ::GetWindowText(GetHWnd(), buffer, size);
- }
-}
-
/*
* WM_CTLCOLOR is not sent by rich edit controls.
* Use EM_SETCHARFORMAT and EM_SETBKGNDCOLOR to set
@@ -984,16 +954,16 @@ void AwtTextArea::_ReplaceText(void *param)
jsize length = env->GetStringLength(text) + 1;
// Bugid 4141477 - Can't use TO_WSTRING here because it uses alloca
// WCHAR* buffer = TO_WSTRING(text);
- WCHAR *buffer = new WCHAR[length];
- env->GetStringRegion(text, 0, length-1, buffer);
+ TCHAR *buffer = new TCHAR[length];
+ env->GetStringRegion(text, 0, length-1, reinterpret_cast<jchar*>(buffer));
buffer[length-1] = '\0';
c->CheckLineSeparator(buffer);
c->RemoveCR(buffer);
// Fix for 5003402: added restoring/hiding selection to enable automatic scrolling
c->SendMessage(EM_HIDESELECTION, FALSE, TRUE);
- c->SendMessageW(EM_SETSEL, start, end);
- c->SendMessageW(EM_REPLACESEL, FALSE, (LPARAM)buffer);
+ c->SendMessage(EM_SETSEL, start, end);
+ c->SendMessage(EM_REPLACESEL, FALSE, (LPARAM)buffer);
c->SendMessage(EM_HIDESELECTION, TRUE, TRUE);
delete[] buffer;
@@ -1187,12 +1157,11 @@ AwtTextArea::OleCallback::QueryAcceptData(LPDATAOBJECT pdataobj,
HGLOBAL hMetaPict) {
if (reco == RECO_PASTE) {
// If CF_TEXT format is available edit controls will select it,
- // otherwise if it is WinNT or Win2000 and CF_UNICODETEXT is
- // available it will be selected, otherwise if CF_OEMTEXT is
- // available it will be selected.
+ // otherwise if it is CF_UNICODETEXT is available it will be
+ // selected, otherwise if CF_OEMTEXT is available it will be selected.
if (::IsClipboardFormatAvailable(CF_TEXT)) {
*pcfFormat = CF_TEXT;
- } else if (!m_isWin95 && ::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
+ } else if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
*pcfFormat = CF_UNICODETEXT;
} else if (::IsClipboardFormatAvailable(CF_OEMTEXT)) {
*pcfFormat = CF_OEMTEXT;
diff --git a/src/windows/native/sun/windows/awt_TextArea.h b/src/windows/native/sun/windows/awt_TextArea.h
index 7e054f610..12ba0baa6 100644
--- a/src/windows/native/sun/windows/awt_TextArea.h
+++ b/src/windows/native/sun/windows/awt_TextArea.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -73,8 +73,6 @@ public:
MsgRouting WmNcHitTest(UINT x, UINT y, LRESULT &retVal);
MsgRouting HandleEvent(MSG *msg, BOOL synthetic);
- INLINE virtual int GetText(LPTSTR buffer, int size);
-
INLINE void SetIgnoreEnChange(BOOL b) { m_bIgnoreEnChange = b; }
virtual void SetColor(COLORREF c);
@@ -96,10 +94,6 @@ protected:
void EditGetSel(CHARRANGE &cr);
LONG EditGetCharFromPos(POINT& pt);
private:
-
- // TRUE if the rich edit version is 2.0
- static BOOL sm_RichEdit20;
-
// RichEdit 1.0 control generates EN_CHANGE notifications not only
// on text changes, but also on any character formatting change.
// This flag is true when the latter case is detected.
diff --git a/src/windows/native/sun/windows/awt_TextComponent.cpp b/src/windows/native/sun/windows/awt_TextComponent.cpp
index 1be6d9f14..9920b9252 100644
--- a/src/windows/native/sun/windows/awt_TextComponent.cpp
+++ b/src/windows/native/sun/windows/awt_TextComponent.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -326,7 +326,7 @@ jstring AwtTextComponent::_GetText(void *param)
WCHAR* buf = new WCHAR[len + 1];
c->GetText(buf, len + 1);
c->RemoveCR(buf);
- result = env->NewString(buf, static_cast<jsize>(wcslen(buf)));
+ result = JNU_NewStringPlatform(env, buf);
delete [] buf;
}
}
@@ -362,7 +362,7 @@ void AwtTextComponent::_SetText(void *param)
{
int length = env->GetStringLength(text);
WCHAR* buffer = new WCHAR[length + 1];
- env->GetStringRegion(text, 0, length, buffer);
+ env->GetStringRegion(text, 0, length, reinterpret_cast<jchar*>(buffer));
buffer[length] = 0;
c->CheckLineSeparator(buffer);
c->RemoveCR(buffer);
diff --git a/src/windows/native/sun/windows/awt_TextComponent.h b/src/windows/native/sun/windows/awt_TextComponent.h
index 2eff05057..32a430baf 100644
--- a/src/windows/native/sun/windows/awt_TextComponent.h
+++ b/src/windows/native/sun/windows/awt_TextComponent.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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
@@ -70,15 +70,6 @@ public:
static jstring _GetText(void *param);
BOOL ActMouseMessage(MSG* pMsg);
- /*
- * For TextComponents that contains WCHAR strings or messages with
- * WCHAR parameters.
- */
- INLINE LRESULT SendMessageW(UINT msg, WPARAM wParam = 0, LPARAM lParam = 0)
- {
- DASSERT(GetHWnd());
- return ::SendMessageW(GetHWnd(), msg, wParam, lParam);
- }
void SetFont(AwtFont* font);
diff --git a/src/windows/native/sun/windows/awt_TextField.cpp b/src/windows/native/sun/windows/awt_TextField.cpp
index 5b95495d0..975fd20bc 100644
--- a/src/windows/native/sun/windows/awt_TextField.cpp
+++ b/src/windows/native/sun/windows/awt_TextField.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. 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 @@
#include "awt_Toolkit.h"
#include "awt_TextField.h"
#include "awt_TextComponent.h"
-#include "awt_dlls.h"
#include "awt_KeyboardFocusManager.h"
#include "awt_Canvas.h"
@@ -69,9 +68,8 @@ AwtTextField* AwtTextField::Create(jobject peer, jobject parent)
{
DWORD style = WS_CHILD | WS_CLIPSIBLINGS |
- ES_LEFT | ES_AUTOHSCROLL |
- (IS_WIN4X ? 0 : WS_BORDER);
- DWORD exStyle = IS_WIN4X ? WS_EX_CLIENTEDGE : 0;
+ ES_LEFT | ES_AUTOHSCROLL;
+ DWORD exStyle = WS_EX_CLIENTEDGE;
if (GetRTL()) {
exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR;
if (GetRTLReadingOrder())
diff --git a/src/windows/native/sun/windows/awt_Toolkit.cpp b/src/windows/native/sun/windows/awt_Toolkit.cpp
index 3a9dfb24c..c4292826a 100644
--- a/src/windows/native/sun/windows/awt_Toolkit.cpp
+++ b/src/windows/native/sun/windows/awt_Toolkit.cpp
@@ -23,15 +23,16 @@
* have any questions.
*/
+#include "awt.h"
#include <signal.h>
#include <windowsx.h>
-#if defined(_DEBUG) && defined(_MSC_VER) && _MSC_VER >= 1000
-#include <crtdbg.h>
-#endif
+//#if defined(_DEBUG) && defined(_MSC_VER) && _MSC_VER >= 1000
+//#include <crtdbg.h>
+//#endif
#define _JNI_IMPLEMENTATION_
-#include "stdhdrs.h"
+
#include "awt_DrawingSurface.h"
#include "awt_AWTEvent.h"
#include "awt_Component.h"
@@ -51,7 +52,6 @@
#include "awt_FileDialog.h"
#include "CmdIDList.h"
#include "awt_new.h"
-#include "awt_Unicode.h"
#include "debug_trace.h"
#include "debug_mem.h"
@@ -225,8 +225,7 @@ BOOL AwtToolkit::activateKeyboardLayout(HKL hkl) {
HKL prev = ::ActivateKeyboardLayout(hkl, 0);
// If the above call fails, try loading the layout in case of NT
- if ((prev == 0) && IS_NT) {
-
+ if (!prev) {
// create input locale string, e.g., "00000409", from hkl.
TCHAR inputLocale[9];
TCHAR buf[9];
@@ -297,7 +296,7 @@ JavaStringBuffer::JavaStringBuffer(JNIEnv *env, jstring jstr) {
if (jstr != NULL) {
int length = env->GetStringLength(jstr);
buffer = new TCHAR[length + 1];
- LPCTSTR tmp = (LPCTSTR)JNU_GetStringPlatformChars(env, jstr, NULL);
+ LPCTSTR tmp = JNU_GetStringPlatformChars(env, jstr, NULL);
_tcscpy(buffer, tmp);
JNU_ReleaseStringPlatformChars(env, jstr, tmp);
} else {
@@ -323,6 +322,7 @@ AwtToolkit::AwtToolkit() {
m_vmSignalled = FALSE;
m_isDynamicLayoutSet = FALSE;
+ m_areExtraMouseButtonsEnabled = TRUE;
m_verifyComponents = FALSE;
m_breakOnError = FALSE;
@@ -490,8 +490,6 @@ BOOL AwtToolkit::Dispose() {
::CloseHandle(m_waitEvent);
- ComCtl32Util::GetInstance().FreeLibraries();
-
tk.m_isDisposed = TRUE;
return TRUE;
@@ -886,8 +884,6 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message,
return (DWORD)ImmGetOpenStatus((HIMC)wParam);
}
case WM_DISPLAYCHANGE: {
- AwtCursor::DirtyAllCustomCursors();
-
// Reinitialize screens
initScreens(env);
@@ -1161,9 +1157,7 @@ BOOL AwtToolkit::PreProcessMsg(MSG& msg)
if (p && p->PreProcessMsg(msg) == mrConsume)
return TRUE;
- if ((msg.message >= WM_MOUSEFIRST && msg.message <= WM_AWT_MOUSELAST) ||
- (IS_WIN95 && !IS_WIN98 &&
- msg.message == AwtComponent::Wheel95GetMsg()) ||
+ if ((msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) ||
(msg.message >= WM_NCMOUSEMOVE && msg.message <= WM_NCMBUTTONDBLCLK)) {
if (PreProcessMouseMsg(p, msg)) {
return TRUE;
@@ -1190,9 +1184,7 @@ BOOL AwtToolkit::PreProcessMouseMsg(AwtComponent* p, MSG& msg)
return FALSE;
}
- if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_AWT_MOUSELAST ||
- (IS_WIN95 && !IS_WIN98 && msg.message == AwtComponent::Wheel95GetMsg()))
- {
+ if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) {
mouseWParam = msg.wParam;
mouseLParam = msg.lParam;
} else {
@@ -1287,21 +1279,6 @@ BOOL AwtToolkit::PreProcessMouseMsg(AwtComponent* p, MSG& msg)
//window
msg.hwnd = hWndForWheel;
}
- else if (IS_WIN95 && !IS_WIN98 &&
- msg.message == AwtComponent::Wheel95GetMsg() &&
- mouseWheelComp != NULL) {
-
- // On Win95, mouse wheels are _always_ delivered to the top level
- // Frame. Default behavior only takes place if the message's hwnd
- // remains that of the Frame. We only want to change the hwnd if
- // we're changing it to a Component that DOESN'T handle the
- // mousewheel natively.
-
- if (!mouseWheelComp->InheritsNativeMouseWheelBehavior()) {
- DTRACE_PRINTLN("AwtT::PPMM: changing hwnd on 95");
- msg.hwnd = hWndForWheel;
- }
- }
/*
* Make sure we get at least one last chance to check for transitions
@@ -1792,7 +1769,7 @@ Java_sun_awt_windows_WToolkit_getScreenInsets(JNIEnv *env,
{
jobject insets = NULL;
RECT rRW;
- MONITOR_INFO *miInfo;
+ LPMONITORINFO miInfo;
TRY;
@@ -1814,10 +1791,10 @@ Java_sun_awt_windows_WToolkit_getScreenInsets(JNIEnv *env,
if (miInfo) {
insets = env->NewObject(env->FindClass("java/awt/Insets"),
AwtToolkit::insetsMID,
- miInfo->rWork.top - miInfo->rMonitor.top,
- miInfo->rWork.left - miInfo->rMonitor.left,
- miInfo->rMonitor.bottom - miInfo->rWork.bottom,
- miInfo->rMonitor.right - miInfo->rWork.right);
+ miInfo->rcWork.top - miInfo->rcMonitor.top,
+ miInfo->rcWork.left - miInfo->rcMonitor.left,
+ miInfo->rcMonitor.bottom - miInfo->rcWork.bottom,
+ miInfo->rcMonitor.right - miInfo->rcWork.right);
}
}
@@ -2057,29 +2034,15 @@ Java_sun_awt_windows_WToolkit_getWindowsVersion(JNIEnv *env, jclass cls)
swprintf(szVer, L"0x%x = %ld", version, version);
int l = lstrlen(szVer);
- if (IS_WIN95) {
- if (IS_WIN98) {
- if (IS_WINME) {
- swprintf(szVer + l, L" (Windows ME)");
- } else {
- swprintf(szVer + l, L" (Windows 98)");
- }
- } else {
- swprintf(szVer + l, L" (Windows 95)");
- }
- } else if (IS_NT) {
- if (IS_WIN2000) {
- if (IS_WINXP) {
- if (IS_WINVISTA) {
- swprintf(szVer + l, L" (Windows Vista)");
- } else {
- swprintf(szVer + l, L" (Windows XP)");
- }
+ if (IS_WIN2000) {
+ if (IS_WINXP) {
+ if (IS_WINVISTA) {
+ swprintf(szVer + l, L" (Windows Vista)");
} else {
- swprintf(szVer + l, L" (Windows 2000)");
+ swprintf(szVer + l, L" (Windows XP)");
}
} else {
- swprintf(szVer + l, L" (Windows NT)");
+ swprintf(szVer + l, L" (Windows 2000)");
}
} else {
swprintf(szVer + l, L" (Unknown)");
@@ -2130,3 +2093,26 @@ Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
splashClose();
}
}
+
+/*
+ * accessible from awt_Component
+ */
+BOOL AwtToolkit::areExtraMouseButtonsEnabled() {
+ return m_areExtraMouseButtonsEnabled;
+}
+
+/*
+ * Class: sun_awt_windows_WToolkit
+ * Method: setExtraMouseButtonsEnabledNative
+ * Signature: (Z)V
+ */
+extern "C" JNIEXPORT void JNICALL Java_sun_awt_windows_WToolkit_setExtraMouseButtonsEnabledNative
+(JNIEnv *env, jclass self, jboolean enable){
+ TRY;
+ AwtToolkit::GetInstance().setExtraMouseButtonsEnabled(enable);
+ CATCH_BAD_ALLOC;
+}
+
+void AwtToolkit::setExtraMouseButtonsEnabled(BOOL enable) {
+ m_areExtraMouseButtonsEnabled = enable;
+}
diff --git a/src/windows/native/sun/windows/awt_Toolkit.h b/src/windows/native/sun/windows/awt_Toolkit.h
index fcee2d4ad..d76546fa4 100644
--- a/src/windows/native/sun/windows/awt_Toolkit.h
+++ b/src/windows/native/sun/windows/awt_Toolkit.h
@@ -50,7 +50,6 @@
#include "awt.h"
#include "awtmsg.h"
-#include "awt_Multimon.h"
#include "Trace.h"
#include "sun_awt_windows_WToolkit.h"
@@ -89,11 +88,8 @@ class JNILocalFrame {
*/
class CriticalSection {
public:
- INLINE CriticalSection() { ::InitializeCriticalSection(&rep);
- ::InitializeCriticalSection(&tryrep);
- tryEntered = 0; }
- INLINE ~CriticalSection() { ::DeleteCriticalSection(&rep);
- ::DeleteCriticalSection(&tryrep); }
+ INLINE CriticalSection() { ::InitializeCriticalSection(&rep); }
+ INLINE ~CriticalSection() { ::DeleteCriticalSection(&rep); }
class Lock {
public:
@@ -111,50 +107,18 @@ class CriticalSection {
private:
CRITICAL_SECTION rep;
- CRITICAL_SECTION tryrep;
- long tryEntered;
-
CriticalSection(const CriticalSection&);
const CriticalSection& operator =(const CriticalSection&);
public:
- virtual void Enter (void)
- {
- ::EnterCriticalSection(&tryrep);
- tryEntered++;
- if (tryEntered == 1) {
- ::EnterCriticalSection(&rep);
- ::LeaveCriticalSection(&tryrep);
- } else {
- ::LeaveCriticalSection(&tryrep);
- ::EnterCriticalSection(&rep);
- }
+ virtual void Enter() {
+ ::EnterCriticalSection(&rep);
}
- // we cannot use ::TryEnterCriticalSection as it is not supported on Win9x/Me
- virtual BOOL TryEnter (void)
- {
- BOOL result = FALSE;
- ::EnterCriticalSection(&tryrep);
- if (tryEntered == 0) {
- ::EnterCriticalSection(&rep);
- tryEntered++;
- result = TRUE;
- }
- ::LeaveCriticalSection(&tryrep);
- return result;
+ virtual BOOL TryEnter() {
+ return ::TryEnterCriticalSection(&rep);
}
- virtual void Leave (void)
- {
- ::EnterCriticalSection(&tryrep);
- if (tryEntered > 0) {
- tryEntered--;
- } else {
- // this may happen only if we call to Leave() before
- // Enter() so this is definitely a bug
- DASSERT(FALSE);
- }
+ virtual void Leave() {
::LeaveCriticalSection(&rep);
- ::LeaveCriticalSection(&tryrep);
}
};
@@ -211,6 +175,8 @@ public:
BOOL IsDynamicLayoutSet();
BOOL IsDynamicLayoutSupported();
BOOL IsDynamicLayoutActive();
+ BOOL areExtraMouseButtonsEnabled();
+ void setExtraMouseButtonsEnabled(BOOL enable);
INLINE BOOL localPump() { return m_localPump; }
INLINE BOOL VerifyComponents() { return FALSE; } // TODO: Use new DebugHelper class to set this flag
@@ -387,6 +353,7 @@ private:
BOOL m_verbose;
BOOL m_isActive; // set to FALSE at beginning of Dispose
BOOL m_isDisposed; // set to TRUE at end of Dispose
+ BOOL m_areExtraMouseButtonsEnabled;
BOOL m_vmSignalled; // set to TRUE if QUERYENDSESSION has successfully
// raised SIGTERM
diff --git a/src/windows/native/sun/windows/awt_TrayIcon.cpp b/src/windows/native/sun/windows/awt_TrayIcon.cpp
index 0d3047b59..9165314bb 100644
--- a/src/windows/native/sun/windows/awt_TrayIcon.cpp
+++ b/src/windows/native/sun/windows/awt_TrayIcon.cpp
@@ -23,9 +23,10 @@
* have any questions.
*/
-#include <windows.h>
+#include "awt.h"
#include <windowsx.h>
#include <shellapi.h>
+#include <shlwapi.h>
#include "awt_Toolkit.h"
#include "awt_TrayIcon.h"
@@ -173,27 +174,20 @@ AwtTrayIcon* AwtTrayIcon::Create(jobject self, jobject parent)
env->DeleteLocalRef(target);
return awtTrayIcon;
}
-typedef struct _SDLLVERSIONINFO
-{
- DWORD cbSize;
- DWORD dwMajorVersion; // Major version
- DWORD dwMinorVersion; // Minor version
- DWORD dwBuildNumber; // Build number
- DWORD dwPlatformID; // DLLVER_PLATFORM_*
-} SDLLVERSIONINFO;
-typedef HRESULT (CALLBACK* SDLLGETVERSIONPROC)(SDLLVERSIONINFO *);
void AwtTrayIcon::InitNID(UINT uID)
{
// fix for 6271589: we MUST set the size of the structure to match
// the shell version, otherwise some errors may occur (like missing
// balloon messages on win2k)
- SDLLVERSIONINFO dllVersionInfo;
- dllVersionInfo.cbSize = sizeof(SDLLVERSIONINFO);
- int shellVersion = 4; // WIN_98
+ DLLVERSIONINFO dllVersionInfo;
+ dllVersionInfo.cbSize = sizeof(DLLVERSIONINFO);
+ int shellVersion = 5; // WIN_2000
+ // MSDN: DllGetVersion should not be implicitly called, but rather
+ // loaded using GetProcAddress
HMODULE hShell = LoadLibrary(TEXT("Shell32.dll"));
if (hShell != NULL) {
- SDLLGETVERSIONPROC proc = (SDLLGETVERSIONPROC)GetProcAddress(hShell, "DllGetVersion");
+ DLLGETVERSIONPROC proc = (DLLGETVERSIONPROC)GetProcAddress(hShell, "DllGetVersion");
if (proc != NULL) {
if (proc(&dllVersionInfo) == NOERROR) {
shellVersion = dllVersionInfo.dwMajorVersion;
@@ -202,14 +196,16 @@ void AwtTrayIcon::InitNID(UINT uID)
}
FreeLibrary(hShell);
switch (shellVersion) {
- case 5: // WIN_2000, WIN_ME
+ case 5: // WIN_2000
m_nid.cbSize = (BYTE *)(&m_nid.guidItem) - (BYTE *)(&m_nid.cbSize);
break;
case 6: // WIN_XP
+ // Uncomment these two lines when moving to VS2008
+// m_nid.cbSize = (BYTE *)(&m_nid.hBalloonIcon) - (BYTE *)(&m_nid.cbSize);
+// break;
+ default: // WIN_VISTA?
m_nid.cbSize = sizeof(m_nid);
break;
- default: // WIN_98, WIN_NT
- m_nid.cbSize = (BYTE *)(&m_nid.szTip) - (BYTE *)(&m_nid.cbSize) + sizeof(m_nid.szTip) / 2;
}
m_nid.hWnd = AwtTrayIcon::sm_msgWindow;
m_nid.uID = uID;
@@ -217,7 +213,7 @@ void AwtTrayIcon::InitNID(UINT uID)
m_nid.uCallbackMessage = WM_AWT_TRAY_NOTIFY;
m_nid.hIcon = AwtToolkit::GetInstance().GetAwtIcon();
m_nid.szTip[0] = '\0';
- m_nid.uVersion = IS_WIN2000 ? AWT_NOTIFYICON_VERSION : 0;
+ m_nid.uVersion = NOTIFYICON_VERSION;
}
BOOL AwtTrayIcon::SendTrayMessage(DWORD dwMessage)
@@ -304,13 +300,13 @@ MsgRouting AwtTrayIcon::WmAwtTrayNotify(WPARAM wParam, LPARAM lParam)
case WM_CONTEXTMENU:
mr = WmContextMenu(0, pos.x, pos.y);
break;
- case AWT_NIN_KEYSELECT:
+ case NIN_KEYSELECT:
mr = WmKeySelect(0, pos.x, pos.y);
break;
- case AWT_NIN_SELECT:
+ case NIN_SELECT:
mr = WmSelect(0, pos.x, pos.y);
break;
- case AWT_NIN_BALLOONUSERCLICK:
+ case NIN_BALLOONUSERCLICK:
mr = WmBalloonUserClick(0, pos.x, pos.y);
break;
}
@@ -371,7 +367,7 @@ MsgRouting AwtTrayIcon::WmMouseUp(UINT flags, int x, int y, int button)
(AwtComponent::GetButton(button) == java_awt_event_MouseEvent_BUTTON3 ?
TRUE : FALSE), AwtComponent::GetButton(button), &msg);
- if ((m_mouseButtonClickAllowed & AwtComponent::GetButtonMK(button)) != 1) { // No up-button in the drag-state
+ if ((m_mouseButtonClickAllowed & AwtComponent::GetButtonMK(button)) != 0) { // No up-button in the drag-state
SendMouseEvent(java_awt_event_MouseEvent_MOUSE_CLICKED,
TimeHelper::windowsToUTC(::GetTickCount()), x, y, AwtComponent::GetJavaModifiers(),
clickCount, JNI_FALSE, AwtComponent::GetButton(button));
@@ -397,7 +393,7 @@ MsgRouting AwtTrayIcon::WmMouseMove(UINT flags, int x, int y)
lastX = x;
lastY = y;
AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
- if ((flags & AwtComponent::ALL_MK_BUTTONS) != 0) {
+ if ((flags & ALL_MK_BUTTONS) != 0) {
m_mouseButtonClickAllowed = 0;
} else {
SendMouseEvent(java_awt_event_MouseEvent_MOUSE_MOVED, TimeHelper::windowsToUTC(::GetTickCount()), x, y,
@@ -473,7 +469,7 @@ MsgRouting AwtTrayIcon::WmTaskbarCreated() {
BOOL result = item->m_trayIcon->SendTrayMessage(NIM_ADD);
// 6270114: Instructs the taskbar to behave according to the Shell version 5.0
if (result) {
- item->m_trayIcon->SendTrayMessage(AWT_NIM_SETVERSION);
+ item->m_trayIcon->SendTrayMessage(NIM_SETVERSION);
}
}
return mrDoDefault;
@@ -733,9 +729,9 @@ void AwtTrayIcon::_SetToolTip(void *param)
goto ret;
}
- tooltipStr = env->GetStringChars(jtooltip, (jboolean *)NULL);
+ tooltipStr = JNU_GetStringPlatformChars(env, jtooltip, (jboolean *)NULL);
trayIcon->SetToolTip(tooltipStr);
- env->ReleaseStringChars(jtooltip, tooltipStr);
+ JNU_ReleaseStringPlatformChars(env, jtooltip, tooltipStr);
ret:
env->DeleteGlobalRef(self);
env->DeleteGlobalRef(jtooltip);
@@ -782,7 +778,7 @@ void AwtTrayIcon::_UpdateIcon(void *param)
BOOL result = trayIcon->SendTrayMessage(jupdate == JNI_TRUE ? NIM_MODIFY : NIM_ADD);
// 6270114: Instructs the taskbar to behave according to the Shell version 5.0
if (result && jupdate == JNI_FALSE) {
- trayIcon->SendTrayMessage(AWT_NIM_SETVERSION);
+ trayIcon->SendTrayMessage(NIM_SETVERSION);
}
ret:
env->DeleteGlobalRef(self);
@@ -791,22 +787,19 @@ ret:
void AwtTrayIcon::DisplayMessage(LPCTSTR caption, LPCTSTR text, LPCTSTR msgType)
{
- if (!IS_WIN2000)
- return;
-
- m_nid.uFlags |= AWT_NIF_INFO;
+ m_nid.uFlags |= NIF_INFO;
m_nid.uTimeout = 10000;
if (lstrcmp(msgType, TEXT("ERROR")) == 0) {
- m_nid.dwInfoFlags = AWT_NIIF_ERROR;
+ m_nid.dwInfoFlags = NIIF_ERROR;
} else if (lstrcmp(msgType, TEXT("WARNING")) == 0) {
- m_nid.dwInfoFlags = AWT_NIIF_WARNING;
+ m_nid.dwInfoFlags = NIIF_WARNING;
} else if (lstrcmp(msgType, TEXT("INFO")) == 0) {
- m_nid.dwInfoFlags = AWT_NIIF_INFO;
+ m_nid.dwInfoFlags = NIIF_INFO;
} else if (lstrcmp(msgType, TEXT("NONE")) == 0) {
- m_nid.dwInfoFlags = AWT_NIIF_NONE;
+ m_nid.dwInfoFlags = NIIF_NONE;
} else {
- m_nid.dwInfoFlags = AWT_NIIF_NONE;
+ m_nid.dwInfoFlags = NIIF_NONE;
}
if (caption[0] == '\0') {
@@ -835,7 +828,7 @@ void AwtTrayIcon::DisplayMessage(LPCTSTR caption, LPCTSTR text, LPCTSTR msgType)
}
SendTrayMessage(NIM_MODIFY);
- m_nid.uFlags &= ~AWT_NIF_INFO;
+ m_nid.uFlags &= ~NIF_INFO;
}
void AwtTrayIcon::_DisplayMessage(void *param)
@@ -855,15 +848,15 @@ void AwtTrayIcon::_DisplayMessage(void *param)
JNI_CHECK_PEER_GOTO(self, ret);
trayIcon = (AwtTrayIcon *)pData;
- captionStr = env->GetStringChars(jcaption, (jboolean *)NULL);
- textStr = env->GetStringChars(jtext, (jboolean *)NULL);
- msgTypeStr = env->GetStringChars(jmsgType, (jboolean *)NULL);
+ captionStr = JNU_GetStringPlatformChars(env, jcaption, (jboolean *)NULL);
+ textStr = JNU_GetStringPlatformChars(env, jtext, (jboolean *)NULL);
+ msgTypeStr = JNU_GetStringPlatformChars(env, jmsgType, (jboolean *)NULL);
trayIcon->DisplayMessage(captionStr, textStr, msgTypeStr);
- env->ReleaseStringChars(jcaption, captionStr);
- env->ReleaseStringChars(jtext, textStr);
- env->ReleaseStringChars(jmsgType, msgTypeStr);
+ JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
+ JNU_ReleaseStringPlatformChars(env, jtext, textStr);
+ JNU_ReleaseStringPlatformChars(env, jmsgType, msgTypeStr);
ret:
env->DeleteGlobalRef(self);
env->DeleteGlobalRef(jcaption);
@@ -1057,16 +1050,14 @@ Java_sun_awt_windows_WTrayIconPeer__1displayMessage(JNIEnv *env, jobject self,
{
TRY;
- if (IS_WIN2000) {
- DisplayMessageStruct *dms = new DisplayMessageStruct;
- dms->trayIcon = env->NewGlobalRef(self);
- dms->caption = (jstring)env->NewGlobalRef(caption);
- dms->text = (jstring)env->NewGlobalRef(text);
- dms->msgType = (jstring)env->NewGlobalRef(msgType);
+ DisplayMessageStruct *dms = new DisplayMessageStruct;
+ dms->trayIcon = env->NewGlobalRef(self);
+ dms->caption = (jstring)env->NewGlobalRef(caption);
+ dms->text = (jstring)env->NewGlobalRef(text);
+ dms->msgType = (jstring)env->NewGlobalRef(msgType);
- AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_DisplayMessage, dms);
- // global ref is deleted in _DisplayMessage
- }
+ AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_DisplayMessage, dms);
+ // global ref is deleted in _DisplayMessage
CATCH_BAD_ALLOC(NULL);
}
diff --git a/src/windows/native/sun/windows/awt_TrayIcon.h b/src/windows/native/sun/windows/awt_TrayIcon.h
index 6a3c25dd7..8ee418aa7 100644
--- a/src/windows/native/sun/windows/awt_TrayIcon.h
+++ b/src/windows/native/sun/windows/awt_TrayIcon.h
@@ -36,57 +36,11 @@
#define TRAY_ICON_X_HOTSPOT 0
#define TRAY_ICON_Y_HOTSPOT 0
-#define TRAY_ICON_TOOLTIP_MAX_SIZE (IS_WIN2000 ? 128 : 64)
+#define TRAY_ICON_TOOLTIP_MAX_SIZE 128
#define TRAY_ICON_BALLOON_TITLE_MAX_SIZE 64
#define TRAY_ICON_BALLOON_INFO_MAX_SIZE 256
-// **********************************************************************
-// The following definitions are duplicates for those from the shellapi.h
-// **********************************************************************
-
-#define AWT_NOTIFYICON_VERSION 3
-
-#define AWT_NIM_SETVERSION 0x00000004
-
-#define AWT_NIN_SELECT (WM_USER + 0)
-#define AWT_NINF_KEY 0x1
-#define AWT_NIN_KEYSELECT (AWT_NIN_SELECT | AWT_NINF_KEY)
-#define AWT_NIN_BALLOONSHOW (WM_USER + 2)
-#define AWT_NIN_BALLOONHIDE (WM_USER + 3)
-#define AWT_NIN_BALLOONTIMEOUT (WM_USER + 4)
-#define AWT_NIN_BALLOONUSERCLICK (WM_USER + 5)
-
-#define AWT_NIIF_NONE 0x00000000
-#define AWT_NIIF_INFO 0x00000001
-#define AWT_NIIF_WARNING 0x00000002
-#define AWT_NIIF_ERROR 0x00000003
-
-#define AWT_NIF_INFO 0x00000010
-
-typedef struct _AWT_NOTIFYICONDATA {
- DWORD cbSize;
- HWND hWnd;
- UINT uID;
- UINT uFlags;
- UINT uCallbackMessage;
- HICON hIcon;
- TCHAR szTip[128];
-
- DWORD dwState; // _WIN32_IE >= 0x0500
- DWORD dwStateMask;
- TCHAR szInfo[256];
- union {
- UINT uTimeout;
- UINT uVersion;
- } DUMMYUNIONNAME;
- TCHAR szInfoTitle[64];
- DWORD dwInfoFlags;
-
- GUID guidItem; // _WIN32_IE >= 0x600
-} AWT_NOTIFYICONDATA, *PAWT_NOTIFYICONDATA;
-
-
/************************************************************************
* AwtTrayIcon class
*/
@@ -174,7 +128,7 @@ public:
static int sm_instCount;
private:
- AWT_NOTIFYICONDATA m_nid;
+ NOTIFYICONDATA m_nid;
/* A bitmask keeps the button's numbers as MK_LBUTTON, MK_MBUTTON, MK_RBUTTON
* which are allowed to
diff --git a/src/windows/native/sun/windows/awt_Win32GraphicsConfig.cpp b/src/windows/native/sun/windows/awt_Win32GraphicsConfig.cpp
index 6f2f62c31..2a23bfa9b 100644
--- a/src/windows/native/sun/windows/awt_Win32GraphicsConfig.cpp
+++ b/src/windows/native/sun/windows/awt_Win32GraphicsConfig.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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
@@ -23,12 +23,12 @@
* have any questions.
*/
-#include <windows.h>
#include "awt.h"
#include <sun_awt_Win32GraphicsConfig.h>
#include "awt_Win32GraphicsConfig.h"
#include "awt_Canvas.h"
#include "awt_Win32GraphicsDevice.h"
+#include "Devices.h"
//Info for building a ColorModel
#include "java_awt_image_DataBuffer.h"
@@ -93,8 +93,8 @@ JNIEXPORT jobject JNICALL
clazz = env->FindClass("java/awt/Rectangle");
mid = env->GetMethodID(clazz, "<init>", "(IIII)V");
if (mid != 0) {
- RECT rRW = {0,0,0,0};
- if( TRUE == ::MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW) ) {
+ RECT rRW = {0, 0, 0, 0};
+ if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) {
bounds = env->NewObject(clazz, mid,
rRW.left, rRW.top,
rRW.right - rRW.left,
diff --git a/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp b/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp
index 047f7148c..aba41a5a4 100644
--- a/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp
+++ b/src/windows/native/sun/windows/awt_Win32GraphicsDevice.cpp
@@ -37,19 +37,17 @@
* array index.
*/
-#include <windows.h>
-#include <jni.h>
#include <awt.h>
#include <sun_awt_Win32GraphicsDevice.h>
#include "awt_Canvas.h"
#include "awt_Win32GraphicsDevice.h"
+#include "awt_Window.h"
#include "java_awt_Transparency.h"
#include "java_awt_color_ColorSpace.h"
#include "sun_awt_Win32GraphicsDevice.h"
#include "java_awt_image_DataBuffer.h"
#include "dither.h"
#include "img_util_md.h"
-#include "awt_dlls.h"
#include "Devices.h"
uns_ordered_dither_array img_oda_alpha;
@@ -72,7 +70,7 @@ int AwtWin32GraphicsDevice::primaryIndex = 0;
* device, and information on whether the primary device is palettized.
*/
AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen,
- MHND mhnd, Devices *arr)
+ HMONITOR mhnd, Devices *arr)
{
this->screen = screen;
this->devicesArray = arr;
@@ -83,8 +81,8 @@ AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen,
cData = NULL;
gpBitmapInfo = NULL;
monitor = mhnd;
- pMonitorInfo = (PMONITOR_INFO)new MONITOR_INFO_EXTENDED;
- pMonitorInfo->dwSize = sizeof(MONITOR_INFO_EXTENDED);
+ pMonitorInfo = new MONITORINFOEX;
+ pMonitorInfo->cbSize = sizeof(MONITORINFOEX);
::GetMonitorInfo(monitor, pMonitorInfo);
// Set primary device info: other devices will need to know
@@ -93,7 +91,7 @@ AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen,
HDC hDC = this->GetDC();
colorData->bitsperpixel = ::GetDeviceCaps(hDC, BITSPIXEL);
this->ReleaseDC(hDC);
- if (MONITOR_INFO_FLAG_PRIMARY & pMonitorInfo->dwFlags) {
+ if (MONITORINFOF_PRIMARY & pMonitorInfo->dwFlags) {
primaryIndex = screen;
if (colorData->bitsperpixel > 8) {
primaryPalettized = FALSE;
@@ -124,6 +122,24 @@ AwtWin32GraphicsDevice::~AwtWin32GraphicsDevice()
}
}
+HDC AwtWin32GraphicsDevice::MakeDCFromMonitor(HMONITOR hmMonitor) {
+ HDC retCode = NULL;
+ if (NULL != hmMonitor) {
+ MONITORINFOEX mieInfo;
+
+ memset((void*)(&mieInfo), 0, sizeof(MONITORINFOEX));
+ mieInfo.cbSize = sizeof(MONITORINFOEX);
+
+ if (TRUE == ::GetMonitorInfo(hmMonitor, (LPMONITORINFOEX)(&mieInfo))) {
+ HDC hDC = CreateDC(mieInfo.szDevice, NULL, NULL, NULL);
+ if (NULL != hDC) {
+ retCode = hDC;
+ }
+ }
+ }
+ return retCode;
+}
+
HDC AwtWin32GraphicsDevice::GetDC()
{
return MakeDCFromMonitor(monitor);
@@ -164,7 +180,7 @@ void AwtWin32GraphicsDevice::Initialize()
VERIFY(::GetDIBits(hBMDC, hBM, 0, 1, NULL, gpBitmapInfo, DIB_RGB_COLORS));
if (colorData->bitsperpixel > 8) {
- if (MONITOR_INFO_FLAG_PRIMARY & pMonitorInfo->dwFlags) {
+ if (MONITORINFOF_PRIMARY & pMonitorInfo->dwFlags) {
primaryPalettized = FALSE;
}
if (colorData->bitsperpixel != 24) { // 15, 16, or 32 bpp
@@ -250,7 +266,7 @@ void AwtWin32GraphicsDevice::Initialize()
((int *)gpBitmapInfo->bmiColors)[2] = 0xff0000;
}
} else {
- if (MONITOR_INFO_FLAG_PRIMARY & pMonitorInfo->dwFlags) {
+ if (MONITORINFOF_PRIMARY & pMonitorInfo->dwFlags) {
primaryPalettized = TRUE;
}
gpBitmapInfo->bmiHeader.biBitCount = 8;
@@ -565,8 +581,8 @@ void AwtWin32GraphicsDevice::RealizePalette(HDC hDC)
*/
int AwtWin32GraphicsDevice::DeviceIndexForWindow(HWND hWnd)
{
- MHND mon = MonitorFromWindow(hWnd, MONITOR_DEFAULT_TO_NEAR);
- int screen = AwtWin32GraphicsDevice::GetScreenFromMHND(mon);
+ HMONITOR mon = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
+ int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(mon);
return screen;
}
@@ -645,14 +661,14 @@ jobject AwtWin32GraphicsDevice::GetColorModel(JNIEnv *env, jboolean dynamic,
return devices->GetDevice(deviceIndex)->GetColorModel(env, dynamic);
}
-MONITOR_INFO *AwtWin32GraphicsDevice::GetMonitorInfo(int deviceIndex)
+LPMONITORINFO AwtWin32GraphicsDevice::GetMonitorInfo(int deviceIndex)
{
Devices::InstanceAccess devices;
return devices->GetDevice(deviceIndex)->GetMonitorInfo();
}
/**
- * This function updates the data in the MONITOR_INFO structure pointed to by
+ * This function updates the data in the MONITORINFOEX structure pointed to by
* pMonitorInfo for all monitors on the system. Added for 4654713.
*/
void AwtWin32GraphicsDevice::ResetAllMonitorInfo()
@@ -660,14 +676,14 @@ void AwtWin32GraphicsDevice::ResetAllMonitorInfo()
Devices::InstanceAccess devices;
int devicesNum = devices->GetNumDevices();
for (int deviceIndex = 0; deviceIndex < devicesNum; deviceIndex++) {
- MHND monitor = devices->GetDevice(deviceIndex)->GetMonitor();
+ HMONITOR monitor = devices->GetDevice(deviceIndex)->GetMonitor();
::GetMonitorInfo(monitor,
devices->GetDevice(deviceIndex)->pMonitorInfo);
}
}
void AwtWin32GraphicsDevice::DisableOffscreenAccelerationForDevice(
- MHND hMonitor)
+ HMONITOR hMonitor)
{
Devices::InstanceAccess devices;
if (hMonitor == NULL) {
@@ -682,7 +698,7 @@ void AwtWin32GraphicsDevice::DisableOffscreenAccelerationForDevice(
}
}
-MHND AwtWin32GraphicsDevice::GetMonitor(int deviceIndex)
+HMONITOR AwtWin32GraphicsDevice::GetMonitor(int deviceIndex)
{
Devices::InstanceAccess devices;
return devices->GetDevice(deviceIndex)->GetMonitor();
@@ -741,30 +757,31 @@ HDC AwtWin32GraphicsDevice::GetDCFromScreen(int screen) {
return MakeDCFromMonitor(dev->GetMonitor());
}
-/** Compare elements of MONITOR_INFO structures for the given MHNDs.
+/** Compare elements of MONITORINFOEX structures for the given HMONITORs.
* If equal, return TRUE
*/
-BOOL AwtWin32GraphicsDevice::AreSameMonitors(MHND mon1, MHND mon2) {
+BOOL AwtWin32GraphicsDevice::AreSameMonitors(HMONITOR mon1, HMONITOR mon2) {
J2dTraceLn2(J2D_TRACE_INFO,
"AwtWin32GraphicsDevice::AreSameMonitors mhnd1=%x mhnd2=%x",
mon1, mon2);
DASSERT(mon1 != NULL);
DASSERT(mon2 != NULL);
- MONITOR_INFO mi1;
- MONITOR_INFO mi2;
-
- memset((void*)(&mi1),0,sizeof(MONITOR_INFO));
- mi1.dwSize = sizeof(MONITOR_INFO);
- memset((void*)(&mi2),0,sizeof(MONITOR_INFO));
- mi2.dwSize = sizeof(MONITOR_INFO);
+ MONITORINFOEX mi1;
+ MONITORINFOEX mi2;
- if (::GetMonitorInfo(mon1,&mi1) != 0 &&
- ::GetMonitorInfo(mon2,&mi2) != 0 ) {
+ memset((void*)(&mi1), 0, sizeof(MONITORINFOEX));
+ mi1.cbSize = sizeof(MONITORINFOEX);
+ memset((void*)(&mi2), 0, sizeof(MONITORINFOEX));
+ mi2.cbSize = sizeof(MONITORINFOEX);
- if (::EqualRect(&mi1.rMonitor,&mi2.rMonitor) &&
- ::EqualRect(&mi1.rWork,&mi2.rWork) &&
- mi1.dwFlags == mi1.dwFlags) {
+ if (::GetMonitorInfo(mon1, &mi1) != 0 &&
+ ::GetMonitorInfo(mon2, &mi2) != 0 )
+ {
+ if (::EqualRect(&mi1.rcMonitor, &mi2.rcMonitor) &&
+ ::EqualRect(&mi1.rcWork, &mi2.rcWork) &&
+ (mi1.dwFlags == mi1.dwFlags))
+ {
J2dTraceLn(J2D_TRACE_VERBOSE, " the monitors are the same");
return TRUE;
@@ -774,15 +791,15 @@ BOOL AwtWin32GraphicsDevice::AreSameMonitors(MHND mon1, MHND mon2) {
return FALSE;
}
-int AwtWin32GraphicsDevice::GetScreenFromMHND(MHND mon) {
+int AwtWin32GraphicsDevice::GetScreenFromHMONITOR(HMONITOR mon) {
J2dTraceLn1(J2D_TRACE_INFO,
- "AwtWin32GraphicsDevice::GetScreenFromMHND mhnd=%x", mon);
+ "AwtWin32GraphicsDevice::GetScreenFromHMONITOR mhnd=%x", mon);
DASSERT(mon != NULL);
Devices::InstanceAccess devices;
for (int i = 0; i < devices->GetNumDevices(); i++) {
- MHND mhnd = devices->GetDevice(i)->GetMonitor();
+ HMONITOR mhnd = devices->GetDevice(i)->GetMonitor();
if (AreSameMonitors(mon, mhnd)) {
J2dTraceLn1(J2D_TRACE_VERBOSE, " Found device: %d", i);
return i;
@@ -790,8 +807,8 @@ int AwtWin32GraphicsDevice::GetScreenFromMHND(MHND mon) {
}
J2dTraceLn1(J2D_TRACE_WARNING,
- "AwtWin32GraphicsDevice::GetScreenFromMHND(): "\
- "couldn't find screen for MHND %x, returning default", mon);
+ "AwtWin32GraphicsDevice::GetScreenFromHMONITOR(): "\
+ "couldn't find screen for HMONITOR %x, returning default", mon);
return AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
}
@@ -1076,19 +1093,19 @@ jobject CreateDisplayMode(JNIEnv* env, jint width, jint height,
* of the structure pointed to by lpDisplayDevice is undefined.
*/
static BOOL
-GetAttachedDisplayDevice(int screen, _DISPLAY_DEVICE *lpDisplayDevice)
+GetAttachedDisplayDevice(int screen, DISPLAY_DEVICE *lpDisplayDevice)
{
DWORD dwDeviceNum = 0;
- lpDisplayDevice->dwSize = sizeof(_DISPLAY_DEVICE);
+ lpDisplayDevice->cb = sizeof(DISPLAY_DEVICE);
while (EnumDisplayDevices(NULL, dwDeviceNum, lpDisplayDevice, 0) &&
dwDeviceNum < 20) // avoid infinite loop with buggy drivers
{
- if (lpDisplayDevice->dwFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
+ if (lpDisplayDevice->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
Devices::InstanceAccess devices;
- MONITOR_INFO_EXTENDED *pMonInfo =
- (PMONITOR_INFO_EXTENDED) devices->GetDevice(screen)->GetMonitorInfo();
+ MONITORINFOEX *pMonInfo =
+ (LPMONITORINFOEX)devices->GetDevice(screen)->GetMonitorInfo();
// make sure the device names match
- if (wcscmp(pMonInfo->strDevice, lpDisplayDevice->strDevName) == 0) {
+ if (wcscmp(pMonInfo->szDevice, lpDisplayDevice->DeviceName) == 0) {
return TRUE;
}
}
@@ -1114,9 +1131,9 @@ Java_sun_awt_Win32GraphicsDevice_getCurrentDisplayMode
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
- _DISPLAY_DEVICE displayDevice;
+ DISPLAY_DEVICE displayDevice;
if (GetAttachedDisplayDevice(screen, &displayDevice)) {
- pName = displayDevice.strDevName;
+ pName = displayDevice.DeviceName;
}
if (!EnumDisplaySettings(pName, ENUM_CURRENT_SETTINGS, &dm))
{
@@ -1156,7 +1173,7 @@ Java_sun_awt_Win32GraphicsDevice_configDisplayMode
// ChangeDisplaySettingsEx is not available on NT,
// so it'd be nice not to break it if we can help it.
if (screen == AwtWin32GraphicsDevice::GetDefaultDeviceIndex()) {
- if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) !=
+ if (::ChangeDisplaySettings(&dm, CDS_FULLSCREEN) !=
DISP_CHANGE_SUCCESSFUL)
{
JNU_ThrowInternalError(env,
@@ -1165,15 +1182,9 @@ Java_sun_awt_Win32GraphicsDevice_configDisplayMode
return;
}
- // make sure the function pointer for fn_change_display_settings_ex
- // is initialized
- load_user_procs();
-
- _DISPLAY_DEVICE displayDevice;
- if (fn_change_display_settings_ex == NULL ||
- !GetAttachedDisplayDevice(screen, &displayDevice) ||
- ((*fn_change_display_settings_ex)
- (displayDevice.strDevName, &dm, NULL, CDS_FULLSCREEN, NULL) !=
+ DISPLAY_DEVICE displayDevice;
+ if (!GetAttachedDisplayDevice(screen, &displayDevice) ||
+ (::ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, CDS_FULLSCREEN, NULL) !=
DISP_CHANGE_SUCCESSFUL))
{
JNU_ThrowInternalError(env,
@@ -1231,11 +1242,11 @@ JNIEXPORT void JNICALL Java_sun_awt_Win32GraphicsDevice_enumDisplayModes
DEVMODE dm;
LPTSTR pName = NULL;
- _DISPLAY_DEVICE displayDevice;
+ DISPLAY_DEVICE displayDevice;
if (GetAttachedDisplayDevice(screen, &displayDevice)) {
- pName = displayDevice.strDevName;
+ pName = displayDevice.DeviceName;
}
dm.dmSize = sizeof(dm);
diff --git a/src/windows/native/sun/windows/awt_Win32GraphicsDevice.h b/src/windows/native/sun/windows/awt_Win32GraphicsDevice.h
index 9d3978ba3..42a7ff180 100644
--- a/src/windows/native/sun/windows/awt_Win32GraphicsDevice.h
+++ b/src/windows/native/sun/windows/awt_Win32GraphicsDevice.h
@@ -32,7 +32,6 @@ extern "C" {
} // extern "C"
#include "colordata.h"
#include "awt_Palette.h"
-#include "awt_MMStub.h"
#include "Devices.h"
class AwtPalette;
@@ -40,7 +39,7 @@ class Devices;
class AwtWin32GraphicsDevice {
public:
- AwtWin32GraphicsDevice(int screen, MHND mhnd, Devices *arr);
+ AwtWin32GraphicsDevice(int screen, HMONITOR mhnd, Devices *arr);
~AwtWin32GraphicsDevice();
void UpdateDeviceColorState();
void SetGrayness(int grayValue);
@@ -60,8 +59,8 @@ public:
HPALETTE GetPalette();
ColorData *GetColorData() { return cData; }
int GetBitDepth() { return colorData->bitsperpixel; }
- MHND GetMonitor() { return monitor; }
- MONITOR_INFO *GetMonitorInfo() { return pMonitorInfo; }
+ HMONITOR GetMonitor() { return monitor; }
+ LPMONITORINFO GetMonitorInfo() { return pMonitorInfo; }
jobject GetJavaDevice() { return javaDevice; }
int GetDeviceIndex() { return screen; }
void Release();
@@ -78,14 +77,14 @@ public:
static void UpdateDynamicColorModel(int deviceIndex);
static BOOL UpdateSystemPalette(int deviceIndex);
static HPALETTE GetPalette(int deviceIndex);
- static MHND GetMonitor(int deviceIndex);
- static MONITOR_INFO *GetMonitorInfo(int deviceIndex);
+ static HMONITOR GetMonitor(int deviceIndex);
+ static LPMONITORINFO GetMonitorInfo(int deviceIndex);
static void ResetAllMonitorInfo();
static BOOL IsPrimaryPalettized() { return primaryPalettized; }
static int GetDefaultDeviceIndex() { return primaryIndex; }
- static void DisableOffscreenAccelerationForDevice(MHND hMonitor);
+ static void DisableOffscreenAccelerationForDevice(HMONITOR hMonitor);
static HDC GetDCFromScreen(int screen);
- static int GetScreenFromMHND(MHND mon);
+ static int GetScreenFromHMONITOR(HMONITOR mon);
static int primaryIndex;
static BOOL primaryPalettized;
@@ -97,17 +96,19 @@ public:
static jmethodID paletteChangedMID;
private:
- static BOOL AreSameMonitors(MHND mon1, MHND mon2);
+ static BOOL AreSameMonitors(HMONITOR mon1, HMONITOR mon2);
ImgColorData *colorData;
AwtPalette *palette;
ColorData *cData; // Could be static, but may sometime
// have per-device info in this structure
BITMAPINFO *gpBitmapInfo;
int screen;
- MHND monitor;
- MONITOR_INFO *pMonitorInfo;
+ HMONITOR monitor;
+ LPMONITORINFO pMonitorInfo;
jobject javaDevice;
Devices *devicesArray;
+
+ static HDC MakeDCFromMonitor(HMONITOR);
};
#endif AWT_WIN32GRAPHICSDEVICE_H
diff --git a/src/windows/native/sun/windows/awt_Win32GraphicsEnv.cpp b/src/windows/native/sun/windows/awt_Win32GraphicsEnv.cpp
index 398e17d27..1617d54e6 100644
--- a/src/windows/native/sun/windows/awt_Win32GraphicsEnv.cpp
+++ b/src/windows/native/sun/windows/awt_Win32GraphicsEnv.cpp
@@ -23,8 +23,6 @@
* have any questions.
*/
-#include <windows.h>
-#include <jni.h>
#include <awt.h>
#include <sun_awt_Win32GraphicsEnvironment.h>
#include "awt_Canvas.h"
@@ -188,44 +186,6 @@ Java_sun_awt_Win32GraphicsEnvironment_getDefaultScreen(JNIEnv *env,
return AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
}
-#define FR_PRIVATE 0x10 /* from wingdi.h */
-typedef int (WINAPI *AddFontResourceExType)(LPCTSTR,DWORD,VOID*);
-typedef int (WINAPI *RemoveFontResourceExType)(LPCTSTR,DWORD,VOID*);
-
-static AddFontResourceExType procAddFontResourceEx = NULL;
-static RemoveFontResourceExType procRemoveFontResourceEx = NULL;
-
-static int winVer = -1;
-
-static int getWinVer() {
- if (winVer == -1) {
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- GetVersionEx(&osvi);
- winVer = osvi.dwMajorVersion;
- if (winVer >= 5) {
- // REMIND verify on 64 bit windows
- HMODULE hGDI = LoadLibrary(TEXT("gdi32.dll"));
- if (hGDI != NULL) {
- procAddFontResourceEx =
- (AddFontResourceExType)GetProcAddress(hGDI,"AddFontResourceExW");
- if (procAddFontResourceEx == NULL) {
- winVer = 0;
- }
- procRemoveFontResourceEx =
- (RemoveFontResourceExType)GetProcAddress(hGDI,
- "RemoveFontResourceExW");
- if (procRemoveFontResourceEx == NULL) {
- winVer = 0;
- }
- FreeLibrary(hGDI);
- }
- }
- }
-
- return winVer;
-}
-
/*
* Class: sun_awt_Win32GraphicsEnvironment
* Method: registerFontWithPlatform
@@ -236,9 +196,10 @@ Java_sun_awt_Win32GraphicsEnvironment_registerFontWithPlatform(JNIEnv *env,
jclass cl,
jstring fontName)
{
- if (getWinVer() >= 5 && procAddFontResourceEx != NULL) {
- LPTSTR file = (LPTSTR)JNU_GetStringPlatformChars(env, fontName, NULL);
- (*procAddFontResourceEx)(file, FR_PRIVATE, NULL);
+ LPTSTR file = (LPTSTR)JNU_GetStringPlatformChars(env, fontName, JNI_FALSE);
+ if (file) {
+ ::AddFontResourceEx(file, FR_PRIVATE, NULL);
+ JNU_ReleaseStringPlatformChars(env, fontName, file);
}
}
@@ -255,9 +216,10 @@ Java_sun_awt_Win32GraphicsEnvironment_deRegisterFontWithPlatform(JNIEnv *env,
jclass cl,
jstring fontName)
{
- if (getWinVer() >= 5 && procRemoveFontResourceEx != NULL) {
- LPTSTR file = (LPTSTR)JNU_GetStringPlatformChars(env, fontName, NULL);
- (*procRemoveFontResourceEx)(file, FR_PRIVATE, NULL);
+ LPTSTR file = (LPTSTR)JNU_GetStringPlatformChars(env, fontName, JNI_FALSE);
+ if (file) {
+ ::RemoveFontResourceEx(file, FR_PRIVATE, NULL);
+ JNU_ReleaseStringPlatformChars(env, fontName, file);
}
}
diff --git a/src/windows/native/sun/windows/awt_Window.cpp b/src/windows/native/sun/windows/awt_Window.cpp
index 13b418632..0c9bce121 100644
--- a/src/windows/native/sun/windows/awt_Window.cpp
+++ b/src/windows/native/sun/windows/awt_Window.cpp
@@ -23,7 +23,7 @@
* have any questions.
*/
-#include <windowsx.h>
+#include "awt.h"
#include "awt_Component.h"
#include "awt_Container.h"
@@ -32,7 +32,6 @@
#include "awt_Panel.h"
#include "awt_Toolkit.h"
#include "awt_Window.h"
-#include "awt_dlls.h"
#include "awt_Win32GraphicsDevice.h"
#include "awt_BitmapUtil.h"
#include "awt_IconCursor.h"
@@ -42,6 +41,8 @@
#include <java_awt_event_ComponentEvent.h>
#include "sun_awt_windows_WCanvasPeer.h"
+#include <windowsx.h>
+
#if !defined(__int3264)
typedef __int32 LONG_PTR;
#endif // __int3264
@@ -501,8 +502,7 @@ void AwtWindow::Show()
// which might involve tagging java.awt.Window instances with a semantic
// property so platforms can animate/decorate/etc accordingly.
//
- if ((IS_WIN98 || IS_WIN2000) &&
- JNU_IsInstanceOfByName(env, target, "com/sun/java/swing/plaf/windows/WindowsPopupWindow") > 0)
+ if (JNU_IsInstanceOfByName(env, target, "com/sun/java/swing/plaf/windows/WindowsPopupWindow") > 0)
{
// need this global ref to make the class unloadable (see 6500204)
static jclass windowsPopupWindowCls;
@@ -535,13 +535,8 @@ void AwtWindow::Show()
windowType = env->GetIntField(target, windowTypeFID);
if (windowType == windowTYPES[TOOLTIP]) {
- if (IS_WIN2000) {
- SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, &animateflag, 0);
- SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, &fadeflag, 0);
- } else {
- // use same setting as menus
- SystemParametersInfo(SPI_GETMENUANIMATION, 0, &animateflag, 0);
- }
+ SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, &animateflag, 0);
+ SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, &fadeflag, 0);
if (animateflag) {
// AW_BLEND currently produces runtime parameter error
// animateStyle = fadeflag? AW_BLEND : AW_SLIDE | AW_VER_POSITIVE;
@@ -551,13 +546,10 @@ void AwtWindow::Show()
windowType == windowTYPES[POPUPMENU]) {
SystemParametersInfo(SPI_GETMENUANIMATION, 0, &animateflag, 0);
if (animateflag) {
-
- if (IS_WIN2000) {
- SystemParametersInfo(SPI_GETMENUFADE, 0, &fadeflag, 0);
- if (fadeflag) {
- // AW_BLEND currently produces runtime parameter error
- //animateStyle = AW_BLEND;
- }
+ SystemParametersInfo(SPI_GETMENUFADE, 0, &fadeflag, 0);
+ if (fadeflag) {
+ // AW_BLEND currently produces runtime parameter error
+ //animateStyle = AW_BLEND;
}
if (animateStyle == 0 && !fadeflag) {
animateStyle = AW_SLIDE;
@@ -578,38 +570,18 @@ void AwtWindow::Show()
}
if (animateStyle != 0) {
- load_user_procs();
-
- if (fn_animate_window != NULL) {
- BOOL result = (*fn_animate_window)(hWnd, (DWORD)200, animateStyle);
- if (result == 0) {
- LPTSTR msgBuffer = NULL;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&msgBuffer, // it's an output parameter when allocate buffer is used
- 0,
- NULL);
-
- if (msgBuffer == NULL) {
- msgBuffer = TEXT("<Could not get GetLastError() message text>");
- }
- _ftprintf(stderr,TEXT("AwtWindow::Show: AnimateWindow: "));
- _ftprintf(stderr,msgBuffer);
- LocalFree(msgBuffer);
- } else {
- // WM_PAINT is not automatically sent when invoking AnimateWindow,
- // so force an expose event
- RECT rect;
- ::GetWindowRect(hWnd,&rect);
- ::ScreenToClient(hWnd, (LPPOINT)&rect);
- ::InvalidateRect(hWnd,&rect,TRUE);
- ::UpdateWindow(hWnd);
- done = TRUE;
- }
+ BOOL result = ::AnimateWindow(hWnd, (DWORD)200, animateStyle);
+ if (!result) {
+ // TODO: log message
+ } else {
+ // WM_PAINT is not automatically sent when invoking AnimateWindow,
+ // so force an expose event
+ RECT rect;
+ ::GetWindowRect(hWnd,&rect);
+ ::ScreenToClient(hWnd, (LPPOINT)&rect);
+ ::InvalidateRect(hWnd, &rect, TRUE);
+ ::UpdateWindow(hWnd);
+ done = TRUE;
}
}
}
@@ -1205,16 +1177,17 @@ MsgRouting AwtWindow::WmNcPaint(HRGN hrgn)
}
/* draw warning text */
- LPWSTR text = TO_WSTRING(warningString);
+ LPCWSTR text = JNU_GetStringPlatformChars(env, warningString, NULL);
VERIFY(::SetBkColor(hDC, ::GetSysColor(COLOR_BTNFACE)) != CLR_INVALID);
VERIFY(::SetTextColor(hDC, ::GetSysColor(COLOR_BTNTEXT)) != CLR_INVALID);
VERIFY(::SelectObject(hDC, ::GetStockObject(DEFAULT_GUI_FONT)) != NULL);
VERIFY(::SetTextAlign(hDC, TA_LEFT | TA_BOTTOM) != GDI_ERROR);
- VERIFY(::ExtTextOutW(hDC, r.left+2, r.bottom-1,
+ VERIFY(::ExtTextOut(hDC, r.left+2, r.bottom-1,
ETO_CLIPPED | ETO_OPAQUE,
&r, text, static_cast<UINT>(wcslen(text)), NULL));
VERIFY(::RestoreDC(hDC, iSaveDC));
::ReleaseDC(GetHWnd(), hDC);
+ JNU_ReleaseStringPlatformChars(env, warningString, text);
}
env->DeleteLocalRef(target);
@@ -1360,13 +1333,13 @@ void AwtWindow::RedrawNonClient()
}
int AwtWindow::GetScreenImOn() {
- MHND hmon;
+ HMONITOR hmon;
int scrnNum;
- hmon = ::MonitorFromWindow(GetHWnd(), MONITOR_DEFAULT_TO_PRIMARY);
+ hmon = ::MonitorFromWindow(GetHWnd(), MONITOR_DEFAULTTOPRIMARY);
DASSERT(hmon != NULL);
- scrnNum = AwtWin32GraphicsDevice::GetScreenFromMHND(hmon);
+ scrnNum = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hmon);
DASSERT(scrnNum > -1);
return scrnNum;
@@ -1619,10 +1592,10 @@ void AwtWindow::_SetTitle(void *param)
if (::IsWindow(w->GetHWnd()))
{
int length = env->GetStringLength(title);
- WCHAR *buffer = new WCHAR[length + 1];
- env->GetStringRegion(title, 0, length, buffer);
+ TCHAR *buffer = new TCHAR[length + 1];
+ env->GetStringRegion(title, 0, length, reinterpret_cast<jchar*>(buffer));
buffer[length] = L'\0';
- VERIFY(::SetWindowTextW(w->GetHWnd(), buffer));
+ VERIFY(::SetWindowText(w->GetHWnd(), buffer));
delete[] buffer;
}
ret:
@@ -1967,13 +1940,11 @@ void AwtWindow::_SetFocusableWindow(void *param)
window->m_isFocusableWindow = isFocusableWindow;
- if (IS_WIN2000) {
- if (!window->m_isFocusableWindow) {
- LONG isPopup = window->GetStyle() & WS_POPUP;
- window->SetStyleEx(window->GetStyleEx() | (isPopup ? 0 : WS_EX_APPWINDOW) | AWT_WS_EX_NOACTIVATE);
- } else {
- window->SetStyleEx(window->GetStyleEx() & ~WS_EX_APPWINDOW & ~AWT_WS_EX_NOACTIVATE);
- }
+ if (!window->m_isFocusableWindow) {
+ LONG isPopup = window->GetStyle() & WS_POPUP;
+ window->SetStyleEx(window->GetStyleEx() | (isPopup ? 0 : WS_EX_APPWINDOW) | AWT_WS_EX_NOACTIVATE);
+ } else {
+ window->SetStyleEx(window->GetStyleEx() & ~WS_EX_APPWINDOW & ~AWT_WS_EX_NOACTIVATE);
}
ret:
diff --git a/src/windows/native/sun/windows/awt_dlls.cpp b/src/windows/native/sun/windows/awt_dlls.cpp
deleted file mode 100644
index 26af4196d..000000000
--- a/src/windows/native/sun/windows/awt_dlls.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright 1999-2003 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-#include "awt.h"
-#include "awt_dlls.h"
-
-/*
- * To reduce memory footprint we don't statically link to COMDLG32.DLL
- * and SHELL32. Instead we programatically load them only when they are
- * needed.
- */
-
-PrintDlgType AwtCommDialog::do_print_dlg;
-PageSetupDlgType AwtCommDialog::do_page_setup_dlg;
-GetOpenFileNameType AwtCommDialog::get_open_file_name;
-GetSaveFileNameType AwtCommDialog::get_save_file_name;
-GetExtendedErrorType AwtCommDialog::get_dlg_extended_error;
-
-/***********************************************************************/
-
-DWORD
-AwtCommDialog::CommDlgExtendedError(VOID) {
- AwtCommDialog::load_comdlg_procs();
- return static_cast<DWORD>(reinterpret_cast<INT_PTR>(
- AwtToolkit::GetInstance().
- InvokeFunction(reinterpret_cast<void *(*)(void)>
- (AwtCommDialog::GetExtendedErrorWrapper))));
-}
-
-BOOL
-AwtCommDialog::PrintDlg(LPPRINTDLG data) {
- AwtCommDialog::load_comdlg_procs();
- return static_cast<BOOL>(reinterpret_cast<INT_PTR>(
- AwtToolkit::GetInstance().
- InvokeFunction(reinterpret_cast<void *(*)(void *)>
- (AwtCommDialog::PrintDlgWrapper), data)));
-}
-
-BOOL
-AwtCommDialog::PageSetupDlg(LPPAGESETUPDLG data) {
- AwtCommDialog::load_comdlg_procs();
- return static_cast<BOOL>(reinterpret_cast<INT_PTR>(
- AwtToolkit::GetInstance().
- InvokeFunction(reinterpret_cast<void *(*)(void *)>
- (AwtCommDialog::PageSetupDlgWrapper), data)));
-}
-
-/*
- * Load the COMDLG32.dll and get pointers to various procedures.
- */
-
-void
-AwtCommDialog::load_comdlg_procs()
-{
- static int initialized = 0;
- HMODULE lib = NULL;
- if (initialized) {
- return;
- }
- lib = LoadLibrary(TEXT("COMDLG32.DLL"));
- HMODULE libUnicows = UnicowsLoader::GetModuleHandle();
- do_print_dlg = (PrintDlgType)GetProcAddress(IS_WIN95 ? libUnicows : lib, "PrintDlgW");
- do_page_setup_dlg = (PageSetupDlgType)GetProcAddress(IS_WIN95 ? libUnicows : lib, "PageSetupDlgW");
- get_open_file_name = (GetOpenFileNameType)GetProcAddress(IS_WIN95 ? libUnicows : lib, "GetOpenFileNameW");
- get_save_file_name = (GetSaveFileNameType)GetProcAddress(IS_WIN95 ? libUnicows : lib, "GetSaveFileNameW");
- get_dlg_extended_error = (GetExtendedErrorType)GetProcAddress(lib, "CommDlgExtendedError");
- initialized = 1;
-}
-
-/***********************************************************************/
-
-DragQueryFileType do_drag_query_file;
-GetPathFromIDListType get_path_from_idlist;
-
-/*
- * Load the SHELL32.dll and get pointers to various procedures.
- */
-
-void
-load_shell_procs()
-{
- static int initialized = 0;
- HMODULE lib = NULL;
- if (initialized) {
- return;
- }
-
- if (IS_WIN95) {
- lib = UnicowsLoader::GetModuleHandle();
- } else {
- lib = LoadLibrary(TEXT("SHELL32.DLL"));
- }
-
- do_drag_query_file = (DragQueryFileType)GetProcAddress(lib, "DragQueryFileW");
- get_path_from_idlist = (GetPathFromIDListType)GetProcAddress(lib,
- "SHGetPathFromIDListW");
- initialized = 1;
-}
-
-/***********************************************************************/
-
-AnimateWindowType fn_animate_window;
-ChangeDisplaySettingsExType fn_change_display_settings_ex;
-
-/*
- * Load the USER32.dll and get pointers to various procedures.
- */
-
-void
-load_user_procs()
-{
- static int initialized = 0;
- HMODULE lib = NULL;
- if (initialized) {
- return;
- }
- lib = LoadLibrary(TEXT("USER32.DLL"));
- HMODULE libUnicows = UnicowsLoader::GetModuleHandle();
- fn_animate_window = (AnimateWindowType)GetProcAddress(lib, "AnimateWindow");
- fn_change_display_settings_ex = (ChangeDisplaySettingsExType)
- GetProcAddress(IS_WIN95 ? libUnicows : lib, "ChangeDisplaySettingsExW");
- initialized = 1;
-}
-
-/***********************************************************************/
-
-GetFileVersionInfoSizeType get_file_version_info_size;
-GetFileVersionInfoType get_file_version_info;
-VerQueryValueType do_ver_query_value;
-
-/*
- * Load the VERSION.dll and get pointers to various procedures.
- */
-
-void
-load_version_procs()
-{
- static int initialized = 0;
- HMODULE lib = NULL;
- if (initialized) {
- return;
- }
-
- if (IS_WIN95) {
- lib = UnicowsLoader::GetModuleHandle();
- } else {
- lib = LoadLibrary(TEXT("VERSION.DLL"));
- }
-
- get_file_version_info_size =
- (GetFileVersionInfoSizeType)GetProcAddress(lib, "GetFileVersionInfoSizeW");
- get_file_version_info =
- (GetFileVersionInfoType)GetProcAddress(lib, "GetFileVersionInfoW");
- do_ver_query_value =
- (VerQueryValueType)GetProcAddress(lib, "VerQueryValueW");
- initialized = 1;
-}
-
-/***********************************************************************/
-
-#define MAX_KNOWN_VERSION 4
-
-/*
- * We are going to use an undocumented procedure RSRC32.DLL.
- * The safest will be to use it only for a finite set of known versions.
- */
-
-DWORD known_versions[MAX_KNOWN_VERSION][2] = {
- { 0x00040000, 0x000003B6 }, // WIN95\RETAIL
- // WIN95\RETAIL\UPGRADE
- // WIN95\OSR2
- // WIN95\OSR25
- // WIN95\international\RETAIL
- // WIN95\international\OSR2
-
- { 0x0004000A, 0x00000672 }, // WIN98\international\win98beta3
-
- { 0x0004000A, 0x000007CE }, // WIN98\RETAIL
- // WIN98\international\RETAIL
- // WIN98\SE
-
- { 0x0004005A, 0x00000BB8 } // WIN98ME
-};
-
-GetFreeSystemResourcesType get_free_system_resources = NULL;
-
-/*
- * Load the RSRC32.dll, check that it is a known version
- * and get the pointer to the undocumented procedure.
- */
-
-void
-load_rsrc32_procs()
-{
- static int initialized = 0;
- if (initialized) {
- return;
- }
- if (IS_NT) {
- // 4310028: Only load library on non-NT systems. The load
- // will always fail anyways. However, if a Win 9x OS is
- // also installed on the system, and the user's path
- // includes C:\WINDOWS\SYSTEM, or the equivalent, a really
- // ugly and annoying warning dialog will appear.
- initialized = 1;
- return;
- }
- HMODULE lib = LoadLibrary(TEXT("RSRC32.DLL"));
- if (lib != NULL) {
- TCHAR szFullPath[_MAX_PATH];
- DWORD dwVerHnd = 0;
- DWORD dwVersionInfoSize;
- LPBYTE lpVersionInfo;
- LPVOID lpBuffer;
- UINT uLength = 0;
-
- /*
- * We use undocumented procedure exported by RSRC32.DLL, so the
- * safest will be to check the library's version and only attempt
- * to get the procedure address if it's a known version.
- */
- if (::GetModuleFileName(lib, szFullPath, sizeof(szFullPath))) {
- load_version_procs();
- dwVersionInfoSize = (*get_file_version_info_size)(szFullPath, &dwVerHnd);
- if (dwVersionInfoSize) {
- lpVersionInfo = new BYTE[dwVersionInfoSize];
- (*get_file_version_info)(szFullPath, dwVerHnd,
- dwVersionInfoSize, lpVersionInfo);
- if ((*do_ver_query_value)(lpVersionInfo, TEXT("\\"), &lpBuffer, &uLength)) {
- VS_FIXEDFILEINFO *lpvsFixedFileInfo = (VS_FIXEDFILEINFO *)lpBuffer;
- DWORD dwFileVersionMS = lpvsFixedFileInfo->dwFileVersionMS;
- DWORD dwFileVersionLS = lpvsFixedFileInfo->dwFileVersionLS;
- for (int i = 0; i < MAX_KNOWN_VERSION; i++) {
- if ((known_versions[i][0] == dwFileVersionMS) &&
- (known_versions[i][1] == dwFileVersionLS)) {
- get_free_system_resources =
- (GetFreeSystemResourcesType)
- ::GetProcAddress(lib, "_MyGetFreeSystemResources32@4");
- break;
- }
- }
- }
- delete[] lpVersionInfo;
- }
- }
- }
- initialized = 1;
-}
-
-void
-load_rich_edit_library() {
- static int initialized = 0;
- BOOL isRichEdit32Needed = IS_WIN95 && !IS_WIN98;
-
- if (initialized) {
- return;
- }
-
- HMODULE lib = NULL;
- if (isRichEdit32Needed) {
- lib = ::LoadLibrary(TEXT("RICHED32.DLL"));
- } else {
- lib = ::LoadLibrary(TEXT("RICHED20.DLL"));
- }
- if (lib == NULL) {
- JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
- JNU_ThrowInternalError(env, "Can't load a rich edit DLL");
- } else if (isRichEdit32Needed) {
- // Richedit language checking logic is needed for RICHED32.DLL only.
- LPTSTR szFullPath = new TCHAR[_MAX_PATH];
- DWORD dwVerHnd = 0;
- DWORD dwVersionInfoSize;
- LPVOID lpVersionInfo;
- UINT uLength = 0;
- struct LANGANDCODEPAGE {
- WORD wLanguage;
- WORD wCodePage;
- } *lpTranslate;
-
- try {
- if (!IS_WIN2000 && ::GetModuleFileName(lib, szFullPath, _MAX_PATH)) {
- load_version_procs();
- dwVersionInfoSize = (*get_file_version_info_size)(szFullPath, &dwVerHnd);
- if (dwVersionInfoSize) {
- lpVersionInfo = new BYTE[dwVersionInfoSize];
- try {
- if ((*get_file_version_info)(szFullPath,
- dwVerHnd,
- dwVersionInfoSize,
- lpVersionInfo)
- && (*do_ver_query_value)(lpVersionInfo,
- TEXT("\\VarFileInfo\\Translation"),
- (LPVOID*)&lpTranslate,
- &uLength)) {
-
- if (::GetSystemMetrics(SM_DBCSENABLED)
- && LANGIDFROMLCID(::GetThreadLocale()) != lpTranslate[0].wLanguage) {
-
- JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
- if (env->PushLocalFrame(6) >= 0) {
- jstring keystr = env->NewStringUTF("AWT.InconsistentDLLsWarning");
- jstring defstr = env->NewStringUTF(
-"Text based operations may not work correctly due to \
-an inconsistent set of dynamic linking libraries (DLLs) installed on your \
-system. For more information on this problem and a suggested workaround \
-please see the Java(TM) 2 SDK, Standard Edition Release Notes \
-on java.sun.com.");
-
- jstring retstr =
- (jstring) JNU_CallStaticMethodByName(
- env,
- NULL,
- "java/awt/Toolkit",
- "getProperty",
- "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
- keystr,
- defstr).l;
-
- jboolean exception;
- jstring pkgstr = env->NewStringUTF("java.awt");
- jobject logger =
- (jobject) JNU_CallStaticMethodByName(
- env,
- &exception,
- "java/util/logging/Logger",
- "getLogger",
- "(Ljava/lang/String;)Ljava/util/logging/Logger;",
- pkgstr).l;
-
- jstring msgstr = (retstr) ? retstr : defstr;
- if (!exception) {
- JNU_CallMethodByName(
- env,
- NULL,
- logger,
- "warning",
- "(Ljava/lang/String;)V",
- msgstr);
- } else {
- LPCTSTR outstr = JNU_GetStringPlatformChars(env, msgstr, NULL);
- _ftprintf(stdout, TEXT("\nWARNING: %s\n"), outstr);
- fflush(stdout);
- JNU_ReleaseStringPlatformChars(env, msgstr, outstr);
- }
-
- env->PopLocalFrame(NULL);
- }
- }
- }
- } catch (...) {
- delete[] lpVersionInfo;
- throw;
- }
- delete[] lpVersionInfo;
- }
- }
- } catch (...) {
- delete[] szFullPath;
- throw;
- }
- delete[] szFullPath;
- }
- initialized = 1;
-}
-
-/***********************************************************************/
-
-bool AwtWinMM::initialized = false;
-AwtWinMM::PlaySoundWFunc* AwtWinMM::playSoundFunc = NULL;
-
-BOOL AwtWinMM::PlaySoundWrapper(LPCTSTR pszSound, HMODULE hmod, DWORD fdwSound) {
- load_winmm_procs();
- if (playSoundFunc == NULL) {
- return FALSE;
- }
- return (*playSoundFunc)(pszSound, hmod, fdwSound);
-}
-
-void AwtWinMM::load_winmm_procs() {
- if (initialized) {
- return;
- }
- HMODULE dll = NULL;
-
- if (IS_WIN95) {
- dll = UnicowsLoader::GetModuleHandle();
- } else {
- dll = ::LoadLibrary(TEXT("winmm.dll"));
- }
-
- if (dll == NULL) {
- return;
- }
- playSoundFunc =
- (PlaySoundWFunc*) GetProcAddress(dll, "PlaySoundW");
- if (playSoundFunc == NULL) {
- return;
- }
- initialized = true;
-}
diff --git a/src/windows/native/sun/windows/awt_dlls.h b/src/windows/native/sun/windows/awt_dlls.h
deleted file mode 100644
index b7e089802..000000000
--- a/src/windows/native/sun/windows/awt_dlls.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 1999-2003 Sun Microsystems, Inc. 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. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-#ifndef AWT_DLLS_H
-#define AWT_DLLS_H
-
-#include <commdlg.h>
-#include <shellapi.h>
-#include <shlobj.h>
-#include "awt_FileDialog.h"
-#include "awt_PrintDialog.h"
-
-/*
- * To reduce memory footprint we don't statically link to COMDLG32.DLL
- * and SHELL32. Instead we programatically load them only when they are
- * needed.
- */
-
-//---------------------------------------------------------------------------
-
-typedef BOOL (APIENTRY *PrintDlgType)(LPPRINTDLGW);
-typedef BOOL (APIENTRY *PageSetupDlgType)(LPPAGESETUPDLGW);
-typedef BOOL (APIENTRY *GetOpenFileNameType)(LPOPENFILENAMEW);
-typedef BOOL (APIENTRY *GetSaveFileNameType)(LPOPENFILENAMEW);
-typedef DWORD (APIENTRY *GetExtendedErrorType)(VOID);
-
-class AwtCommDialog {
-public:
- static DWORD CommDlgExtendedError(VOID);
-
- static BOOL PrintDlg(LPPRINTDLG data);
-
- static BOOL PageSetupDlg(LPPAGESETUPDLG data);
-
-private:
- static void load_comdlg_procs();
-
- // Use wrapper functions with default calling convention. If the
- // default isn't __stdcall, accessing the Win32 functions directly
- // will cause stack corruption if we cast away __stdcall.
- static BOOL PrintDlgWrapper(LPPRINTDLG data) {
- return (*do_print_dlg)(data);
- }
- static BOOL PageSetupDlgWrapper(LPPAGESETUPDLG data) {
- return (*do_page_setup_dlg)(data);
- }
- static BOOL GetOpenFileNameWrapper(LPOPENFILENAME data) {
- return (*get_open_file_name)(data);
- }
- static BOOL GetSaveFileNameWrapper(LPOPENFILENAME data) {
- return (*get_save_file_name)(data);
- }
- static DWORD GetExtendedErrorWrapper(VOID) {
- return (*get_dlg_extended_error)();
- }
-
- friend BOOL AwtFileDialog::GetOpenFileName(LPAWTOPENFILENAME);
- friend BOOL AwtFileDialog::GetSaveFileName(LPAWTOPENFILENAME);
- friend BOOL AwtPrintDialog::PrintDlg(LPPRINTDLG);
-
- static PrintDlgType do_print_dlg;
- static PageSetupDlgType do_page_setup_dlg;
- static GetOpenFileNameType get_open_file_name;
- static GetSaveFileNameType get_save_file_name;
- static GetExtendedErrorType get_dlg_extended_error;
-};
-
-//---------------------------------------------------------------------------
-
-// Dynamically load in SHELL32.DLL and define the procedure pointers listed below.
-extern void load_shell_procs();
-
-// Procedure pointers obtained from SHELL32.DLL
-// You must call load_shell_procs() before using any of these.
-typedef UINT (APIENTRY *DragQueryFileType)(HDROP,UINT,LPTSTR,UINT);
-typedef BOOL (APIENTRY *GetPathFromIDListType)(LPCITEMIDLIST,LPTSTR);
-extern DragQueryFileType do_drag_query_file;
-extern GetPathFromIDListType get_path_from_idlist;
-
-//---------------------------------------------------------------------------
-
-// Dynamically load in USER32.DLL and define the procedure pointers listed below.
-extern void load_user_procs();
-
-// Procedure pointers obtained from USER32.DLL
-// You must call load_user_procs() before using any of these.
-typedef BOOL (WINAPI *AnimateWindowType)(HWND,DWORD,DWORD);
-typedef LONG (WINAPI *ChangeDisplaySettingsExType)(LPCTSTR,LPDEVMODE,HWND,DWORD,LPVOID lParam);
-extern AnimateWindowType fn_animate_window;
-extern ChangeDisplaySettingsExType fn_change_display_settings_ex;
-
-//---------------------------------------------------------------------------
-
-// Dynamically load in VERSION.DLL and define the procedure pointers listed below.
-extern void load_version_procs();
-
-// Procedure pointers obtained from VERSION.DLL
-// You must call load_version_procs() before using any of these.
-typedef DWORD (APIENTRY *GetFileVersionInfoSizeType)(LPTSTR,LPDWORD);
-typedef BOOL (APIENTRY *GetFileVersionInfoType)(LPTSTR,DWORD,DWORD,LPVOID);
-typedef BOOL (APIENTRY *VerQueryValueType)(const LPVOID,LPTSTR,LPVOID*,PUINT);
-extern GetFileVersionInfoSizeType get_file_version_info_size;
-extern GetFileVersionInfoType get_file_version_info;
-extern VerQueryValueType do_ver_query_value;
-
-//---------------------------------------------------------------------------
-
-// Dynamically load in RSRC32.DLL and define the procedure pointers listed below.
-extern void load_rsrc32_procs();
-
-// Procedure pointers obtained from RSRC32.DLL
-// You must call load_rsrc32_procs() before using this procedure.
-
-/*
- * NOTE: even after load_rsrc32_procs() you must check that
- * the function pointer is valid before use.
- * It will be NULL in three cases:
- * 1.RSRC32.DLL not found. This means that Resource Meter
- * isn't installed.
- * 2.RSRC32.DLL can't be loaded. This happens on WinNT.
- * 3.Unknown version of RSRC32.DLL. This is undocumented
- * procedure, so the safest will be to use it only for
- * a finite set of known versions.
- */
-typedef UINT (APIENTRY *GetFreeSystemResourcesType)(UINT);
-
-extern GetFreeSystemResourcesType get_free_system_resources;
-
-extern void load_rich_edit_library();
-
-//---------------------------------------------------------------------------
-
-/*
- * Loading WINMM.DLL (the Windows MultiMedia library) is extremely
- * expensive. The AWT only uses it to play certain Windows sounds
- * (which are off by default) so we dynamically load it upon demand
- * instead of statically linking to it.
- */
-
-class AwtWinMM {
-public:
- static BOOL PlaySoundWrapper(LPCTSTR pszSound, HMODULE hmod, DWORD fdwSound);
-
-private:
- static void load_winmm_procs();
- static bool initialized;
- typedef BOOL WINAPI PlaySoundWFunc(LPCTSTR pszSound, HMODULE hmod, DWORD fdwSound);
- static PlaySoundWFunc* playSoundFunc;
-};
-
-#endif /* AWT_DLLS_H */
diff --git a/src/windows/native/sun/windows/awtmsg.h b/src/windows/native/sun/windows/awtmsg.h
index fe03b0c46..35e436b53 100644
--- a/src/windows/native/sun/windows/awtmsg.h
+++ b/src/windows/native/sun/windows/awtmsg.h
@@ -182,18 +182,6 @@ extern const UINT SYSCOMMAND_IMM;
#endif //AW_BLEND
-
-
-// WM_MOUSEWHEEL should be WM_MOUSELAST, but
-// is not being defined. See winuser.h
-#ifdef WM_MOUSELAST
-#if WM_MOUSELAST <= 0x020A
-#define WM_AWT_MOUSELAST 0x020A
-#else
-#error Unexpected value of WM_MOUSELAST
-#endif //WM_MOUSELAST <= 0x0209
-#endif //WM_MOUSELAST
-
// AwtComponent messages
enum {
// 6427323: unfortunately WM_APP+nnn conflicts with edit control messages
diff --git a/src/windows/native/sun/windows/jawt.cpp b/src/windows/native/sun/windows/jawt.cpp
index 91de38a9f..ae2d09dd4 100644
--- a/src/windows/native/sun/windows/jawt.cpp
+++ b/src/windows/native/sun/windows/jawt.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. 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,6 +26,7 @@
#define _JNI_IMPLEMENTATION_
#include <jawt.h>
+#include "awt.h"
#include "awt_DrawingSurface.h"
/*