CLOVER🍀

That was when it all began.

Javadocの@sinceタグから、特定のJDKで追加されたクラスを検索する

先のエントリで、ConcurrentLinkedDequeクラスを見つけた時にふと思ったのですが、新しいバージョンのJDKで追加されたクラスって、みなさんどうやって探しているんでしょうね?

だいたい新しいJDKのリリース前に新機能に関する記事が出てきて、それを使って掘り下げていくうちに、また別のクラスを知る、みたいな感じだと思います。

そして、その時ってJavadocの@sinceタグを見ていることが多いんじゃないかなーと思うわけです。なのであれば、Javadocを使えば、例えば「JDK 7で追加されたクラスを探す」なんてことができるのではないかと思った次第です。

で、実際できそうですね。
http://docs.oracle.com/javase/jp/6/technotes/guides/javadoc/index.html
http://docs.oracle.com/javase/jp/6/technotes/guides/javadoc/doclet/overview.html
http://docs.oracle.com/javase/jp/6/jdk/api/javadoc/doclet/index.html

ドックレットを使えばいいみたいです。ドックレットは、本来Javadocの出力を好みの形式にカスタマイズすることに使うんだとか。

以前、XDocletとか名前だけ聞いたことはありましたけど、この辺りのテクノロジの話だったんだなと、今更ながらに思いました。

では、目的のドックレットを書いてみます。
SinceFindDoclet.java

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Set;
import java.util.TreeSet;

import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;

public class SinceFindDoclet {
    public static boolean start(RootDoc root) {
        Set<String> classes = new TreeSet<>();

        for (ClassDoc classDoc : root.classes()) {
            for (Tag tag : classDoc.tags("since")) {
                if ("1.7".equals(tag.text())) {
                    classes.add(classDoc.toString());
                }
            }
        }

        try {
            Files.write(Paths.get("classes_" + System.currentTimeMillis() + ".txt"),
                        classes,
                        StandardCharsets.UTF_8,
                        StandardOpenOption.CREATE,
                        StandardOpenOption.TRUNCATE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true; 
    }
}

ドックレットは、staticなstartメソッドで開始されるのだとか。

    public static boolean start(RootDoc root) {

今回は、渡ってきたRootDocからClassの一覧を取得し、クラスに付与された@sinceタグの値が「1.7」のものを抽出しています。

抽出結果は、タイムスタンプ付きで出力。

それでは、まずはコンパイル

$ javac -cp $JAVA_HOME/lib/tools.jar SinceFindDoclet.java

コンパイル時に、クラスパスにtools.jarを加える必要があります。

続いて、Javaのソースを対象にするので…とりあえず適当にディレクトリを作成して、そこにsrc.zipをコピーしてunzip。

$ mkdir src
$ cd src
$ unzip src.zip

まあ、こうなりますよ、と。

$ ll
合計 19420
drwxrwxr-x  8 xxxxx xxxxx     4096 Mar 30 02:30 ./
drwxrwxr-x  3 xxxxx xxxxx     4096 Mar 30 02:43 ../
drwxr-xr-x  3 xxxxx xxxxx     4096 Mar  1 20:20 com/
drwxr-xr-x 15 xxxxx xxxxx     4096 Mar  1 20:20 java/
drwxr-xr-x 17 xxxxx xxxxx     4096 Mar  1 20:20 javax/
drwxr-xr-x  2 xxxxx xxxxx     4096 Mar  1 20:20 launcher/
drwxr-xr-x  6 xxxxx xxxxx     4096 Mar  1 20:20 org/
-rw-r--r--  1 xxxxx xxxxx 19852819 Mar 30 00:26 src.zip
drwxr-xr-x  4 xxxxx xxxxx     4096 Mar  1 20:20 sunw/

ひとつ上のディレクトリから見ると、こんな感じになっています。

$ find ./ -maxdepth 2
./
./src
./src/org
./src/sunw
./src/src.zip
./src/java
./src/com
./src/launcher
./src/javax
./SinceFindDoclet.class
./SinceFindDoclet.java

で、再度srcに降りて

$ cd src

java、javax、orgで始まるパッケージくらいに絞ってドックレットを動かします。

$ find java javax org -name "*.java"

ドックレットを組み込んでjavadocコマンドを動かすには、以下の様に書きます。

$ javadoc -doclet SinceFindDoclet -docletpath ../ [ソースコード]

「-doclet」オプションではドックレットのクラス名を指定し、「-docletpath」ではドックレットに対してのクラスパスを設定します。また、追加のクラスパス設定が必要な場合は、「-classpath」を使用します。

で、これを組み合わせてこんなコマンドを作って実行。

$ find java javax org -name "*.java" | xargs javadoc -doclet SinceFindDoclet -docletpath ../

少々待つと、カレントディレクトリに実行結果ができます。結果は、最後に。

とりあえず、今回のコマンドでは191クラスが検出されました。とはいえ、「Javadocに@sinceタグが律儀に書かれていること」がこの調べ方の大前提なので、@sinceタグを書いてなかったりすると普通にスルーされます。

なので、完璧ではありませんし、そんなに他のコードに適用できる方法ではないかもしれませんね。

で、実行結果についてですが、できた結果を開いてみると

$ cat classes_*

こんな感じになりました。ほとんどNIO.2とSwing。って、java.lang.invokeとかがほとんど入ってないので、やっぱりご参考程度、ですね。

java.awt.GraphicsDevice.WindowTranslucency
java.awt.SecondaryLoop
java.awt.Window.Type
java.awt.font.NumericShaper.Range
java.beans.Transient
java.lang.AutoCloseable
java.lang.BootstrapMethodError
java.lang.Character.UnicodeScript
java.lang.ClassValue
java.lang.ProcessBuilder.Redirect
java.lang.ReflectiveOperationException
java.lang.invoke.WrongMethodTypeException
java.lang.management.BufferPoolMXBean
java.lang.management.PlatformLoggingMXBean
java.lang.management.PlatformManagedObject
java.net.ProtocolFamily
java.net.SocketOption
java.net.StandardProtocolFamily
java.net.StandardSocketOptions
java.nio.channels.AcceptPendingException
java.nio.channels.AlreadyBoundException
java.nio.channels.AsynchronousByteChannel
java.nio.channels.AsynchronousChannel
java.nio.channels.AsynchronousChannelGroup
java.nio.channels.AsynchronousFileChannel
java.nio.channels.AsynchronousServerSocketChannel
java.nio.channels.AsynchronousSocketChannel
java.nio.channels.CompletionHandler
java.nio.channels.IllegalChannelGroupException
java.nio.channels.InterruptedByTimeoutException
java.nio.channels.MembershipKey
java.nio.channels.MulticastChannel
java.nio.channels.NetworkChannel
java.nio.channels.ReadPendingException
java.nio.channels.SeekableByteChannel
java.nio.channels.ShutdownChannelGroupException
java.nio.channels.WritePendingException
java.nio.channels.spi.AsynchronousChannelProvider
java.nio.charset.StandardCharsets
java.nio.file.AccessDeniedException
java.nio.file.AccessMode
java.nio.file.AtomicMoveNotSupportedException
java.nio.file.ClosedDirectoryStreamException
java.nio.file.CopyOption
java.nio.file.DirectoryIteratorException
java.nio.file.DirectoryNotEmptyException
java.nio.file.DirectoryStream
java.nio.file.DirectoryStream.Filter
java.nio.file.FileAlreadyExistsException
java.nio.file.FileStore
java.nio.file.FileSystem
java.nio.file.FileSystemException
java.nio.file.FileSystemLoopException
java.nio.file.FileSystems
java.nio.file.FileVisitOption
java.nio.file.FileVisitResult
java.nio.file.FileVisitor
java.nio.file.Files
java.nio.file.LinkOption
java.nio.file.LinkPermission
java.nio.file.NoSuchFileException
java.nio.file.NotDirectoryException
java.nio.file.NotLinkException
java.nio.file.OpenOption
java.nio.file.Path
java.nio.file.PathMatcher
java.nio.file.Paths
java.nio.file.SecureDirectoryStream
java.nio.file.SimpleFileVisitor
java.nio.file.StandardCopyOption
java.nio.file.StandardOpenOption
java.nio.file.StandardWatchEventKinds
java.nio.file.WatchEvent
java.nio.file.WatchEvent.Kind
java.nio.file.WatchEvent.Modifier
java.nio.file.WatchKey
java.nio.file.WatchService
java.nio.file.Watchable
java.nio.file.attribute.AclEntry
java.nio.file.attribute.AclEntry.Builder
java.nio.file.attribute.AclEntryFlag
java.nio.file.attribute.AclEntryPermission
java.nio.file.attribute.AclEntryType
java.nio.file.attribute.AclFileAttributeView
java.nio.file.attribute.AttributeView
java.nio.file.attribute.BasicFileAttributeView
java.nio.file.attribute.BasicFileAttributes
java.nio.file.attribute.DosFileAttributeView
java.nio.file.attribute.DosFileAttributes
java.nio.file.attribute.FileAttribute
java.nio.file.attribute.FileAttributeView
java.nio.file.attribute.FileOwnerAttributeView
java.nio.file.attribute.FileStoreAttributeView
java.nio.file.attribute.FileTime
java.nio.file.attribute.GroupPrincipal
java.nio.file.attribute.PosixFileAttributeView
java.nio.file.attribute.PosixFileAttributes
java.nio.file.attribute.PosixFilePermission
java.nio.file.attribute.PosixFilePermissions
java.nio.file.attribute.UserDefinedFileAttributeView
java.nio.file.attribute.UserPrincipal
java.nio.file.attribute.UserPrincipalLookupService
java.nio.file.attribute.UserPrincipalNotFoundException
java.nio.file.spi.FileSystemProvider
java.nio.file.spi.FileTypeDetector
java.security.AlgorithmConstraints
java.security.CryptoPrimitive
java.security.cert.CRLReason
java.security.cert.CertPathValidatorException.BasicReason
java.security.cert.CertPathValidatorException.Reason
java.security.cert.CertificateRevokedException
java.security.cert.Extension
java.security.cert.PKIXReason
java.sql.PseudoColumnUsage
java.util.IllformedLocaleException
java.util.Locale.Builder
java.util.Locale.Category
java.util.Objects
java.util.concurrent.ConcurrentLinkedDeque
java.util.concurrent.ForkJoinPool
java.util.concurrent.ForkJoinTask
java.util.concurrent.ForkJoinWorkerThread
java.util.concurrent.LinkedTransferQueue
java.util.concurrent.Phaser
java.util.concurrent.RecursiveAction
java.util.concurrent.RecursiveTask
java.util.concurrent.ThreadLocalRandom
java.util.concurrent.TransferQueue
javax.lang.model.UnknownEntityException
javax.lang.model.element.Parameterizable
javax.lang.model.element.QualifiedNameable
javax.lang.model.type.UnionType
javax.lang.model.util.AbstractAnnotationValueVisitor7
javax.lang.model.util.AbstractElementVisitor7
javax.lang.model.util.AbstractTypeVisitor7
javax.lang.model.util.ElementKindVisitor7
javax.lang.model.util.ElementScanner7
javax.lang.model.util.SimpleAnnotationValueVisitor7
javax.lang.model.util.SimpleElementVisitor7
javax.lang.model.util.SimpleTypeVisitor7
javax.lang.model.util.TypeKindVisitor7
javax.print.attribute.standard.DialogTypeSelection
javax.security.auth.kerberos.KeyTab
javax.sql.rowset.RowSetFactory
javax.sql.rowset.RowSetProvider
javax.swing.JLayer
javax.swing.border.StrokeBorder
javax.swing.plaf.LayerUI
javax.swing.plaf.synth.SynthButtonUI
javax.swing.plaf.synth.SynthCheckBoxMenuItemUI
javax.swing.plaf.synth.SynthCheckBoxUI
javax.swing.plaf.synth.SynthColorChooserUI
javax.swing.plaf.synth.SynthComboBoxUI
javax.swing.plaf.synth.SynthDesktopIconUI
javax.swing.plaf.synth.SynthDesktopPaneUI
javax.swing.plaf.synth.SynthEditorPaneUI
javax.swing.plaf.synth.SynthFormattedTextFieldUI
javax.swing.plaf.synth.SynthInternalFrameUI
javax.swing.plaf.synth.SynthLabelUI
javax.swing.plaf.synth.SynthListUI
javax.swing.plaf.synth.SynthMenuBarUI
javax.swing.plaf.synth.SynthMenuItemUI
javax.swing.plaf.synth.SynthMenuUI
javax.swing.plaf.synth.SynthOptionPaneUI
javax.swing.plaf.synth.SynthPanelUI
javax.swing.plaf.synth.SynthPasswordFieldUI
javax.swing.plaf.synth.SynthPopupMenuUI
javax.swing.plaf.synth.SynthProgressBarUI
javax.swing.plaf.synth.SynthRadioButtonMenuItemUI
javax.swing.plaf.synth.SynthRadioButtonUI
javax.swing.plaf.synth.SynthRootPaneUI
javax.swing.plaf.synth.SynthScrollBarUI
javax.swing.plaf.synth.SynthScrollPaneUI
javax.swing.plaf.synth.SynthSeparatorUI
javax.swing.plaf.synth.SynthSliderUI
javax.swing.plaf.synth.SynthSpinnerUI
javax.swing.plaf.synth.SynthSplitPaneUI
javax.swing.plaf.synth.SynthTabbedPaneUI
javax.swing.plaf.synth.SynthTableHeaderUI
javax.swing.plaf.synth.SynthTableUI
javax.swing.plaf.synth.SynthTextAreaUI
javax.swing.plaf.synth.SynthTextFieldUI
javax.swing.plaf.synth.SynthTextPaneUI
javax.swing.plaf.synth.SynthToggleButtonUI
javax.swing.plaf.synth.SynthToolBarUI
javax.swing.plaf.synth.SynthToolTipUI
javax.swing.plaf.synth.SynthTreeUI
javax.swing.plaf.synth.SynthUI
javax.swing.plaf.synth.SynthViewportUI
javax.sound.midi.MidiDeviceReceiver
javax.sound.midi.MidiDeviceTransmitter