OpenJDK / jdk / hs
changeset 35409:73f0c1f79df8
Merge
author | duke |
---|---|
date | Wed, 05 Jul 2017 21:16:36 +0200 |
parents | a077e87423b1 77d4106c07d5 |
children | af04ac0cdea9 |
files | jdk/test/java/net/SocketPermission/policy |
diffstat | 265 files changed, 84798 insertions(+), 2297 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags-top-repo Thu Jan 28 15:42:54 2016 -0800 +++ b/.hgtags-top-repo Wed Jul 05 21:16:36 2017 +0200 @@ -345,3 +345,4 @@ c1f30ac14db0eaff398429c04cd9fab92e1b4b2a jdk-9+100 c4d72a1620835b5d657b7b6792c2879367d0154f jdk-9+101 6406ecf5d39482623225bb1b3098c2cac6f7d450 jdk-9+102 +47d6462e514b2097663305a57d9c844c15d5b609 jdk-9+103
--- a/common/autoconf/generated-configure.sh Thu Jan 28 15:42:54 2016 -0800 +++ b/common/autoconf/generated-configure.sh Wed Jul 05 21:16:36 2017 +0200 @@ -787,8 +787,6 @@ ac_ct_CXX CXXFLAGS CXX -ac_ct_PROPER_COMPILER_CXX -PROPER_COMPILER_CXX TOOLCHAIN_PATH_CXX POTENTIAL_CXX OBJEXT @@ -798,8 +796,6 @@ LDFLAGS CFLAGS CC -ac_ct_PROPER_COMPILER_CC -PROPER_COMPILER_CC TOOLCHAIN_PATH_CC POTENTIAL_CC TOOLCHAIN_VERSION @@ -4119,16 +4115,6 @@ - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false - # Fastdebug builds with this setting will essentially be slowdebug - # in hotspot. - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false # # Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -4849,7 +4835,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1453385294 +DATE_WHEN_GENERATED=1454146111 ############################################################################### # @@ -32022,12 +32008,10 @@ fi TEST_COMPILER="$CC" - # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links - # to 'xlc' but it is crucial that we invoke the compiler with the right name! - if test "x$OPENJDK_BUILD_OS" != xaix; then - # FIXME: This test should not be needed anymore; we don't do that for any platform. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CC" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CC" >&5 $as_echo_n "checking resolved symbolic links for CC... " >&6; } + SYMLINK_ORIGINAL="$TEST_COMPILER" if test "x$OPENJDK_BUILD_OS" != xwindows; then # Follow a chain of symbolic links. Use readlink @@ -32046,13 +32030,13 @@ fi if test "x$READLINK" != x; then - TEST_COMPILER=`$READLINK -f $TEST_COMPILER` + SYMLINK_ORIGINAL=`$READLINK -f $SYMLINK_ORIGINAL` else # Save the current directory for restoring afterwards STARTDIR=$PWD COUNTER=0 - sym_link_dir=`$DIRNAME $TEST_COMPILER` - sym_link_file=`$BASENAME $TEST_COMPILER` + sym_link_dir=`$DIRNAME $SYMLINK_ORIGINAL` + sym_link_file=`$BASENAME $SYMLINK_ORIGINAL` cd $sym_link_dir # Use -P flag to resolve symlinks in directories. cd `$THEPWDCMD -P` @@ -32072,474 +32056,25 @@ let COUNTER=COUNTER+1 done cd $STARTDIR - TEST_COMPILER=$sym_link_dir/$sym_link_file - fi - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_COMPILER" >&5 -$as_echo "$TEST_COMPILER" >&6; } - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CC is disguised ccache" >&5 -$as_echo_n "checking if CC is disguised ccache... " >&6; } - - COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"` - if test "x$COMPILER_BASENAME" = "xccache"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, trying to find proper $COMPILER_NAME compiler" >&5 -$as_echo "yes, trying to find proper $COMPILER_NAME compiler" >&6; } - # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache. - # We want to control ccache invocation ourselves, so ignore this cc and try - # searching again. - - # Remove the path to the fake ccache cc from the PATH - RETRY_COMPILER_SAVED_PATH="$PATH" - COMPILER_DIRNAME=`$DIRNAME $CC` - PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`" - - # Try again looking for our compiler - if test -n "$ac_tool_prefix"; then - for ac_prog in $TOOLCHAIN_CC_BINARY - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PROPER_COMPILER_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PROPER_COMPILER_CC"; then - ac_cv_prog_PROPER_COMPILER_CC="$PROPER_COMPILER_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PROPER_COMPILER_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PROPER_COMPILER_CC=$ac_cv_prog_PROPER_COMPILER_CC -if test -n "$PROPER_COMPILER_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CC" >&5 -$as_echo "$PROPER_COMPILER_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$PROPER_COMPILER_CC" && break - done -fi -if test -z "$PROPER_COMPILER_CC"; then - ac_ct_PROPER_COMPILER_CC=$PROPER_COMPILER_CC - for ac_prog in $TOOLCHAIN_CC_BINARY -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_PROPER_COMPILER_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_PROPER_COMPILER_CC"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CC="$ac_ct_PROPER_COMPILER_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_PROPER_COMPILER_CC=$ac_cv_prog_ac_ct_PROPER_COMPILER_CC -if test -n "$ac_ct_PROPER_COMPILER_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PROPER_COMPILER_CC" >&5 -$as_echo "$ac_ct_PROPER_COMPILER_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_PROPER_COMPILER_CC" && break -done - - if test "x$ac_ct_PROPER_COMPILER_CC" = x; then - PROPER_COMPILER_CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PROPER_COMPILER_CC=$ac_ct_PROPER_COMPILER_CC - fi -fi - - - # Only process if variable expands to non-empty - - if test "x$PROPER_COMPILER_CC" != x; then - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CC" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path=`$CYGPATH -u "$path"` - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` - fi - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path=`$CYGPATH -u "$path"` - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` - fi - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - fi - - # Cygwin tries to hide some aspects of the Windows file system, such that binaries are - # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered - # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then - # "foo.exe" is OK but "foo" is an error. - # - # This test is therefore slightly more accurate than "test -f" to check for file presence. - # It is also a way to make sure we got the proper file name for the real test later on. - test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null` - if test "x$test_shortpath" = x; then - # Short path failed, file does not exist as specified. - # Try adding .exe or .cmd - if test -f "${new_path}.exe"; then - input_to_shortpath="${new_path}.exe" - elif test -f "${new_path}.cmd"; then - input_to_shortpath="${new_path}.cmd" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$new_path\", is invalid." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$new_path\", is invalid." >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&5 -$as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&6;} - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - else - input_to_shortpath="$new_path" - fi - - # Call helper function which possibly converts this using DOS-style short mode. - # If so, the updated path is stored in $new_path. - new_path="$input_to_shortpath" - - input_path="$input_to_shortpath" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - shortmode_path=`$CYGPATH -s -m -a "$input_path"` - path_after_shortmode=`$CYGPATH -u "$shortmode_path"` - if test "x$path_after_shortmode" != "x$input_to_shortpath"; then - # Going to short mode and back again did indeed matter. Since short mode is - # case insensitive, let's make it lowercase to improve readability. - shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Now convert it back to Unix-style (cygpath) - input_path=`$CYGPATH -u "$shortmode_path"` - new_path="$input_path" - fi - fi - - test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/` - if test "x$test_cygdrive_prefix" = x; then - # As a simple fix, exclude /usr/bin since it's not a real path. - if test "x`$ECHO $input_to_shortpath | $GREP ^/usr/bin/`" = x; then - # The path is in a Cygwin special directory (e.g. /home). We need this converted to - # a path prefixed by /cygdrive for fixpath to work. - new_path="$CYGWIN_ROOT_PATH$input_path" - fi - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CC" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in MSYS causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - fi - - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - fi - - # Now new_path has a complete unix path to the binary - if test "x`$ECHO $new_path | $GREP ^/bin/`" != x; then - # Keep paths in /bin as-is, but remove trailing .exe if any - new_path="${new_path/%.exe/}" - # Do not save /bin paths to all_fixpath_prefixes! - else - # Not in mixed or Windows style, start by that. - new_path=`cmd //c echo $new_path` - - input_path="$new_path" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - fi - - # Output is in $new_path - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - # Save the first 10 bytes of this path to the storage, so fixpath can work. - all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}") - fi - - else - # We're on a unix platform. Hooray! :) - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CC" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Cannot rely on the command "which" here since it doesn't always work. - is_absolute_path=`$ECHO "$path" | $GREP ^/` - if test -z "$is_absolute_path"; then - # Path to executable is not absolute. Find it. - IFS_save="$IFS" - IFS=: - for p in $PATH; do - if test -f "$p/$path" && test -x "$p/$path"; then - new_path="$p/$path" - break - fi - done - IFS="$IFS_save" - else - # This is an absolute path, we can use it without further modifications. - new_path="$path" - fi - - if test "x$new_path" = x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: This might be caused by spaces in the path, which is not allowed." >&5 -$as_echo "$as_me: This might be caused by spaces in the path, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - fi - - # Now join together the path and the arguments once again - if test "x$arguments" != xEOL; then - new_complete="$new_path ${arguments% *}" - else - new_complete="$new_path" - fi - - if test "x$complete" != "x$new_complete"; then - PROPER_COMPILER_CC="$new_complete" - { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting PROPER_COMPILER_CC to \"$new_complete\"" >&5 -$as_echo "$as_me: Rewriting PROPER_COMPILER_CC to \"$new_complete\"" >&6;} - fi - fi - - PATH="$RETRY_COMPILER_SAVED_PATH" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolved symbolic links for CC" >&5 -$as_echo_n "checking for resolved symbolic links for CC... " >&6; } - - if test "x$OPENJDK_BUILD_OS" != xwindows; then - # Follow a chain of symbolic links. Use readlink - # where it exists, else fall back to horribly - # complicated shell code. - if test "x$READLINK_TESTED" != yes; then - # On MacOSX there is a readlink tool with a different - # purpose than the GNU readlink tool. Check the found readlink. - ISGNU=`$READLINK --version 2>&1 | $GREP GNU` - if test "x$ISGNU" = x; then - # A readlink that we do not know how to use. - # Are there other non-GNU readlinks out there? - READLINK_TESTED=yes - READLINK= - fi - fi - - if test "x$READLINK" != x; then - PROPER_COMPILER_CC=`$READLINK -f $PROPER_COMPILER_CC` - else - # Save the current directory for restoring afterwards - STARTDIR=$PWD - COUNTER=0 - sym_link_dir=`$DIRNAME $PROPER_COMPILER_CC` - sym_link_file=`$BASENAME $PROPER_COMPILER_CC` - cd $sym_link_dir - # Use -P flag to resolve symlinks in directories. - cd `$THEPWDCMD -P` - sym_link_dir=`$THEPWDCMD -P` - # Resolve file symlinks - while test $COUNTER -lt 20; do - ISLINK=`$LS -l $sym_link_dir/$sym_link_file | $GREP '\->' | $SED -e 's/.*-> \(.*\)/\1/'` - if test "x$ISLINK" == x; then - # This is not a symbolic link! We are done! - break - fi - # Again resolve directory symlinks since the target of the just found - # link could be in a different directory - cd `$DIRNAME $ISLINK` - sym_link_dir=`$THEPWDCMD -P` - sym_link_file=`$BASENAME $ISLINK` - let COUNTER=COUNTER+1 - done - cd $STARTDIR - PROPER_COMPILER_CC=$sym_link_dir/$sym_link_file - fi - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CC" >&5 -$as_echo "$PROPER_COMPILER_CC" >&6; } - CC="$PROPER_COMPILER_CC" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, keeping CC" >&5 -$as_echo "no, keeping CC" >&6; } + SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file + fi + fi + + if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no symlink" >&5 +$as_echo "no symlink" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5 +$as_echo "$SYMLINK_ORIGINAL" >&6; } + + # We can't handle ccache by gcc wrappers, since we need to know if we're + # using ccache. Instead ccache usage must be controlled by a configure option. + COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"` + if test "x$COMPILER_BASENAME" = "xccache"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: Please use --enable-ccache instead of providing a wrapped compiler." >&5 +$as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped compiler." >&6;} + as_fn_error $? "$TEST_COMPILER is a symbolic link to ccache. This is not supported." "$LINENO" 5 + fi fi @@ -33770,12 +33305,10 @@ fi TEST_COMPILER="$CXX" - # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links - # to 'xlc' but it is crucial that we invoke the compiler with the right name! - if test "x$OPENJDK_BUILD_OS" != xaix; then - # FIXME: This test should not be needed anymore; we don't do that for any platform. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CXX" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CXX" >&5 $as_echo_n "checking resolved symbolic links for CXX... " >&6; } + SYMLINK_ORIGINAL="$TEST_COMPILER" if test "x$OPENJDK_BUILD_OS" != xwindows; then # Follow a chain of symbolic links. Use readlink @@ -33794,13 +33327,13 @@ fi if test "x$READLINK" != x; then - TEST_COMPILER=`$READLINK -f $TEST_COMPILER` + SYMLINK_ORIGINAL=`$READLINK -f $SYMLINK_ORIGINAL` else # Save the current directory for restoring afterwards STARTDIR=$PWD COUNTER=0 - sym_link_dir=`$DIRNAME $TEST_COMPILER` - sym_link_file=`$BASENAME $TEST_COMPILER` + sym_link_dir=`$DIRNAME $SYMLINK_ORIGINAL` + sym_link_file=`$BASENAME $SYMLINK_ORIGINAL` cd $sym_link_dir # Use -P flag to resolve symlinks in directories. cd `$THEPWDCMD -P` @@ -33820,474 +33353,25 @@ let COUNTER=COUNTER+1 done cd $STARTDIR - TEST_COMPILER=$sym_link_dir/$sym_link_file - fi - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_COMPILER" >&5 -$as_echo "$TEST_COMPILER" >&6; } - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CXX is disguised ccache" >&5 -$as_echo_n "checking if CXX is disguised ccache... " >&6; } - - COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"` - if test "x$COMPILER_BASENAME" = "xccache"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, trying to find proper $COMPILER_NAME compiler" >&5 -$as_echo "yes, trying to find proper $COMPILER_NAME compiler" >&6; } - # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache. - # We want to control ccache invocation ourselves, so ignore this cc and try - # searching again. - - # Remove the path to the fake ccache cc from the PATH - RETRY_COMPILER_SAVED_PATH="$PATH" - COMPILER_DIRNAME=`$DIRNAME $CXX` - PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`" - - # Try again looking for our compiler - if test -n "$ac_tool_prefix"; then - for ac_prog in $TOOLCHAIN_CXX_BINARY - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PROPER_COMPILER_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PROPER_COMPILER_CXX"; then - ac_cv_prog_PROPER_COMPILER_CXX="$PROPER_COMPILER_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PROPER_COMPILER_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PROPER_COMPILER_CXX=$ac_cv_prog_PROPER_COMPILER_CXX -if test -n "$PROPER_COMPILER_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CXX" >&5 -$as_echo "$PROPER_COMPILER_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$PROPER_COMPILER_CXX" && break - done -fi -if test -z "$PROPER_COMPILER_CXX"; then - ac_ct_PROPER_COMPILER_CXX=$PROPER_COMPILER_CXX - for ac_prog in $TOOLCHAIN_CXX_BINARY -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_PROPER_COMPILER_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_PROPER_COMPILER_CXX"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CXX="$ac_ct_PROPER_COMPILER_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_PROPER_COMPILER_CXX=$ac_cv_prog_ac_ct_PROPER_COMPILER_CXX -if test -n "$ac_ct_PROPER_COMPILER_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PROPER_COMPILER_CXX" >&5 -$as_echo "$ac_ct_PROPER_COMPILER_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_PROPER_COMPILER_CXX" && break -done - - if test "x$ac_ct_PROPER_COMPILER_CXX" = x; then - PROPER_COMPILER_CXX="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PROPER_COMPILER_CXX=$ac_ct_PROPER_COMPILER_CXX - fi -fi - - - # Only process if variable expands to non-empty - - if test "x$PROPER_COMPILER_CXX" != x; then - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CXX" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path=`$CYGPATH -u "$path"` - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` - fi - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path=`$CYGPATH -u "$path"` - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` - fi - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - fi - - # Cygwin tries to hide some aspects of the Windows file system, such that binaries are - # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered - # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then - # "foo.exe" is OK but "foo" is an error. - # - # This test is therefore slightly more accurate than "test -f" to check for file presence. - # It is also a way to make sure we got the proper file name for the real test later on. - test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null` - if test "x$test_shortpath" = x; then - # Short path failed, file does not exist as specified. - # Try adding .exe or .cmd - if test -f "${new_path}.exe"; then - input_to_shortpath="${new_path}.exe" - elif test -f "${new_path}.cmd"; then - input_to_shortpath="${new_path}.cmd" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$new_path\", is invalid." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$new_path\", is invalid." >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&5 -$as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&6;} - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - else - input_to_shortpath="$new_path" - fi - - # Call helper function which possibly converts this using DOS-style short mode. - # If so, the updated path is stored in $new_path. - new_path="$input_to_shortpath" - - input_path="$input_to_shortpath" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - shortmode_path=`$CYGPATH -s -m -a "$input_path"` - path_after_shortmode=`$CYGPATH -u "$shortmode_path"` - if test "x$path_after_shortmode" != "x$input_to_shortpath"; then - # Going to short mode and back again did indeed matter. Since short mode is - # case insensitive, let's make it lowercase to improve readability. - shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Now convert it back to Unix-style (cygpath) - input_path=`$CYGPATH -u "$shortmode_path"` - new_path="$input_path" - fi - fi - - test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/` - if test "x$test_cygdrive_prefix" = x; then - # As a simple fix, exclude /usr/bin since it's not a real path. - if test "x`$ECHO $input_to_shortpath | $GREP ^/usr/bin/`" = x; then - # The path is in a Cygwin special directory (e.g. /home). We need this converted to - # a path prefixed by /cygdrive for fixpath to work. - new_path="$CYGWIN_ROOT_PATH$input_path" - fi - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CXX" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in MSYS causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - fi - - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - fi - - # Now new_path has a complete unix path to the binary - if test "x`$ECHO $new_path | $GREP ^/bin/`" != x; then - # Keep paths in /bin as-is, but remove trailing .exe if any - new_path="${new_path/%.exe/}" - # Do not save /bin paths to all_fixpath_prefixes! - else - # Not in mixed or Windows style, start by that. - new_path=`cmd //c echo $new_path` - - input_path="$new_path" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - fi - - # Output is in $new_path - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - # Save the first 10 bytes of this path to the storage, so fixpath can work. - all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}") - fi - - else - # We're on a unix platform. Hooray! :) - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CXX" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Cannot rely on the command "which" here since it doesn't always work. - is_absolute_path=`$ECHO "$path" | $GREP ^/` - if test -z "$is_absolute_path"; then - # Path to executable is not absolute. Find it. - IFS_save="$IFS" - IFS=: - for p in $PATH; do - if test -f "$p/$path" && test -x "$p/$path"; then - new_path="$p/$path" - break - fi - done - IFS="$IFS_save" - else - # This is an absolute path, we can use it without further modifications. - new_path="$path" - fi - - if test "x$new_path" = x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: This might be caused by spaces in the path, which is not allowed." >&5 -$as_echo "$as_me: This might be caused by spaces in the path, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - fi - - # Now join together the path and the arguments once again - if test "x$arguments" != xEOL; then - new_complete="$new_path ${arguments% *}" - else - new_complete="$new_path" - fi - - if test "x$complete" != "x$new_complete"; then - PROPER_COMPILER_CXX="$new_complete" - { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting PROPER_COMPILER_CXX to \"$new_complete\"" >&5 -$as_echo "$as_me: Rewriting PROPER_COMPILER_CXX to \"$new_complete\"" >&6;} - fi - fi - - PATH="$RETRY_COMPILER_SAVED_PATH" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolved symbolic links for CXX" >&5 -$as_echo_n "checking for resolved symbolic links for CXX... " >&6; } - - if test "x$OPENJDK_BUILD_OS" != xwindows; then - # Follow a chain of symbolic links. Use readlink - # where it exists, else fall back to horribly - # complicated shell code. - if test "x$READLINK_TESTED" != yes; then - # On MacOSX there is a readlink tool with a different - # purpose than the GNU readlink tool. Check the found readlink. - ISGNU=`$READLINK --version 2>&1 | $GREP GNU` - if test "x$ISGNU" = x; then - # A readlink that we do not know how to use. - # Are there other non-GNU readlinks out there? - READLINK_TESTED=yes - READLINK= - fi - fi - - if test "x$READLINK" != x; then - PROPER_COMPILER_CXX=`$READLINK -f $PROPER_COMPILER_CXX` - else - # Save the current directory for restoring afterwards - STARTDIR=$PWD - COUNTER=0 - sym_link_dir=`$DIRNAME $PROPER_COMPILER_CXX` - sym_link_file=`$BASENAME $PROPER_COMPILER_CXX` - cd $sym_link_dir - # Use -P flag to resolve symlinks in directories. - cd `$THEPWDCMD -P` - sym_link_dir=`$THEPWDCMD -P` - # Resolve file symlinks - while test $COUNTER -lt 20; do - ISLINK=`$LS -l $sym_link_dir/$sym_link_file | $GREP '\->' | $SED -e 's/.*-> \(.*\)/\1/'` - if test "x$ISLINK" == x; then - # This is not a symbolic link! We are done! - break - fi - # Again resolve directory symlinks since the target of the just found - # link could be in a different directory - cd `$DIRNAME $ISLINK` - sym_link_dir=`$THEPWDCMD -P` - sym_link_file=`$BASENAME $ISLINK` - let COUNTER=COUNTER+1 - done - cd $STARTDIR - PROPER_COMPILER_CXX=$sym_link_dir/$sym_link_file - fi - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CXX" >&5 -$as_echo "$PROPER_COMPILER_CXX" >&6; } - CXX="$PROPER_COMPILER_CXX" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, keeping CXX" >&5 -$as_echo "no, keeping CXX" >&6; } + SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file + fi + fi + + if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no symlink" >&5 +$as_echo "no symlink" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5 +$as_echo "$SYMLINK_ORIGINAL" >&6; } + + # We can't handle ccache by gcc wrappers, since we need to know if we're + # using ccache. Instead ccache usage must be controlled by a configure option. + COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"` + if test "x$COMPILER_BASENAME" = "xccache"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: Please use --enable-ccache instead of providing a wrapped compiler." >&5 +$as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped compiler." >&6;} + as_fn_error $? "$TEST_COMPILER is a symbolic link to ccache. This is not supported." "$LINENO" 5 + fi fi @@ -58558,6 +57642,10 @@ fi done + # Due to https://llvm.org/bugs/show_bug.cgi?id=16902, llvm does not + # always properly detect -ltinfo + LLVM_LIBS="${LLVM_LIBS} -ltinfo" +
--- a/common/autoconf/hotspot.m4 Thu Jan 28 15:42:54 2016 -0800 +++ b/common/autoconf/hotspot.m4 Wed Jul 05 21:16:36 2017 +0200 @@ -266,14 +266,3 @@ HOTSPOT_MAKE_ARGS="$HOTSPOT_TARGET" AC_SUBST(HOTSPOT_MAKE_ARGS) ]) - - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false - # Fastdebug builds with this setting will essentially be slowdebug - # in hotspot. - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false \ No newline at end of file
--- a/common/autoconf/libraries.m4 Thu Jan 28 15:42:54 2016 -0800 +++ b/common/autoconf/libraries.m4 Wed Jul 05 21:16:36 2017 +0200 @@ -144,6 +144,10 @@ fi done + # Due to https://llvm.org/bugs/show_bug.cgi?id=16902, llvm does not + # always properly detect -ltinfo + LLVM_LIBS="${LLVM_LIBS} -ltinfo" + AC_SUBST(LLVM_CFLAGS) AC_SUBST(LLVM_LDFLAGS) AC_SUBST(LLVM_LIBS)
--- a/common/autoconf/spec.gmk.in Thu Jan 28 15:42:54 2016 -0800 +++ b/common/autoconf/spec.gmk.in Wed Jul 05 21:16:36 2017 +0200 @@ -500,7 +500,7 @@ INTERIM_LANGTOOLS_JAR = $(BUILDTOOLS_OUTPUTDIR)/interim_langtools.jar INTERIM_LANGTOOLS_ARGS = "-Xbootclasspath/p:$(INTERIM_LANGTOOLS_JAR)" -cp $(INTERIM_LANGTOOLS_JAR) NEW_JAVAC = $(INTERIM_LANGTOOLS_ARGS) com.sun.tools.javac.Main -NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) com.sun.tools.javadoc.Main +NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) jdk.javadoc.internal.tool.Main # Base flags for RC # Guarding this against resetting value. Legacy make files include spec multiple
--- a/common/autoconf/toolchain.m4 Thu Jan 28 15:42:54 2016 -0800 +++ b/common/autoconf/toolchain.m4 Wed Jul 05 21:16:36 2017 +0200 @@ -433,39 +433,22 @@ # Now we have a compiler binary in $1. Make sure it's okay. BASIC_FIXUP_EXECUTABLE($1) TEST_COMPILER="[$]$1" - # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links - # to 'xlc' but it is crucial that we invoke the compiler with the right name! - if test "x$OPENJDK_BUILD_OS" != xaix; then - # FIXME: This test should not be needed anymore; we don't do that for any platform. - AC_MSG_CHECKING([resolved symbolic links for $1]) - BASIC_REMOVE_SYMBOLIC_LINKS(TEST_COMPILER) - AC_MSG_RESULT([$TEST_COMPILER]) - fi - AC_MSG_CHECKING([if $1 is disguised ccache]) - COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"` - if test "x$COMPILER_BASENAME" = "xccache"; then - AC_MSG_RESULT([yes, trying to find proper $COMPILER_NAME compiler]) - # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache. - # We want to control ccache invocation ourselves, so ignore this cc and try - # searching again. + AC_MSG_CHECKING([resolved symbolic links for $1]) + SYMLINK_ORIGINAL="$TEST_COMPILER" + BASIC_REMOVE_SYMBOLIC_LINKS(SYMLINK_ORIGINAL) + if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then + AC_MSG_RESULT([no symlink]) + else + AC_MSG_RESULT([$SYMLINK_ORIGINAL]) - # Remove the path to the fake ccache cc from the PATH - RETRY_COMPILER_SAVED_PATH="$PATH" - COMPILER_DIRNAME=`$DIRNAME [$]$1` - PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`" - - # Try again looking for our compiler - AC_CHECK_TOOLS(PROPER_COMPILER_$1, $3) - BASIC_FIXUP_EXECUTABLE(PROPER_COMPILER_$1) - PATH="$RETRY_COMPILER_SAVED_PATH" - - AC_MSG_CHECKING([for resolved symbolic links for $1]) - BASIC_REMOVE_SYMBOLIC_LINKS(PROPER_COMPILER_$1) - AC_MSG_RESULT([$PROPER_COMPILER_$1]) - $1="$PROPER_COMPILER_$1" - else - AC_MSG_RESULT([no, keeping $1]) + # We can't handle ccache by gcc wrappers, since we need to know if we're + # using ccache. Instead ccache usage must be controlled by a configure option. + COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"` + if test "x$COMPILER_BASENAME" = "xccache"; then + AC_MSG_NOTICE([Please use --enable-ccache instead of providing a wrapped compiler.]) + AC_MSG_ERROR([$TEST_COMPILER is a symbolic link to ccache. This is not supported.]) + fi fi TOOLCHAIN_CHECK_COMPILER_VERSION([$1], [$COMPILER_NAME])
--- a/common/bin/compare.sh Thu Jan 28 15:42:54 2016 -0800 +++ b/common/bin/compare.sh Wed Jul 05 21:16:36 2017 +0200 @@ -306,7 +306,7 @@ ! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \ ! -name "*.obj" ! -name "*.o" ! -name "JavaControlPanelHelper" \ ! -name "JavaUpdater" ! -name "JavaWSApplicationStub" \ - ! -name "jspawnhelper" \ + ! -name "jspawnhelper" ! -name "*.a" \ | $GREP -v "./bin/" | $SORT | $FILTER) echo Other files with binary differences... @@ -939,7 +939,7 @@ WORK_DIR=$3 LIBS=$(cd $THIS_DIR && $FIND . -type f \( -name 'lib*.so' -o -name '*.dylib' \ - -o -name '*.dll' -o -name '*.obj' -o -name '*.o' \ + -o -name '*.dll' -o -name '*.obj' -o -name '*.o' -o -name '*.a' \ -o -name '*.cpl' \) | $SORT | $FILTER) if [ -n "$LIBS" ]; then
--- a/corba/.hgtags Thu Jan 28 15:42:54 2016 -0800 +++ b/corba/.hgtags Wed Jul 05 21:16:36 2017 +0200 @@ -345,3 +345,4 @@ 791d0d3ac0138faeb6110bd840a4545bc1950df2 jdk-9+100 30dfb3bd3d06b4bb80a087babc0d1841edba187b jdk-9+101 9c4662334d933d299928d1f599d02ff50777cbf8 jdk-9+102 +0680fb7dae4da1ee6cf783c4b74184e3e08d3179 jdk-9+103
--- a/hotspot/.hgtags Thu Jan 28 15:42:54 2016 -0800 +++ b/hotspot/.hgtags Wed Jul 05 21:16:36 2017 +0200 @@ -505,3 +505,4 @@ bdb0acafc63c42e84d9d8195bf2e2b25ee9c3306 jdk-9+100 9f45d3d57d6948cf526fbc2e2891a9a74ac6941a jdk-9+101 d5239fc1b69749ae50793c61b899fcdacf3df857 jdk-9+102 +c5f55130b1b69510d9a6f4a3105b58e21cd7ffe1 jdk-9+103
--- a/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java Thu Jan 28 15:42:54 2016 -0800 +++ b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java Wed Jul 05 21:16:36 2017 +0200 @@ -64,14 +64,18 @@ throws Exception { MemoryPoolMXBean pool = getMemoryPool(memoryPoolName); + // First, call all the methods to let them allocate their own slab of metadata + getMinCapacity(perfNS); + getCapacity(perfNS); + getUsed(perfNS); + pool.getUsage().getInit(); + pool.getUsage().getUsed(); + pool.getUsage().getCommitted(); + assertEQ(1L, 1L); + // Must do a GC to update performance counters System.gc(); assertEQ(getMinCapacity(perfNS), pool.getUsage().getInit()); - - // Must do a second GC to update the perfomance counters again, since - // the call pool.getUsage().getInit() could have allocated some - // metadata. - System.gc(); assertEQ(getUsed(perfNS), pool.getUsage().getUsed()); assertEQ(getCapacity(perfNS), pool.getUsage().getCommitted()); }
--- a/jdk/.hgtags Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/.hgtags Wed Jul 05 21:16:36 2017 +0200 @@ -345,3 +345,4 @@ 3d452840f48299a36842760d17c0c8402f0e1266 jdk-9+100 5e8370fb3ed925335164afe340d1e54beab2d4d5 jdk-9+101 6eb3c8132e489dab81adde4ce29844904ce15482 jdk-9+102 +eee1ced1d8e78293f2a004af818ca474387dbebf jdk-9+103
--- a/jdk/make/launcher/Launcher-jdk.javadoc.gmk Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/make/launcher/Launcher-jdk.javadoc.gmk Wed Jul 05 21:16:36 2017 +0200 @@ -26,7 +26,7 @@ include LauncherCommon.gmk $(eval $(call SetupBuildLauncher, javadoc, \ - MAIN_CLASS := com.sun.tools.javadoc.Main, \ + MAIN_CLASS := jdk.javadoc.internal.tool.Main, \ CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \ -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \ ))
--- a/jdk/src/java.base/share/classes/java/lang/Integer.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Integer.java Wed Jul 05 21:16:36 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -339,7 +339,6 @@ // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { byte[] buf = new byte[chars]; formatUnsignedInt(val, shift, buf, 0, chars); @@ -477,8 +476,13 @@ * values, to cover the Integer.MIN_VALUE case. Converting otherwise * (negative to positive) will expose -Integer.MIN_VALUE that overflows * integer. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, Latin1-encoded + * @return index of the most significant digit or minus sign, if present */ - static void getChars(int i, int index, byte[] buf) { + static int getChars(int i, int index, byte[] buf) { int q, r; int charPos = index; @@ -509,9 +513,19 @@ if (negative) { buf[--charPos] = (byte)'-'; } + return charPos; } - static void getCharsUTF16(int i, int index, byte[] buf) { + /** + * This is a variant of {@link #getChars(int, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + static int getCharsUTF16(int i, int index, byte[] buf) { int q, r; int charPos = index; @@ -542,6 +556,7 @@ if (negative) { StringUTF16.putChar(buf, --charPos, '-'); } + return charPos; } // Left here for compatibility reasons, see JDK-8143900.
--- a/jdk/src/java.base/share/classes/java/lang/Long.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Long.java Wed Jul 05 21:16:36 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -379,7 +379,6 @@ // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { byte[] buf = new byte[chars]; formatUnsignedLong0(val, shift, buf, 0, chars); @@ -489,8 +488,13 @@ * values, to cover the Long.MIN_VALUE case. Converting otherwise * (negative to positive) will expose -Long.MIN_VALUE that overflows * long. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, Latin1-encoded + * @return index of the most significant digit or minus sign, if present */ - static void getChars(long i, int index, byte[] buf) { + static int getChars(long i, int index, byte[] buf) { long q; int r; int charPos = index; @@ -533,9 +537,19 @@ if (negative) { buf[--charPos] = (byte)'-'; } + return charPos; } - static void getCharsUTF16(long i, int index, byte[] buf) { + /** + * This is a variant of {@link #getChars(long, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + static int getCharsUTF16(long i, int index, byte[] buf) { long q; int r; int charPos = index; @@ -578,6 +592,7 @@ if (negative) { StringUTF16.putChar(buf, --charPos, '-'); } + return charPos; } /**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Helper for string concatenation. These methods are mostly looked up with private lookups + * from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle} + * combinators there. + */ +final class StringConcatHelper { + + private StringConcatHelper() { + // no instantiation + } + + /** + * Check for overflow, throw the exception on overflow. + * @param len String length + * @return length + */ + private static int checkOverflow(int len) { + if (len < 0) { + throw new OutOfMemoryError("Overflow: String length out of range"); + } + return len; + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, boolean value) { + return checkOverflow(current + (value ? 4 : 5)); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, byte value) { + return mixLen(current, (int)value); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, char value) { + return checkOverflow(current + 1); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, short value) { + return mixLen(current, (int)value); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, int value) { + return checkOverflow(current + Integer.stringSize(value)); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, long value) { + return checkOverflow(current + Long.stringSize(value)); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, String value) { + return checkOverflow(current + value.length()); + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, char value) { + return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1)); + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, String value) { + return (byte)(current | value.coder()); + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, boolean value) { + // Booleans are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, byte value) { + // Bytes are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, short value) { + // Shorts are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, int value) { + // Ints are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, long value) { + // Longs are represented with Latin1 + return current; + } + + /** + * Prepends the stringly representation of boolean value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value boolean value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, boolean value) { + if (coder == String.LATIN1) { + if (value) { + buf[--index] = 'e'; + buf[--index] = 'u'; + buf[--index] = 'r'; + buf[--index] = 't'; + } else { + buf[--index] = 'e'; + buf[--index] = 's'; + buf[--index] = 'l'; + buf[--index] = 'a'; + buf[--index] = 'f'; + } + } else { + if (value) { + StringUTF16.putChar(buf, --index, 'e'); + StringUTF16.putChar(buf, --index, 'u'); + StringUTF16.putChar(buf, --index, 'r'); + StringUTF16.putChar(buf, --index, 't'); + } else { + StringUTF16.putChar(buf, --index, 'e'); + StringUTF16.putChar(buf, --index, 's'); + StringUTF16.putChar(buf, --index, 'l'); + StringUTF16.putChar(buf, --index, 'a'); + StringUTF16.putChar(buf, --index, 'f'); + } + } + return index; + } + + /** + * Prepends the stringly representation of byte value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value byte value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, byte value) { + return prepend(index, buf, coder, (int)value); + } + + /** + * Prepends the stringly representation of char value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value char value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, char value) { + if (coder == String.LATIN1) { + buf[--index] = (byte) (value & 0xFF); + } else { + StringUTF16.putChar(buf, --index, value); + } + return index; + } + + /** + * Prepends the stringly representation of short value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value short value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, short value) { + return prepend(index, buf, coder, (int)value); + } + + /** + * Prepends the stringly representation of integer value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value integer value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, int value) { + if (coder == String.LATIN1) { + return Integer.getChars(value, index, buf); + } else { + return Integer.getCharsUTF16(value, index, buf); + } + } + + /** + * Prepends the stringly representation of long value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value long value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, long value) { + if (coder == String.LATIN1) { + return Long.getChars(value, index, buf); + } else { + return Long.getCharsUTF16(value, index, buf); + } + } + + /** + * Prepends the stringly representation of String value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value String value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, String value) { + index -= value.length(); + value.getBytes(buf, index, coder); + return index; + } + + /** + * Instantiates the String with given buffer and coder + * @param buf buffer to use + * @param coder coder to use + * @return String resulting string + */ + static String newString(byte[] buf, byte coder) { + // Use the private, non-copying constructor (unsafe!) + return new String(buf, coder); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +/** + * StringConcatException is thrown by {@link StringConcatFactory} when linkage + * invariants are violated. + * + * @since 9 + */ +public class StringConcatException extends Exception { + private static final long serialVersionUID = 292L + 9L; + + /** + * Constructs an exception with a message + * @param msg exception message + */ + public StringConcatException(String msg) { + super(msg); + } + + /** + * Constructs an exception with a message and a linked throwable + * @param msg exception message + * @param cause throwable cause + */ + public StringConcatException(String msg, Throwable cause) { + super(msg, cause); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,1792 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Label; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.vm.annotation.ForceInline; +import sun.misc.Unsafe; + +import java.lang.invoke.MethodHandles.Lookup; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +/** + * <p>Methods to facilitate the creation of String concatenation methods, that + * can be used to efficiently concatenate a known number of arguments of known + * types, possibly after type adaptation and partial evaluation of arguments. + * These methods are typically used as <em>bootstrap methods</em> for {@code + * invokedynamic} call sites, to support the <em>string concatenation</em> + * feature of the Java Programming Language. + * + * <p>Indirect access to the behavior specified by the provided {@code + * MethodHandle} proceeds in order through two phases: + * + * <ol> + * <li><em>Linkage</em> occurs when the methods in this class are invoked. + * They take as arguments a method type describing the concatenated arguments + * count and types, and optionally the String <em>recipe</em>, plus the + * constants that participate in the String concatenation. The details on + * accepted recipe shapes are described further below. Linkage may involve + * dynamically loading a new class that implements the expected concatenation + * behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the + * exact concatenation method. The concatenation methods may be shared among + * different {@code CallSite}s, e.g. if linkage methods produce them as pure + * functions.</li> + * + * <li><em>Invocation</em> occurs when a generated concatenation method is + * invoked with the exact dynamic arguments. This may occur many times for a + * single concatenation method. The method referenced by the behavior {@code + * MethodHandle} is invoked with the static arguments and any additional dynamic + * arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li> + * </ol> + * + * <p> This class provides two forms of linkage methods: a simple version + * ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String, + * MethodType)}) using only the dynamic arguments, and an advanced version + * ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup, + * String, MethodType, String, Object...)} using the advanced forms of capturing + * the constant arguments. The advanced strategy can produce marginally better + * invocation bytecode, at the expense of exploding the number of shapes of + * string concatenation methods present at runtime, because those shapes would + * include constant static arguments as well. + * + * @author Aleksey Shipilev + * @author Remi Forax + * @author Peter Levart + * + * @apiNote + * <p>There is a JVM limit (classfile structural constraint): no method + * can call with more than 255 slots. This limits the number of static and + * dynamic arguments one can pass to bootstrap method. Since there are potential + * concatenation strategies that use {@code MethodHandle} combinators, we need + * to reserve a few empty slots on the parameter lists to to capture the + * temporal results. This is why bootstrap methods in this factory do not accept + * more than 200 argument slots. Users requiring more than 200 argument slots in + * concatenation are expected to split the large concatenation in smaller + * expressions. + * + * @since 9 + */ +public final class StringConcatFactory { + + /** + * Tag used to demarcate an ordinary argument. + */ + private static final char TAG_ARG = '\u0001'; + + /** + * Tag used to demarcate a constant. + */ + private static final char TAG_CONST = '\u0002'; + + /** + * Maximum number of argument slots in String Concat call. + * + * While the maximum number of argument slots that indy call can handle is 253, + * we do not use all those slots, to let the strategies with MethodHandle + * combinators to use some arguments. + */ + private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200; + + /** + * Concatenation strategy to use. See {@link Strategy} for possible options. + * This option is controllable with -Djava.lang.invoke.stringConcat JDK option. + */ + private static final Strategy STRATEGY; + + /** + * Default strategy to use for concatenation. + */ + private static final Strategy DEFAULT_STRATEGY = Strategy.BC_SB; + + private enum Strategy { + /** + * Bytecode generator, calling into {@link java.lang.StringBuilder}. + */ + BC_SB, + + /** + * Bytecode generator, calling into {@link java.lang.StringBuilder}; + * but trying to estimate the required storage. + */ + BC_SB_SIZED, + + /** + * Bytecode generator, calling into {@link java.lang.StringBuilder}; + * but computing the required storage exactly. + */ + BC_SB_SIZED_EXACT, + + /** + * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. + * This strategy also tries to estimate the required storage. + */ + MH_SB_SIZED, + + /** + * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. + * This strategy also estimate the required storage exactly. + */ + MH_SB_SIZED_EXACT, + + /** + * MethodHandle-based generator, that constructs its own byte[] array from + * the arguments. It computes the required storage exactly. + */ + MH_INLINE_SIZED_EXACT + } + + /** + * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance) + * checks, etc. + */ + private static final boolean DEBUG; + + /** + * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated + * code, at the expense of contaminating the profiles. + */ + private static final boolean CACHE_ENABLE; + + private static final ConcurrentMap<Key, MethodHandle> CACHE; + + static { + // Poke the privileged block once, taking everything we need: + final Object[] values = new Object[3]; + AccessController.doPrivileged((PrivilegedAction<Object>) () -> { + values[0] = System.getProperty("java.lang.invoke.stringConcat"); + values[1] = Boolean.getBoolean("java.lang.invoke.stringConcat.cache"); + values[2] = Boolean.getBoolean("java.lang.invoke.stringConcat.debug"); + return null; + }); + + final String strategy = (String) values[0]; + CACHE_ENABLE = (Boolean) values[1]; + DEBUG = (Boolean) values[2]; + + STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy); + CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null; + } + + private static final class Key { + final MethodType mt; + final Recipe recipe; + + public Key(MethodType mt, Recipe recipe) { + this.mt = mt; + this.recipe = recipe; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Key key = (Key) o; + + if (!mt.equals(key.mt)) return false; + if (!recipe.equals(key.recipe)) return false; + return true; + } + + @Override + public int hashCode() { + int result = mt.hashCode(); + result = 31 * result + recipe.hashCode(); + return result; + } + } + + /** + * Parses the recipe string, and produces the traversable collection of + * {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator + * strategies. Notably, this class parses out the constants from the recipe + * and from other static arguments. + */ + private static final class Recipe { + private final List<RecipeElement> elements; + private final List<RecipeElement> elementsRev; + + public Recipe(String src, Object[] constants) { + List<RecipeElement> el = new ArrayList<>(); + + int constC = 0; + int argC = 0; + + StringBuilder acc = new StringBuilder(); + + for (int i = 0; i < src.length(); i++) { + char c = src.charAt(i); + + if (c == TAG_CONST || c == TAG_ARG) { + // Detected a special tag, flush all accumulated characters + // as a constant first: + if (acc.length() > 0) { + el.add(new RecipeElement(acc.toString())); + acc.setLength(0); + } + if (c == TAG_CONST) { + Object cnst = constants[constC++]; + el.add(new RecipeElement(cnst)); + } + if (c == TAG_ARG) { + el.add(new RecipeElement(argC++)); + } + } else { + // Not a special characters, this is a constant embedded into + // the recipe itself. + acc.append(c); + } + } + + // Flush the remaining characters as constant: + if (acc.length() > 0) { + el.add(new RecipeElement(acc.toString())); + } + + elements = new ArrayList<>(el); + Collections.reverse(el); + elementsRev = el; + } + + public Collection<RecipeElement> getElements() { + return elements; + } + + public Collection<RecipeElement> getElementsReversed() { + return elementsRev; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Recipe recipe = (Recipe) o; + return elements.equals(recipe.elements); + } + + @Override + public int hashCode() { + return elements.hashCode(); + } + } + + private static final class RecipeElement { + private final Object value; + private final int argPos; + private final Tag tag; + + public RecipeElement(Object cnst) { + this.value = Objects.requireNonNull(cnst); + this.argPos = -1; + this.tag = Tag.CONST; + } + + public RecipeElement(int arg) { + this.value = null; + this.argPos = arg; + this.tag = Tag.ARG; + } + + public Object getValue() { + assert (tag == Tag.CONST); + return value; + } + + public int getArgPos() { + assert (tag == Tag.ARG); + return argPos; + } + + public Tag getTag() { + return tag; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RecipeElement that = (RecipeElement) o; + + if (tag != that.tag) return false; + if (tag == Tag.CONST && (!value.equals(that.value))) return false; + if (tag == Tag.ARG && (argPos != that.argPos)) return false; + return true; + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + } + + private enum Tag { + CONST, ARG + } + + /** + * Facilitates the creation of optimized String concatenation methods, that + * can be used to efficiently concatenate a known number of arguments of + * known types, possibly after type adaptation and partial evaluation of + * arguments. Typically used as a <em>bootstrap method</em> for {@code + * invokedynamic} call sites, to support the <em>string concatenation</em> + * feature of the Java Programming Language. + * + * <p>When the target of the {@code CallSite} returned from this method is + * invoked, it returns the result of String concatenation, taking all + * function arguments passed to the linkage method as inputs for + * concatenation. The target signature is given by {@code concatType}. + * The arguments are concatenated as per requirements stated in JLS 15.18.1 + * "String Concatenation Operator +". Notably, the inputs are converted as + * per JLS 5.1.11 "String Conversion", and combined from left to right. + * + * <p>Assume the linkage arguments are as follows: + * + * <ul> + * <li>{@code concatType}, describing the {@code CallSite} signature</li> + * </ul> + * + * <p>Then the following linkage invariants must hold: + * + * <ul> + * <li>The parameter count in {@code concatType} is less than or equal to 200</li> + * + * <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li> + * </ul> + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code + * invokedynamic}, this is stacked automatically by the VM. + * @param name The name of the method to implement. This name is + * arbitrary, and has no meaning for this linkage method. + * When used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param concatType The expected signature of the {@code CallSite}. The + * parameter types represent the types of concatenation + * arguments; the return type is always assignable from {@link + * java.lang.String}. When used with {@code invokedynamic}, + * this is provided by the {@code NameAndType} of the {@code + * InvokeDynamic} structure and is stacked automatically by + * the VM. + * @return a CallSite whose target can be used to perform String + * concatenation, with dynamic concatenation arguments described by the given + * {@code concatType}. + * @throws StringConcatException If any of the linkage invariants described + * here are violated. + * @throws NullPointerException If any of the incoming arguments is null. + * This will never happen when a bootstrap method + * is called with invokedynamic. + * + * @jls 5.1.11 String Conversion + * @jls 15.18.1 String Concatenation Operator + + */ + public static CallSite makeConcat(MethodHandles.Lookup lookup, + String name, + MethodType concatType) throws StringConcatException { + if (DEBUG) { + System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType); + } + + return doStringConcat(lookup, name, concatType, true, null); + } + + /** + * Facilitates the creation of optimized String concatenation methods, that + * can be used to efficiently concatenate a known number of arguments of + * known types, possibly after type adaptation and partial evaluation of + * arguments. Typically used as a <em>bootstrap method</em> for {@code + * invokedynamic} call sites, to support the <em>string concatenation</em> + * feature of the Java Programming Language. + * + * <p>When the target of the {@code CallSite} returned from this method is + * invoked, it returns the result of String concatenation, taking all + * function arguments and constants passed to the linkage method as inputs for + * concatenation. The target signature is given by {@code concatType}, and + * does not include constants. The arguments are concatenated as per requirements + * stated in JLS 15.18.1 "String Concatenation Operator +". Notably, the inputs + * are converted as per JLS 5.1.11 "String Conversion", and combined from left + * to right. + * + * <p>The concatenation <em>recipe</em> is a String description for the way to + * construct a concatenated String from the arguments and constants. The + * recipe is processed from left to right, and each character represents an + * input to concatenation. Recipe characters mean: + * + * <ul> + * + * <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This + * input is passed through dynamic argument, and is provided during the + * concatenation method invocation. This input can be null.</li> + * + * <li><em>\2 (Unicode point 0002):</em> a constant. This input passed + * through static bootstrap argument. This constant can be any value + * representable in constant pool. If necessary, the factory would call + * {@code toString} to perform a one-time String conversion.</li> + * + * <li><em>Any other char value:</em> a single character constant.</li> + * </ul> + * + * <p>Assume the linkage arguments are as follows: + * + * <ul> + * <li>{@code concatType}, describing the {@code CallSite} signature</li> + * <li>{@code recipe}, describing the String recipe</li> + * <li>{@code constants}, the vararg array of constants</li> + * </ul> + * + * <p>Then the following linkage invariants must hold: + * + * <ul> + * <li>The parameter count in {@code concatType} is less than or equal to + * 200</li> + * + * <li>The parameter count in {@code concatType} equals to number of \1 tags + * in {@code recipe}</li> + * + * <li>The return type in {@code concatType} is assignable + * from {@link java.lang.String}, and matches the return type of the + * returned {@link MethodHandle}</li> + * + * <li>The number of elements in {@code constants} equals to number of \2 + * tags in {@code recipe}</li> + * </ul> + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code + * invokedynamic}, this is stacked automatically by the + * VM. + * @param name The name of the method to implement. This name is + * arbitrary, and has no meaning for this linkage method. + * When used with {@code invokedynamic}, this is provided + * by the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param concatType The expected signature of the {@code CallSite}. The + * parameter types represent the types of dynamic concatenation + * arguments; the return type is always assignable from {@link + * java.lang.String}. When used with {@code + * invokedynamic}, this is provided by the {@code + * NameAndType} of the {@code InvokeDynamic} structure and + * is stacked automatically by the VM. + * @param recipe Concatenation recipe, described above. + * @param constants A vararg parameter representing the constants passed to + * the linkage method. + * @return a CallSite whose target can be used to perform String + * concatenation, with dynamic concatenation arguments described by the given + * {@code concatType}. + * @throws StringConcatException If any of the linkage invariants described + * here are violated. + * @throws NullPointerException If any of the incoming arguments is null, or + * any constant in {@code recipe} is null. + * This will never happen when a bootstrap method + * is called with invokedynamic. + * @apiNote Code generators have three distinct ways to process a constant + * string operand S in a string concatenation expression. First, S can be + * materialized as a reference (using ldc) and passed as an ordinary argument + * (recipe '\1'). Or, S can be stored in the constant pool and passed as a + * constant (recipe '\2') . Finally, if S contains neither of the recipe + * tag characters ('\1', '\2') then S can be interpolated into the recipe + * itself, causing its characters to be inserted into the result. + * + * @jls 5.1.11 String Conversion + * @jls 15.18.1 String Concatenation Operator + + */ + public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup, + String name, + MethodType concatType, + String recipe, + Object... constants) throws StringConcatException { + if (DEBUG) { + System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants)); + } + + return doStringConcat(lookup, name, concatType, false, recipe, constants); + } + + private static CallSite doStringConcat(MethodHandles.Lookup lookup, + String name, + MethodType concatType, + boolean generateRecipe, + String recipe, + Object... constants) throws StringConcatException { + Objects.requireNonNull(lookup, "Lookup is null"); + Objects.requireNonNull(name, "Name is null"); + Objects.requireNonNull(concatType, "Concat type is null"); + Objects.requireNonNull(constants, "Constants are null"); + + for (Object o : constants) { + Objects.requireNonNull(o, "Cannot accept null constants"); + } + + int cCount = 0; + int oCount = 0; + if (generateRecipe) { + // Mock the recipe to reuse the concat generator code + char[] value = new char[concatType.parameterCount()]; + Arrays.fill(value, TAG_ARG); + recipe = new String(value); + oCount = concatType.parameterCount(); + } else { + Objects.requireNonNull(recipe, "Recipe is null"); + + for (int i = 0; i < recipe.length(); i++) { + char c = recipe.charAt(i); + if (c == TAG_CONST) cCount++; + if (c == TAG_ARG) oCount++; + } + } + + if (oCount != concatType.parameterCount()) { + throw new StringConcatException( + "Mismatched number of concat arguments: recipe wants " + + oCount + + " arguments, but signature provides " + + concatType.parameterCount()); + } + + if (cCount != constants.length) { + throw new StringConcatException( + "Mismatched number of concat constants: recipe wants " + + cCount + + " constants, but only " + + constants.length + + " are passed"); + } + + if (!concatType.returnType().isAssignableFrom(String.class)) { + throw new StringConcatException( + "The return type should be compatible with String, but it is " + + concatType.returnType()); + } + + if (concatType.parameterCount() > MAX_INDY_CONCAT_ARG_SLOTS) { + throw new StringConcatException("Too many concat argument slots: " + + concatType.parameterCount() + + ", can only accept " + + MAX_INDY_CONCAT_ARG_SLOTS); + } + + MethodType mt = adaptType(concatType); + + Recipe rec = new Recipe(recipe, constants); + + MethodHandle mh; + if (CACHE_ENABLE) { + Key key = new Key(mt, rec); + mh = CACHE.get(key); + if (mh == null) { + mh = generate(lookup, mt, rec); + CACHE.put(key, mh); + } + } else { + mh = generate(lookup, mt, rec); + } + return new ConstantCallSite(mh.asType(concatType)); + } + + /** + * Adapt method type to an API we are going to use. + * + * This strips the concrete classes from the signatures, thus preventing + * class leakage when we cache the concatenation stubs. + * + * @param args actual argument types + * @return argument types the strategy is going to use + */ + private static MethodType adaptType(MethodType args) { + Class<?>[] ptypes = args.parameterArray(); + boolean changed = false; + for (int i = 0; i < ptypes.length; i++) { + Class<?> ptype = ptypes[i]; + if (!ptype.isPrimitive() && + ptype != String.class && + ptype != Object.class) { // truncate to Object + ptypes[i] = Object.class; + changed = true; + } + // else other primitives or String or Object (unchanged) + } + return changed + ? MethodType.methodType(args.returnType(), ptypes) + : args; + } + + private static MethodHandle generate(Lookup lookup, MethodType mt, Recipe recipe) throws StringConcatException { + try { + switch (STRATEGY) { + case BC_SB: + return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.DEFAULT); + case BC_SB_SIZED: + return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.SIZED); + case BC_SB_SIZED_EXACT: + return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.SIZED_EXACT); + case MH_SB_SIZED: + return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED); + case MH_SB_SIZED_EXACT: + return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT); + case MH_INLINE_SIZED_EXACT: + return MethodHandleInlineCopyStrategy.generate(mt, recipe); + default: + throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented"); + } + } catch (Throwable t) { + throw new StringConcatException("Generator failed", t); + } + } + + private enum Mode { + DEFAULT(false, false), + SIZED(true, false), + SIZED_EXACT(true, true); + + private final boolean sized; + private final boolean exact; + + Mode(boolean sized, boolean exact) { + this.sized = sized; + this.exact = exact; + } + + boolean isSized() { + return sized; + } + + boolean isExact() { + return exact; + } + } + + /** + * Bytecode StringBuilder strategy. + * + * <p>This strategy operates in three modes, gated by {@link Mode}. + * + * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b> + * + * <p>This strategy spins up the bytecode that has the same StringBuilder + * chain javac would otherwise emit. This strategy uses only the public API, + * and comes as the baseline for the current JDK behavior. On other words, + * this strategy moves the javac generated bytecode to runtime. The + * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with + * the caller class coming from the BSM -- in other words, the protection + * guarantees are inherited from the method where invokedynamic was + * originally called. This means, among other things, that the bytecode is + * verified for all non-JDK uses. + * + * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but + * sized".</b> + * + * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also + * tries to guess the capacity required for StringBuilder to accept all + * arguments without resizing. This strategy only makes an educated guess: + * it only guesses the space required for known types (e.g. primitives and + * Strings), but does not otherwise convert arguments. Therefore, the + * capacity estimate may be wrong, and StringBuilder may have to + * transparently resize or trim when doing the actual concatenation. While + * this does not constitute a correctness issue (in the end, that what BC_SB + * has to do anyway), this does pose a potential performance problem. + * + * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but + * sized exactly".</b> + * + * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first + * converting all arguments to String in order to get the exact capacity + * StringBuilder should have. The conversion is done via the public + * String.valueOf and/or Object.toString methods, and does not touch any + * private String API. + */ + private static final class BytecodeStringBuilderStrategy { + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + static final int CLASSFILE_VERSION = 52; + static final String NAME_FACTORY = "concat"; + static final String CLASS_NAME = "java/lang/String$Concat"; + + private BytecodeStringBuilderStrategy() { + // no instantiation + } + + private static MethodHandle generate(MethodHandles.Lookup lookup, MethodType args, Recipe recipe, Mode mode) throws Exception { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); + + cw.visit(CLASSFILE_VERSION, + ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, + CLASS_NAME, + null, + "java/lang/Object", + null + ); + + MethodVisitor mv = cw.visitMethod( + ACC_PUBLIC + ACC_STATIC + ACC_FINAL, + NAME_FACTORY, + args.toMethodDescriptorString(), + null, + null); + + mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true); + mv.visitCode(); + + Class<?>[] arr = args.parameterArray(); + boolean[] guaranteedNonNull = new boolean[arr.length]; + + if (mode.isExact()) { + /* + In exact mode, we need to convert all arguments to their String representations, + as this allows to compute their String sizes exactly. We cannot use private + methods for primitives in here, therefore we need to convert those as well. + + We also record what arguments are guaranteed to be non-null as the result + of the conversion. String.valueOf does the null checks for us. The only + corner case to take care of is String.valueOf(Object) returning null itself. + + Also, if any conversion happened, then the slot indices in the incoming + arguments are not equal to the final local maps. The only case this may break + is when converting 2-slot long/double argument to 1-slot String. Therefore, + we get away with tracking modified offset, since no conversion can overwrite + the upcoming the argument. + */ + + int off = 0; + int modOff = 0; + for (int c = 0; c < arr.length; c++) { + Class<?> cl = arr[c]; + if (cl == String.class) { + if (off != modOff) { + mv.visitIntInsn(getLoadOpcode(cl), off); + mv.visitIntInsn(ASTORE, modOff); + } + } else { + mv.visitIntInsn(getLoadOpcode(cl), off); + mv.visitMethodInsn( + INVOKESTATIC, + "java/lang/String", + "valueOf", + getStringValueOfDesc(cl), + false + ); + mv.visitIntInsn(ASTORE, modOff); + arr[c] = String.class; + guaranteedNonNull[c] = cl.isPrimitive(); + } + off += getParameterSize(cl); + modOff += getParameterSize(String.class); + } + } + + if (mode.isSized()) { + /* + When operating in sized mode (this includes exact mode), it makes sense to make + StringBuilder append chains look familiar to OptimizeStringConcat. For that, we + need to do null-checks early, not make the append chain shape simpler. + */ + + int off = 0; + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + // Guaranteed non-null, no null check required. + break; + } + case ARG: { + // Null-checks are needed only for String arguments, and when a previous stage + // did not do implicit null-checks. If a String is null, we eagerly replace it + // with "null" constant. Note, we omit Objects here, because we don't call + // .length() on them down below. + int ac = el.getArgPos(); + Class<?> cl = arr[ac]; + if (cl == String.class && !guaranteedNonNull[ac]) { + Label l0 = new Label(); + mv.visitIntInsn(ALOAD, off); + mv.visitJumpInsn(IFNONNULL, l0); + mv.visitLdcInsn("null"); + mv.visitIntInsn(ASTORE, off); + mv.visitLabel(l0); + } + off += getParameterSize(cl); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + } + + // Prepare StringBuilder instance + mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); + mv.visitInsn(DUP); + + if (mode.isSized()) { + /* + Sized mode requires us to walk through the arguments, and estimate the final length. + In exact mode, this will operate on Strings only. This code would accumulate the + final length on stack. + */ + int len = 0; + int off = 0; + + mv.visitInsn(ICONST_0); + + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + len += cnst.toString().length(); + break; + } + case ARG: { + /* + If an argument is String, then we can call .length() on it. Sized/Exact modes have + converted arguments for us. If an argument is primitive, we can provide a guess + for its String representation size. + */ + Class<?> cl = arr[el.getArgPos()]; + if (cl == String.class) { + mv.visitIntInsn(ALOAD, off); + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/String", + "length", + "()I", + false + ); + mv.visitInsn(IADD); + } else if (cl.isPrimitive()) { + len += estimateSize(cl); + } + off += getParameterSize(cl); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + + // Constants have non-zero length, mix in + if (len > 0) { + iconst(mv, len); + mv.visitInsn(IADD); + } + + mv.visitMethodInsn( + INVOKESPECIAL, + "java/lang/StringBuilder", + "<init>", + "(I)V", + false + ); + } else { + mv.visitMethodInsn( + INVOKESPECIAL, + "java/lang/StringBuilder", + "<init>", + "()V", + false + ); + } + + // At this point, we have a blank StringBuilder on stack, fill it in with .append calls. + { + int off = 0; + for (RecipeElement el : recipe.getElements()) { + String desc; + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + mv.visitLdcInsn(cnst); + desc = getSBAppendDesc(cnst.getClass()); + break; + } + case ARG: { + Class<?> cl = arr[el.getArgPos()]; + mv.visitVarInsn(getLoadOpcode(cl), off); + off += getParameterSize(cl); + desc = getSBAppendDesc(cl); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "append", + desc, + false + ); + } + } + + if (DEBUG && mode.isExact()) { + /* + Exactness checks compare the final StringBuilder.capacity() with a resulting + String.length(). If these values disagree, that means StringBuilder had to perform + storage trimming, which defeats the purpose of exact strategies. + */ + + mv.visitInsn(DUP); + + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "capacity", + "()I", + false + ); + + mv.visitIntInsn(ISTORE, 0); + + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "toString", + "()Ljava/lang/String;", + false + ); + + mv.visitInsn(DUP); + + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/String", + "length", + "()I", + false + ); + + mv.visitIntInsn(ILOAD, 0); + + Label l0 = new Label(); + mv.visitJumpInsn(IF_ICMPEQ, l0); + + mv.visitTypeInsn(NEW, "java/lang/AssertionError"); + mv.visitInsn(DUP); + mv.visitLdcInsn("Failed exactness check"); + mv.visitMethodInsn(INVOKESPECIAL, + "java/lang/AssertionError", + "<init>", + "(Ljava/lang/Object;)V", + false); + mv.visitInsn(ATHROW); + + mv.visitLabel(l0); + } else { + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "toString", + "()Ljava/lang/String;", + false + ); + } + + mv.visitInsn(ARETURN); + + mv.visitMaxs(-1, -1); + mv.visitEnd(); + cw.visitEnd(); + + Class<?> targetClass = lookup.lookupClass(); + final byte[] classBytes = cw.toByteArray(); + final Class<?> innerClass = UNSAFE.defineAnonymousClass(targetClass, classBytes, null); + + try { + UNSAFE.ensureClassInitialized(innerClass); + return lookup.findStatic(innerClass, NAME_FACTORY, args); + } catch (ReflectiveOperationException e) { + throw new StringConcatException("Exception finding constructor", e); + } + } + + private static String getSBAppendDesc(Class<?> cl) { + if (cl.isPrimitive()) { + if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) { + return "(I)Ljava/lang/StringBuilder;"; + } else if (cl == Boolean.TYPE) { + return "(Z)Ljava/lang/StringBuilder;"; + } else if (cl == Character.TYPE) { + return "(C)Ljava/lang/StringBuilder;"; + } else if (cl == Double.TYPE) { + return "(D)Ljava/lang/StringBuilder;"; + } else if (cl == Float.TYPE) { + return "(F)Ljava/lang/StringBuilder;"; + } else if (cl == Long.TYPE) { + return "(J)Ljava/lang/StringBuilder;"; + } else { + throw new IllegalStateException("Unhandled primitive StringBuilder.append: " + cl); + } + } else if (cl == String.class) { + return "(Ljava/lang/String;)Ljava/lang/StringBuilder;"; + } else { + return "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"; + } + } + + private static String getStringValueOfDesc(Class<?> cl) { + if (cl.isPrimitive()) { + if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) { + return "(I)Ljava/lang/String;"; + } else if (cl == Boolean.TYPE) { + return "(Z)Ljava/lang/String;"; + } else if (cl == Character.TYPE) { + return "(C)Ljava/lang/String;"; + } else if (cl == Double.TYPE) { + return "(D)Ljava/lang/String;"; + } else if (cl == Float.TYPE) { + return "(F)Ljava/lang/String;"; + } else if (cl == Long.TYPE) { + return "(J)Ljava/lang/String;"; + } else { + throw new IllegalStateException("Unhandled String.valueOf: " + cl); + } + } else if (cl == String.class) { + return "(Ljava/lang/String;)Ljava/lang/String;"; + } else { + return "(Ljava/lang/Object;)Ljava/lang/String;"; + } + } + + /** + * The following method is copied from + * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small + * and fast Java bytecode manipulation framework. + * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. + */ + private static void iconst(MethodVisitor mv, final int cst) { + if (cst >= -1 && cst <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + cst); + } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, cst); + } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, cst); + } else { + mv.visitLdcInsn(cst); + } + } + + private static int getLoadOpcode(Class<?> c) { + if (c == Void.TYPE) { + throw new InternalError("Unexpected void type of load opcode"); + } + return ILOAD + getOpcodeOffset(c); + } + + private static int getOpcodeOffset(Class<?> c) { + if (c.isPrimitive()) { + if (c == Long.TYPE) { + return 1; + } else if (c == Float.TYPE) { + return 2; + } else if (c == Double.TYPE) { + return 3; + } + return 0; + } else { + return 4; + } + } + + private static int getParameterSize(Class<?> c) { + if (c == Void.TYPE) { + return 0; + } else if (c == Long.TYPE || c == Double.TYPE) { + return 2; + } + return 1; + } + } + + /** + * MethodHandle StringBuilder strategy. + * + * <p>This strategy operates in two modes, gated by {@link Mode}. + * + * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder, + * sized".</b> + * + * <p>This strategy avoids spinning up the bytecode by building the + * computation on MethodHandle combinators. The computation is built with + * public MethodHandle APIs, resolved from a public Lookup sequence, and + * ends up calling the public StringBuilder API. Therefore, this strategy + * does not use any private API at all, even the Unsafe.defineAnonymousClass, + * since everything is handled under cover by java.lang.invoke APIs. + * + * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, + * sized exactly".</b> + * + * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first + * converting all arguments to String in order to get the exact capacity + * StringBuilder should have. The conversion is done via the public + * String.valueOf and/or Object.toString methods, and does not touch any + * private String API. + */ + private static final class MethodHandleStringBuilderStrategy { + + private MethodHandleStringBuilderStrategy() { + // no instantiation + } + + private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception { + int pc = mt.parameterCount(); + + Class<?>[] ptypes = mt.parameterArray(); + MethodHandle[] filters = new MethodHandle[ptypes.length]; + for (int i = 0; i < ptypes.length; i++) { + MethodHandle filter; + switch (mode) { + case SIZED: + // In sized mode, we convert all references and floats/doubles + // to String: there is no specialization for different + // classes in StringBuilder API, and it will convert to + // String internally anyhow. + filter = Stringifiers.forMost(ptypes[i]); + break; + case SIZED_EXACT: + // In exact mode, we convert everything to String: + // this helps to compute the storage exactly. + filter = Stringifiers.forAny(ptypes[i]); + break; + default: + throw new StringConcatException("Not supported"); + } + if (filter != null) { + filters[i] = filter; + ptypes[i] = filter.type().returnType(); + } + } + + List<Class<?>> ptypesList = Arrays.asList(ptypes); + MethodHandle[] lengthers = new MethodHandle[pc]; + + // Figure out lengths: constants' lengths can be deduced on the spot. + // All reference arguments were filtered to String in the combinators below, so we can + // call the usual String.length(). Primitive values string sizes can be estimated. + int initial = 0; + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + initial += cnst.toString().length(); + break; + } + case ARG: { + final int i = el.getArgPos(); + Class<?> type = ptypesList.get(i); + if (type.isPrimitive()) { + MethodHandle est = MethodHandles.constant(int.class, estimateSize(type)); + est = MethodHandles.dropArguments(est, 0, type); + lengthers[i] = est; + } else { + lengthers[i] = STRING_LENGTH; + } + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + + // Create (StringBuilder, <args>) shape for appending: + MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList); + + // Compose append calls. This is done in reverse because the application order is + // reverse as well. + for (RecipeElement el : recipe.getElementsReversed()) { + MethodHandle appender; + switch (el.getTag()) { + case CONST: { + Object constant = el.getValue(); + MethodHandle mh = appender(adaptToStringBuilder(constant.getClass())); + appender = MethodHandles.insertArguments(mh, 1, constant); + break; + } + case ARG: { + int ac = el.getArgPos(); + appender = appender(ptypesList.get(ac)); + + // Insert dummy arguments to match the prefix in the signature. + // The actual appender argument will be the ac-ith argument. + if (ac != 0) { + appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac)); + } + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + builder = MethodHandles.foldArguments(builder, appender); + } + + // Build the sub-tree that adds the sizes and produces a StringBuilder: + + // a) Start with the reducer that accepts all arguments, plus one + // slot for the initial value. Inject the initial value right away. + // This produces (<ints>)int shape: + MethodHandle sum = getReducerFor(pc + 1); + MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial); + + // b) Apply lengthers to transform arguments to lengths, producing (<args>)int + adder = MethodHandles.filterArguments(adder, 0, lengthers); + + // c) Instantiate StringBuilder (<args>)int -> (<args>)StringBuilder + MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER); + + // d) Fold in StringBuilder constructor, this produces (<args>)StringBuilder + MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder); + + // Convert non-primitive arguments to Strings + mh = MethodHandles.filterArguments(mh, 0, filters); + + // Convert (<args>)StringBuilder to (<args>)String + if (DEBUG && mode.isExact()) { + mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING_CHECKED); + } else { + mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING); + } + + return mh; + } + + private static MethodHandle getReducerFor(int cnt) { + return SUMMERS.computeIfAbsent(cnt, SUMMER); + } + + private static MethodHandle appender(Class<?> appendType) { + MethodHandle appender = lookupVirtual(MethodHandles.publicLookup(), StringBuilder.class, "append", + StringBuilder.class, adaptToStringBuilder(appendType)); + + // appenders should return void, this would not modify the target signature during folding + MethodType nt = MethodType.methodType(void.class, StringBuilder.class, appendType); + return appender.asType(nt); + } + + private static String toStringChecked(StringBuilder sb) { + String s = sb.toString(); + if (s.length() != sb.capacity()) { + throw new AssertionError("Exactness check failed: result length = " + s.length() + ", buffer capacity = " + sb.capacity()); + } + return s; + } + + private static int sum(int v1, int v2) { + return v1 + v2; + } + + private static int sum(int v1, int v2, int v3) { + return v1 + v2 + v3; + } + + private static int sum(int v1, int v2, int v3, int v4) { + return v1 + v2 + v3 + v4; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5) { + return v1 + v2 + v3 + v4 + v5; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) { + return v1 + v2 + v3 + v4 + v5 + v6; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) { + return v1 + v2 + v3 + v4 + v5 + v6 + v7; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) { + return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8; + } + + private static int sum(int initial, int[] vs) { + int sum = initial; + for (int v : vs) { + sum += v; + } + return sum; + } + + private static final ConcurrentMap<Integer, MethodHandle> SUMMERS; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() { + @Override + public MethodHandle apply(Integer cnt) { + if (cnt == 1) { + return MethodHandles.identity(int.class); + } else if (cnt <= 8) { + // Variable-arity collectors are not as efficient as small-count methods, + // unroll some initial sizes. + Class<?>[] cls = new Class<?>[cnt]; + Arrays.fill(cls, int.class); + return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); + } else { + return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) + .asCollector(int[].class, cnt - 1); + } + } + }; + + private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED; + + static { + SUMMERS = new ConcurrentHashMap<>(); + Lookup publicLookup = MethodHandles.publicLookup(); + NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class); + STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class); + BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class); + if (DEBUG) { + BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP, + MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class); + } else { + BUILDER_TO_STRING_CHECKED = null; + } + } + + } + + + /** + * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline, + * sized exactly".</b> + * + * <p>This strategy replicates what StringBuilders are doing: it builds the + * byte[] array on its own and passes that byte[] array to String + * constructor. This strategy requires access to some private APIs in JDK, + * most notably, the read-only Integer/Long.stringSize methods that measure + * the character length of the integers, and the private String constructor + * that accepts byte[] arrays without copying. While this strategy assumes a + * particular implementation details for String, this opens the door for + * building a very optimal concatenation sequence. This is the only strategy + * that requires porting if there are private JDK changes occur. + */ + private static final class MethodHandleInlineCopyStrategy { + + private MethodHandleInlineCopyStrategy() { + // no instantiation + } + + static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { + + // Create filters and obtain filtered parameter types. Filters would be used in the beginning + // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings). + // The filtered argument type list is used all over in the combinators below. + Class<?>[] ptypes = mt.parameterArray(); + MethodHandle[] filters = null; + for (int i = 0; i < ptypes.length; i++) { + MethodHandle filter = Stringifiers.forMost(ptypes[i]); + if (filter != null) { + if (filters == null) { + filters = new MethodHandle[ptypes.length]; + } + filters[i] = filter; + ptypes[i] = filter.type().returnType(); + } + } + List<Class<?>> ptypesList = Arrays.asList(ptypes); + + // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes" + // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up, + // which makes the code arguably hard to read. + + // Drop all remaining parameter types, leave only helper arguments: + MethodHandle mh; + + mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); + mh = MethodHandles.dropArguments(mh, 0, int.class); + + // In debug mode, check that remaining index is zero. + if (DEBUG) { + mh = MethodHandles.filterArgument(mh, 0, CHECK_INDEX); + } + + // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already + // known from the combinators below. We are assembling the string backwards, so "index" is the + // *ending* index. + for (RecipeElement el : recipe.getElements()) { + MethodHandle prepender; + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst); + break; + } + case ARG: { + int pos = el.getArgPos(); + prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + + // Remove "old" index from arguments + mh = MethodHandles.dropArguments(mh, 1, int.class); + + // Do the prepend, and put "new" index at index 0 + mh = MethodHandles.foldArguments(mh, prepender); + } + + // Prepare the argument list for prepending. The tree below would instantiate + // the storage byte[] into argument 0, so we need to swap "storage" and "index". + // The index at this point equals to "size", and resides at argument 1. + { + MethodType nmt = mh.type() + .changeParameterType(0, byte[].class) + .changeParameterType(1, int.class); + mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount())); + } + + // Fold in byte[] instantiation at argument 0. + MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList); + mh = MethodHandles.foldArguments(mh, combiner); + + // Start combining length and coder mixers. + // + // Length is easy: constant lengths can be computed on the spot, and all non-constant + // shapes have been either converted to Strings, or explicit methods for getting the + // string length out of primitives are provided. + // + // Coders are more interesting. Only Object, String and char arguments (and constants) + // can have non-Latin1 encoding. It is easier to blindly convert constants to String, + // and deduce the coder from there. Arguments would be either converted to Strings + // during the initial filtering, or handled by primitive specializations in CODER_MIXERS. + // + // The method handle shape after all length and coder mixers is: + // (int, byte, <args>)String = ("index", "coder", <args>) + byte initialCoder = 0; // initial coder + int initialLen = 0; // initial length, in characters + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + Object constant = el.getValue(); + String s = constant.toString(); + initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s); + initialLen += s.length(); + break; + } + case ARG: { + int ac = el.getArgPos(); + + Class<?> argClass = ptypesList.get(ac); + MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac); + lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*) + lm = MethodHandles.dropArguments(lm, 2, byte.class); + + MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac); + cm = MethodHandles.dropArguments(cm, 0, int.class); // (**) + + // Read this bottom up: + + // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>) + mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class); + + // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>) + // Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*) + mh = MethodHandles.foldArguments(mh, lm); + + // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>) + // Coder mixer ignores the "old-index" arg due to dropArguments above (**) + mh = MethodHandles.foldArguments(mh, cm); + + // 1. The mh shape here is ("old-index", "old-coder", <args>) + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + + // Insert initial lengths and coders here. + // The method handle shape here is (<args>). + mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder); + + // Apply filters, converting the arguments: + if (filters != null) { + mh = MethodHandles.filterArguments(mh, 0, filters); + } + + return mh; + } + + private static int[] swap10(int count) { + int[] perm = new int[count]; + perm[0] = 1; + perm[1] = 0; + for (int i = 2; i < count; i++) { + perm[i] = i; + } + return perm; + } + + // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R + private static MethodHandle selectArgument(MethodHandle mh, int prefix, List<Class<?>> ptypes, int pos) { + if (pos == 0) { + return MethodHandles.dropArguments(mh, prefix + 1, ptypes.subList(1, ptypes.size())); + } else if (pos == ptypes.size() - 1) { + return MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, ptypes.size() - 1)); + } else { // 0 < pos < ptypes.size() - 1 + MethodHandle t = MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, pos)); + return MethodHandles.dropArguments(t, prefix + 1 + pos, ptypes.subList(pos + 1, ptypes.size())); + } + } + + @ForceInline + private static byte[] newArray(int length, byte coder) { + return new byte[length << coder]; + } + + @ForceInline + private static int checkIndex(int index) { + if (index != 0) { + throw new AssertionError("Exactness check failed: " + index + " characters left in the buffer."); + } + return index; + } + + private static MethodHandle prepender(Class<?> cl) { + return PREPENDERS.computeIfAbsent(cl, PREPEND); + } + + private static MethodHandle coderMixer(Class<?> cl) { + return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX); + } + + private static MethodHandle lengthMixer(Class<?> cl) { + return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX); + } + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() { + @Override + public MethodHandle apply(Class<?> c) { + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class, c); + } + }; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() { + @Override + public MethodHandle apply(Class<?> c) { + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c); + } + }; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() { + @Override + public MethodHandle apply(Class<?> c) { + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c); + } + }; + + private static final MethodHandle NEW_STRING; + private static final MethodHandle CHECK_INDEX; + private static final MethodHandle NEW_ARRAY; + private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS; + private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS; + private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS; + private static final Class<?> STRING_HELPER; + + static { + try { + STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + + PREPENDERS = new ConcurrentHashMap<>(); + LENGTH_MIXERS = new ConcurrentHashMap<>(); + CODER_MIXERS = new ConcurrentHashMap<>(); + + NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, byte.class); + NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class); + + if (DEBUG) { + CHECK_INDEX = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "checkIndex", int.class, int.class); + } else { + CHECK_INDEX = null; + } + } + } + + /** + * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally + * delegate to {@code String.valueOf}, depending on argument's type. + */ + private static final class Stringifiers { + private Stringifiers() { + // no instantiation + } + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function<Class<?>, MethodHandle> MOST = new Function<Class<?>, MethodHandle>() { + @Override + public MethodHandle apply(Class<?> cl) { + MethodHandle mhObject = lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, Object.class); + + // We need the additional conversion here, because String.valueOf(Object) may return null. + // String conversion rules in Java state we need to produce "null" String in this case. + // It can be easily done with applying valueOf the second time. + MethodHandle mhObjectNoNulls = MethodHandles.filterReturnValue(mhObject, + mhObject.asType(MethodType.methodType(String.class, String.class))); + + if (cl == String.class) { + return mhObject; + } else if (cl == float.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, float.class); + } else if (cl == double.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, double.class); + } else if (!cl.isPrimitive()) { + return mhObjectNoNulls; + } + + return null; + } + }; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function<Class<?>, MethodHandle> ANY = new Function<Class<?>, MethodHandle>() { + @Override + public MethodHandle apply(Class<?> cl) { + MethodHandle mh = MOST.apply(cl); + if (mh != null) { + return mh; + } + + if (cl == byte.class || cl == short.class || cl == int.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, int.class); + } else if (cl == boolean.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, boolean.class); + } else if (cl == char.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, char.class); + } else if (cl == long.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, long.class); + } else { + throw new IllegalStateException("Unknown class: " + cl); + } + } + }; + + private static final ConcurrentMap<Class<?>, MethodHandle> STRINGIFIERS_MOST = new ConcurrentHashMap<>(); + private static final ConcurrentMap<Class<?>, MethodHandle> STRINGIFIERS_ANY = new ConcurrentHashMap<>(); + + /** + * Returns a stringifier for references and floats/doubles only. + * Always returns null for other primitives. + * + * @param t class to stringify + * @return stringifier; null, if not available + */ + static MethodHandle forMost(Class<?> t) { + return STRINGIFIERS_MOST.computeIfAbsent(t, MOST); + } + + /** + * Returns a stringifier for any type. Never returns null. + * + * @param t class to stringify + * @return stringifier + */ + static MethodHandle forAny(Class<?> t) { + return STRINGIFIERS_ANY.computeIfAbsent(t, ANY); + } + } + + /* ------------------------------- Common utilities ------------------------------------ */ + + private static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) { + try { + return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) { + try { + return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) { + try { + return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static int estimateSize(Class<?> cl) { + if (cl == Integer.TYPE) { + return 11; // "-2147483648" + } else if (cl == Boolean.TYPE) { + return 5; // "false" + } else if (cl == Byte.TYPE) { + return 4; // "-128" + } else if (cl == Character.TYPE) { + return 1; // duh + } else if (cl == Short.TYPE) { + return 6; // "-32768" + } else if (cl == Double.TYPE) { + return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer + } else if (cl == Float.TYPE) { + return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer + } else if (cl == Long.TYPE) { + return 20; // "-9223372036854775808" + } else { + throw new IllegalArgumentException("Cannot estimate the size for " + cl); + } + } + + private static Class<?> adaptToStringBuilder(Class<?> c) { + if (c.isPrimitive()) { + if (c == Byte.TYPE || c == Short.TYPE) { + return int.class; + } + } else { + if (c != String.class) { + return Object.class; + } + } + return c; + } + + private StringConcatFactory() { + // no instantiation + } + +}
--- a/jdk/src/java.base/share/classes/java/net/URI.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/net/URI.java Wed Jul 05 21:16:36 2017 +0200 @@ -1080,11 +1080,8 @@ * If a protocol handler for the URL could not be found, * or if some other error occurred while constructing the URL */ - public URL toURL() - throws MalformedURLException { - if (!isAbsolute()) - throw new IllegalArgumentException("URI is not absolute"); - return new URL(toString()); + public URL toURL() throws MalformedURLException { + return URL.fromURI(this); } // -- Component access methods --
--- a/jdk/src/java.base/share/classes/java/net/URL.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/net/URL.java Wed Jul 05 21:16:36 2017 +0200 @@ -36,6 +36,7 @@ import java.io.ObjectStreamField; import java.io.ObjectInputStream.GetField; import java.util.Iterator; +import java.util.Locale; import java.util.NoSuchElementException; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; @@ -405,7 +406,7 @@ } } - protocol = protocol.toLowerCase(); + protocol = protocol.toLowerCase(Locale.ROOT); this.protocol = protocol; if (host != null) { @@ -579,8 +580,7 @@ for (i = start ; !aRef && (i < limit) && ((c = spec.charAt(i)) != '/') ; i++) { if (c == ':') { - - String s = spec.substring(start, i).toLowerCase(); + String s = spec.substring(start, i).toLowerCase(Locale.ROOT); if (isValidProtocol(s)) { newProtocol = s; start = i + 1; @@ -659,6 +659,44 @@ } } + /** + * Creates a URL from a URI, as if by invoking {@code uri.toURL()}. + * + * @see java.net.URI#toURL() + */ + static URL fromURI(URI uri) throws MalformedURLException { + if (!uri.isAbsolute()) { + throw new IllegalArgumentException("URI is not absolute"); + } + String protocol = uri.getScheme(); + + // In general we need to go via Handler.parseURL, but for the jrt + // protocol we enforce that the Handler is not overrideable and can + // optimize URI to URL conversion. + // + // Case-sensitive comparison for performance; malformed protocols will + // be handled correctly by the slow path. + if (protocol.equals("jrt") && !uri.isOpaque() + && uri.getRawFragment() == null) { + + String query = uri.getRawQuery(); + String path = uri.getRawPath(); + String file = (query == null) ? path : path + "?" + query; + + // URL represent undefined host as empty string while URI use null + String host = uri.getHost(); + if (host == null) { + host = ""; + } + + int port = uri.getPort(); + + return new URL("jrt", host, port, file, null); + } else { + return new URL((URL)null, uri.toString(), null); + } + } + /* * Returns true if specified string is a valid protocol name. */ @@ -1275,11 +1313,28 @@ } } - private static final String[] NON_OVERRIDEABLE_PROTOCOLS = {"file", "jrt"}; - private static boolean isOverrideable(String protocol) { - for (String p : NON_OVERRIDEABLE_PROTOCOLS) - if (protocol.equalsIgnoreCase(p)) + + /** + * Non-overrideable protocols: "jrt" and "file" + * + * Character-based comparison for performance reasons; also ensures + * case-insensitive comparison in a locale-independent fashion. + */ + static boolean isOverrideable(String protocol) { + if (protocol.length() == 3) { + if ((Character.toLowerCase(protocol.charAt(0)) == 'j') && + (Character.toLowerCase(protocol.charAt(1)) == 'r') && + (Character.toLowerCase(protocol.charAt(2)) == 't')) { return false; + } + } else if (protocol.length() == 4) { + if ((Character.toLowerCase(protocol.charAt(0)) == 'f') && + (Character.toLowerCase(protocol.charAt(1)) == 'i') && + (Character.toLowerCase(protocol.charAt(2)) == 'l') && + (Character.toLowerCase(protocol.charAt(3)) == 'e')) { + return false; + } + } return true; }
--- a/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java Wed Jul 05 21:16:36 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,7 @@ /** * Read the file owner. * - * <p> It it implementation specific if the file owner can be a {@link + * <p> It is implementation specific if the file owner can be a {@link * GroupPrincipal group}. * * @return the file owner @@ -78,7 +78,7 @@ /** * Updates the file owner. * - * <p> It it implementation specific if the file owner can be a {@link + * <p> It is 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.
--- a/jdk/src/java.base/share/classes/java/util/Map.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/util/Map.java Wed Jul 05 21:16:36 2017 +0200 @@ -649,7 +649,7 @@ try { k = entry.getKey(); v = entry.getValue(); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } @@ -704,7 +704,7 @@ try { k = entry.getKey(); v = entry.getValue(); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } @@ -714,7 +714,7 @@ try { entry.setValue(v); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } @@ -887,7 +887,7 @@ * or atomicity properties of this method. Any implementation providing * atomicity guarantees must override this method and document its * concurrency properties. - * + * * @param key key with which the specified value is associated * @param value value to be associated with the specified key * @return the previous value associated with the specified key, or @@ -984,6 +984,9 @@ * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) * @since 1.8 */ default V computeIfAbsent(K key, @@ -1058,6 +1061,9 @@ * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) * @since 1.8 */ default V computeIfPresent(K key, @@ -1103,7 +1109,7 @@ * <pre> {@code * V oldValue = map.get(key); * V newValue = remappingFunction.apply(key, oldValue); - * if (oldValue != null ) { + * if (oldValue != null) { * if (newValue != null) * map.put(key, newValue); * else @@ -1147,6 +1153,9 @@ * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) * @since 1.8 */ default V compute(K key, @@ -1239,6 +1248,9 @@ * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) * @throws NullPointerException if the specified key is null and this map * does not support null keys or the value or remappingFunction is * null @@ -1251,7 +1263,7 @@ V oldValue = get(key); V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); - if(newValue == null) { + if (newValue == null) { remove(key); } else { put(key, newValue);
--- a/jdk/src/java.base/share/classes/java/util/Queue.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/util/Queue.java Wed Jul 05 21:16:36 2017 +0200 @@ -129,14 +129,6 @@ * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>. * - * @see java.util.Collection - * @see LinkedList - * @see PriorityQueue - * @see java.util.concurrent.LinkedBlockingQueue - * @see java.util.concurrent.BlockingQueue - * @see java.util.concurrent.ArrayBlockingQueue - * @see java.util.concurrent.LinkedBlockingQueue - * @see java.util.concurrent.PriorityBlockingQueue * @since 1.5 * @author Doug Lea * @param <E> the type of elements held in this queue
--- a/jdk/src/java.base/share/classes/java/util/Vector.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/util/Vector.java Wed Jul 05 21:16:36 2017 +0200 @@ -233,42 +233,56 @@ public synchronized void ensureCapacity(int minCapacity) { if (minCapacity > 0) { modCount++; - ensureCapacityHelper(minCapacity); + if (minCapacity > elementData.length) + grow(minCapacity); } } /** - * This implements the unsynchronized semantics of ensureCapacity. - * Synchronized methods in this class can internally call this - * method for ensuring capacity without incurring the cost of an - * extra synchronization. - * - * @see #ensureCapacity(int) - */ - private void ensureCapacityHelper(int minCapacity) { - // overflow-conscious code - if (minCapacity - elementData.length > 0) - grow(minCapacity); - } - - /** - * The maximum size of array to allocate. + * The maximum size of array to allocate (unless necessary). * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - private void grow(int minCapacity) { + /** + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if minCapacity is less than zero + */ + private Object[] grow(int minCapacity) { + return elementData = Arrays.copyOf(elementData, + newCapacity(minCapacity)); + } + + private Object[] grow() { + return grow(elementCount + 1); + } + + /** + * Returns a capacity at least as large as the given minimum capacity. + * Will not return a capacity greater than MAX_ARRAY_SIZE unless + * the given minimum capacity is greater than MAX_ARRAY_SIZE. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if minCapacity is less than zero + */ + private int newCapacity(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); - if (newCapacity - minCapacity < 0) - newCapacity = minCapacity; - if (newCapacity - MAX_ARRAY_SIZE > 0) - newCapacity = hugeCapacity(minCapacity); - elementData = Arrays.copyOf(elementData, newCapacity); + if (newCapacity - minCapacity <= 0) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return minCapacity; + } + return (newCapacity - MAX_ARRAY_SIZE <= 0) + ? newCapacity + : hugeCapacity(minCapacity); } private static int hugeCapacity(int minCapacity) { @@ -290,13 +304,10 @@ */ public synchronized void setSize(int newSize) { modCount++; - if (newSize > elementCount) { - ensureCapacityHelper(newSize); - } else { - for (int i = newSize ; i < elementCount ; i++) { - elementData[i] = null; - } - } + if (newSize > elementData.length) + grow(newSize); + for (int i = newSize; i < elementCount; i++) + elementData[i] = null; elementCount = newSize; } @@ -604,11 +615,16 @@ throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); } - ensureCapacityHelper(elementCount + 1); - System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); + modCount++; + final int s = elementCount; + Object[] elementData = this.elementData; + if (s == elementData.length) + elementData = grow(); + System.arraycopy(elementData, index, + elementData, index + 1, + s - index); elementData[index] = obj; - modCount++; - elementCount++; + elementCount = s + 1; } /** @@ -623,9 +639,8 @@ * @param obj the component to be added */ public synchronized void addElement(E obj) { - ensureCapacityHelper(elementCount + 1); modCount++; - elementData[elementCount++] = obj; + add(obj, elementData, elementCount); } /** @@ -781,6 +796,18 @@ } /** + * This helper method split out from add(E) to keep method + * bytecode size under 35 (the -XX:MaxInlineSize default value), + * which helps when add(E) is called in a C1-compiled loop. + */ + private void add(E e, Object[] elementData, int s) { + if (s == elementData.length) + elementData = grow(); + elementData[s] = e; + elementCount = s + 1; + } + + /** * Appends the specified element to the end of this Vector. * * @param e element to be appended to this Vector @@ -788,9 +815,8 @@ * @since 1.2 */ public synchronized boolean add(E e) { - ensureCapacityHelper(elementCount + 1); modCount++; - elementData[elementCount++] = e; + add(e, elementData, elementCount); return true; } @@ -891,16 +917,19 @@ */ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); + modCount++; int numNew = a.length; - if (numNew > 0) { - synchronized (this) { - ensureCapacityHelper(elementCount + numNew); - System.arraycopy(a, 0, elementData, elementCount, numNew); - modCount++; - elementCount += numNew; - } + if (numNew == 0) + return false; + synchronized (this) { + Object[] elementData = this.elementData; + final int s = elementCount; + if (numNew > elementData.length - s) + elementData = grow(s + numNew); + System.arraycopy(a, 0, elementData, s, numNew); + elementCount = s + numNew; + return true; } - return numNew > 0; } /** @@ -969,21 +998,23 @@ throw new ArrayIndexOutOfBoundsException(index); Object[] a = c.toArray(); + modCount++; int numNew = a.length; + if (numNew == 0) + return false; + Object[] elementData = this.elementData; + final int s = elementCount; + if (numNew > elementData.length - s) + elementData = grow(s + numNew); - if (numNew > 0) { - ensureCapacityHelper(elementCount + numNew); - - int numMoved = elementCount - index; - if (numMoved > 0) - System.arraycopy(elementData, index, elementData, - index + numNew, numMoved); - - System.arraycopy(a, 0, elementData, index, numNew); - elementCount += numNew; - modCount++; - } - return numNew > 0; + int numMoved = s - index; + if (numMoved > 0) + System.arraycopy(elementData, index, + elementData, index + numNew, + numMoved); + System.arraycopy(a, 0, elementData, index, numNew); + elementCount = s + numNew; + return true; } /**
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java Wed Jul 05 21:16:36 2017 +0200 @@ -301,19 +301,15 @@ * * @implSpec * The default implementation is equivalent to the following steps for this - * {@code map}, then returning the current value or {@code null} if now - * absent: + * {@code map}: * * <pre> {@code - * if (map.get(key) == null) { - * V newValue = mappingFunction.apply(key); - * if (newValue != null) - * return map.putIfAbsent(key, newValue); - * }}</pre> - * - * The default implementation may retry these steps when multiple - * threads attempt updates including potentially calling the mapping - * function multiple times. + * V oldValue, newValue; + * return ((oldValue = map.get(key)) == null + * && (newValue = mappingFunction.apply(key)) != null + * && (oldValue = map.putIfAbsent(key, newValue)) == null) + * ? newValue + * : oldValue;}</pre> * * <p>This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -323,16 +319,19 @@ * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { Objects.requireNonNull(mappingFunction); - V v, newValue; - return ((v = get(key)) == null && - (newValue = mappingFunction.apply(key)) != null && - (v = putIfAbsent(key, newValue)) == null) ? newValue : v; + V oldValue, newValue; + return ((oldValue = get(key)) == null + && (newValue = mappingFunction.apply(key)) != null + && (oldValue = putIfAbsent(key, newValue)) == null) + ? newValue + : oldValue; } /** @@ -340,22 +339,19 @@ * * @implSpec * The default implementation is equivalent to performing the following - * steps for this {@code map}, then returning the current value or - * {@code null} if now absent: + * steps for this {@code map}: * * <pre> {@code - * if (map.get(key) != null) { - * V oldValue = map.get(key); + * for (V oldValue; (oldValue = map.get(key)) != null; ) { * V newValue = remappingFunction.apply(key, oldValue); - * if (newValue != null) - * map.replace(key, oldValue, newValue); - * else - * map.remove(key, oldValue); - * }}</pre> - * - * The default implementation may retry these steps when multiple threads - * attempt updates including potentially calling the remapping function - * multiple times. + * if ((newValue == null) + * ? map.remove(key, oldValue) + * : map.replace(key, oldValue, newValue)) + * return newValue; + * } + * return null;}</pre> + * When multiple threads attempt updates, map operations and the + * remapping function may be called multiple times. * * <p>This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -365,22 +361,21 @@ * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); - V oldValue; - while ((oldValue = get(key)) != null) { + for (V oldValue; (oldValue = get(key)) != null; ) { V newValue = remappingFunction.apply(key, oldValue); - if (newValue != null) { - if (replace(key, oldValue, newValue)) - return newValue; - } else if (remove(key, oldValue)) - return null; + if ((newValue == null) + ? remove(key, oldValue) + : replace(key, oldValue, newValue)) + return newValue; } - return oldValue; + return null; } /** @@ -388,27 +383,23 @@ * * @implSpec * The default implementation is equivalent to performing the following - * steps for this {@code map}, then returning the current value or - * {@code null} if absent: + * steps for this {@code map}: * * <pre> {@code - * V oldValue = map.get(key); - * V newValue = remappingFunction.apply(key, oldValue); - * if (oldValue != null ) { - * if (newValue != null) - * map.replace(key, oldValue, newValue); - * else - * map.remove(key, oldValue); - * } else { - * if (newValue != null) - * map.putIfAbsent(key, newValue); - * else + * for (;;) { + * V oldValue = map.get(key); + * V newValue = remappingFunction.apply(key, oldValue); + * if (newValue != null) { + * if ((oldValue != null) + * ? map.replace(key, oldValue, newValue) + * : map.putIfAbsent(key, newValue) == null) + * return newValue; + * } else if (oldValue == null || map.remove(key, oldValue)) { * return null; + * } * }}</pre> - * - * The default implementation may retry these steps when multiple - * threads attempt updates including potentially calling the remapping - * function multiple times. + * When multiple threads attempt updates, map operations and the + * remapping function may be called multiple times. * * <p>This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -418,50 +409,29 @@ * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override default V compute(K key, - BiFunction<? super K, ? super V, ? extends V> remappingFunction) { - Objects.requireNonNull(remappingFunction); - V oldValue = get(key); - for (;;) { - V newValue = remappingFunction.apply(key, oldValue); - if (newValue == null) { - // delete mapping - if (oldValue != null || containsKey(key)) { - // something to remove - if (remove(key, oldValue)) { - // removed the old value as expected - return null; + BiFunction<? super K, ? super V, ? extends V> remappingFunction) { + retry: for (;;) { + V oldValue = get(key); + // if putIfAbsent fails, opportunistically use its return value + haveOldValue: for (;;) { + V newValue = remappingFunction.apply(key, oldValue); + if (newValue != null) { + if (oldValue != null) { + if (replace(key, oldValue, newValue)) + return newValue; } - - // some other value replaced old value. try again. - oldValue = get(key); - } else { - // nothing to do. Leave things as they were. + else if ((oldValue = putIfAbsent(key, newValue)) == null) + return newValue; + else continue haveOldValue; + } else if (oldValue == null || remove(key, oldValue)) { return null; } - } else { - // add or replace old mapping - if (oldValue != null) { - // replace - if (replace(key, oldValue, newValue)) { - // replaced as expected. - return newValue; - } - - // some other value replaced old value. try again. - oldValue = get(key); - } else { - // add (replace if oldValue was null) - if ((oldValue = putIfAbsent(key, newValue)) == null) { - // replaced - return newValue; - } - - // some other value replaced old value. try again. - } + continue retry; } } } @@ -471,21 +441,25 @@ * * @implSpec * The default implementation is equivalent to performing the following - * steps for this {@code map}, then returning the current value or - * {@code null} if absent: + * steps for this {@code map}: * * <pre> {@code - * V oldValue = map.get(key); - * V newValue = (oldValue == null) ? value : - * remappingFunction.apply(oldValue, value); - * if (newValue == null) - * map.remove(key); - * else - * map.put(key, newValue);}</pre> - * - * <p>The default implementation may retry these steps when multiple - * threads attempt updates including potentially calling the remapping - * function multiple times. + * for (;;) { + * V oldValue = map.get(key); + * if (oldValue != null) { + * V newValue = remappingFunction.apply(oldValue, value); + * if (newValue != null) { + * if (map.replace(key, oldValue, newValue)) + * return newValue; + * } else if (map.remove(key, oldValue)) { + * return null; + * } + * } else if (map.putIfAbsent(key, value) == null) { + * return value; + * } + * }}</pre> + * When multiple threads attempt updates, map operations and the + * remapping function may be called multiple times. * * <p>This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -495,6 +469,7 @@ * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override @@ -502,20 +477,23 @@ BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); - V oldValue = get(key); - for (;;) { - if (oldValue != null) { - V newValue = remappingFunction.apply(oldValue, value); - if (newValue != null) { - if (replace(key, oldValue, newValue)) - return newValue; - } else if (remove(key, oldValue)) { - return null; - } - oldValue = get(key); - } else { - if ((oldValue = putIfAbsent(key, value)) == null) { - return value; + retry: for (;;) { + V oldValue = get(key); + // if putIfAbsent fails, opportunistically use its return value + haveOldValue: for (;;) { + if (oldValue != null) { + V newValue = remappingFunction.apply(oldValue, value); + if (newValue != null) { + if (replace(key, oldValue, newValue)) + return newValue; + } else if (remove(key, oldValue)) { + return null; + } + continue retry; + } else { + if ((oldValue = putIfAbsent(key, value)) == null) + return value; + continue haveOldValue; } } }
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Wed Jul 05 21:16:36 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,9 @@ // The number of temp buffers in our pool private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX; + // The max size allowed for a cached temp buffer, in bytes + private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize(); + // Per-thread cache of temporary direct buffers private static ThreadLocal<BufferCache> bufferCache = new ThreadLocal<BufferCache>() @@ -55,6 +58,52 @@ }; /** + * Returns the max size allowed for a cached temp buffers, in + * bytes. It defaults to Long.MAX_VALUE. It can be set with the + * jdk.nio.maxCachedBufferSize property. Even though + * ByteBuffer.capacity() returns an int, we're using a long here + * for potential future-proofing. + */ + private static long getMaxCachedBufferSize() { + String s = java.security.AccessController.doPrivileged( + new PrivilegedAction<String>() { + @Override + public String run() { + return System.getProperty("jdk.nio.maxCachedBufferSize"); + } + }); + if (s != null) { + try { + long m = Long.parseLong(s); + if (m >= 0) { + return m; + } else { + // if it's negative, ignore the system property + } + } catch (NumberFormatException e) { + // if the string is not well formed, ignore the system property + } + } + return Long.MAX_VALUE; + } + + /** + * Returns true if a buffer of this size is too large to be + * added to the buffer cache, false otherwise. + */ + private static boolean isBufferTooLarge(int size) { + return size > MAX_CACHED_BUFFER_SIZE; + } + + /** + * Returns true if the buffer is too large to be added to the + * buffer cache, false otherwise. + */ + private static boolean isBufferTooLarge(ByteBuffer buf) { + return isBufferTooLarge(buf.capacity()); + } + + /** * A simple cache of direct buffers. */ private static class BufferCache { @@ -80,6 +129,9 @@ * size (or null if no suitable buffer is found). */ ByteBuffer get(int size) { + // Don't call this if the buffer would be too large. + assert !isBufferTooLarge(size); + if (count == 0) return null; // cache is empty @@ -117,6 +169,9 @@ } boolean offerFirst(ByteBuffer buf) { + // Don't call this if the buffer is too large. + assert !isBufferTooLarge(buf); + if (count >= TEMP_BUF_POOL_SIZE) { return false; } else { @@ -128,6 +183,9 @@ } boolean offerLast(ByteBuffer buf) { + // Don't call this if the buffer is too large. + assert !isBufferTooLarge(buf); + if (count >= TEMP_BUF_POOL_SIZE) { return false; } else { @@ -156,6 +214,15 @@ * Returns a temporary buffer of at least the given size */ public static ByteBuffer getTemporaryDirectBuffer(int size) { + // If a buffer of this size is too large for the cache, there + // should not be a buffer in the cache that is at least as + // large. So we'll just create a new one. Also, we don't have + // to remove the buffer from the cache (as this method does + // below) given that we won't put the new buffer in the cache. + if (isBufferTooLarge(size)) { + return ByteBuffer.allocateDirect(size); + } + BufferCache cache = bufferCache.get(); ByteBuffer buf = cache.get(size); if (buf != null) { @@ -185,6 +252,13 @@ * likely to be returned by a subsequent call to getTemporaryDirectBuffer. */ static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { + // If the buffer is too large for the cache we don't have to + // check the cache. We'll just free it. + if (isBufferTooLarge(buf)) { + free(buf); + return; + } + assert buf != null; BufferCache cache = bufferCache.get(); if (!cache.offerFirst(buf)) { @@ -200,6 +274,13 @@ * cache in same order that they were obtained. */ static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { + // If the buffer is too large for the cache we don't have to + // check the cache. We'll just free it. + if (isBufferTooLarge(buf)) { + free(buf); + return; + } + assert buf != null; BufferCache cache = bufferCache.get(); if (!cache.offerLast(buf)) {
--- a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java Thu Jan 28 15:42:54 2016 -0800 +++ b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java Wed Jul 05 21:16:36 2017 +0200 @@ -59,7 +59,11 @@ AccessController.doPrivileged(new PrivilegedAction<>() { @Override public Object run() { - Thread thr = new Thread(null, thisRunnable, "FileSystemWatchService", 0, false); + Thread thr = new Thread(null, + thisRunnable, + "FileSystemWatchService", + 0, + false); thr.setDaemon(true); thr.start(); return null; @@ -216,11 +220,11 @@ throw new ClosedWatchServiceException(); } requestList.add(req); + + // wakeup thread + wakeup(); } - // wakeup thread - wakeup(); - // wait for result Object result = req.awaitResult(); @@ -244,6 +248,7 @@ // if in process of shutdown then reject request if (shutdown) { req.release(new ClosedWatchServiceException()); + continue; } switch (req.type()) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.*; + +import jdk.internal.joptsimple.internal.Reflection; +import jdk.internal.joptsimple.internal.ReflectionException; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * @param <V> represents the type of the arguments this option accepts + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +abstract class AbstractOptionSpec<V> implements OptionSpec<V>, OptionDescriptor { + private final List<String> options = new ArrayList<String>(); + private final String description; + private boolean forHelp; + + protected AbstractOptionSpec( String option ) { + this( singletonList( option ), EMPTY ); + } + + protected AbstractOptionSpec( Collection<String> options, String description ) { + arrangeOptions( options ); + + this.description = description; + } + + public final Collection<String> options() { + return unmodifiableList( options ); + } + + public final List<V> values( OptionSet detectedOptions ) { + return detectedOptions.valuesOf( this ); + } + + public final V value( OptionSet detectedOptions ) { + return detectedOptions.valueOf( this ); + } + + public String description() { + return description; + } + + public final AbstractOptionSpec<V> forHelp() { + forHelp = true; + return this; + } + + public final boolean isForHelp() { + return forHelp; + } + + public boolean representsNonOptions() { + return false; + } + + protected abstract V convert( String argument ); + + protected V convertWith( ValueConverter<V> converter, String argument ) { + try { + return Reflection.convertWith( converter, argument ); + } + catch ( ReflectionException ex ) { + throw new OptionArgumentConversionException( options(), argument, ex ); + } + catch ( ValueConversionException ex ) { + throw new OptionArgumentConversionException( options(), argument, ex ); + } + } + + protected String argumentTypeIndicatorFrom( ValueConverter<V> converter ) { + if ( converter == null ) + return null; + + String pattern = converter.valuePattern(); + return pattern == null ? converter.valueType().getName() : pattern; + } + + abstract void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ); + + private void arrangeOptions( Collection<String> unarranged ) { + if ( unarranged.size() == 1 ) { + options.addAll( unarranged ); + return; + } + + List<String> shortOptions = new ArrayList<String>(); + List<String> longOptions = new ArrayList<String>(); + + for ( String each : unarranged ) { + if ( each.length() == 1 ) + shortOptions.add( each ); + else + longOptions.add( each ); + } + + sort( shortOptions ); + sort( longOptions ); + + options.addAll( shortOptions ); + options.addAll( longOptions ); + } + + @Override + public boolean equals( Object that ) { + if ( !( that instanceof AbstractOptionSpec<?> ) ) + return false; + + AbstractOptionSpec<?> other = (AbstractOptionSpec<?>) that; + return options.equals( other.options ); + } + + @Override + public int hashCode() { + return options.hashCode(); + } + + @Override + public String toString() { + return options.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * Represents the <kbd>"-W"</kbd> form of long option specification. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class AlternativeLongOptionSpec extends ArgumentAcceptingOptionSpec<String> { + AlternativeLongOptionSpec() { + super( singletonList( RESERVED_FOR_EXTENSIONS ), true, "Alternative form of long options" ); + + describedAs( "opt=value" ); + } + + @Override + protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + if ( !arguments.hasMore() ) + throw new OptionMissingRequiredArgumentException( options() ); + + arguments.treatNextAsLongOption(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringTokenizer; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.internal.Objects.*; +import static jdk.internal.joptsimple.internal.Reflection.*; +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * <p>Specification of an option that accepts an argument.</p> + * + * <p>Instances are returned from {@link OptionSpecBuilder} methods to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:</p> + * + * <pre> + * <code> + * OptionParser parser = new OptionParser(); + * parser.accepts( "c" ).withRequiredArg().<strong>ofType( Integer.class )</strong>; + * </code> + * </pre> + * + * <p>If no methods are invoked on an instance of this class, then that instance's option will treat its argument as + * a {@link String}.</p> + * + * @param <V> represents the type of the arguments this option accepts + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public abstract class ArgumentAcceptingOptionSpec<V> extends AbstractOptionSpec<V> { + private static final char NIL_VALUE_SEPARATOR = '\u0000'; + + private boolean optionRequired; + private final boolean argumentRequired; + private ValueConverter<V> converter; + private String argumentDescription = ""; + private String valueSeparator = String.valueOf( NIL_VALUE_SEPARATOR ); + private final List<V> defaultValues = new ArrayList<V>(); + + ArgumentAcceptingOptionSpec( String option, boolean argumentRequired ) { + super( option ); + + this.argumentRequired = argumentRequired; + } + + ArgumentAcceptingOptionSpec( Collection<String> options, boolean argumentRequired, String description ) { + super( options, description ); + + this.argumentRequired = argumentRequired; + } + + /** + * <p>Specifies a type to which arguments of this spec's option are to be converted.</p> + * + * <p>JOpt Simple accepts types that have either:</p> + * + * <ol> + * <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String} + * and whose return type is the same as the class on which the method is declared. The {@code java.lang} + * primitive wrapper classes have such methods.</li> + * + * <li>a public constructor which accepts a single argument of type {@link String}.</li> + * </ol> + * + * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked + * before a one-{@link String}-arg constructor would.</p> + * + * <p>Invoking this method will trump any previous calls to this method or to + * {@link #withValuesConvertedBy(ValueConverter)}.</p> + * + * @param <T> represents the runtime class of the desired option argument type + * @param argumentType desired type of arguments to this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the type is {@code null} + * @throws IllegalArgumentException if the type does not have the standard conversion methods + */ + public final <T> ArgumentAcceptingOptionSpec<T> ofType( Class<T> argumentType ) { + return withValuesConvertedBy( findConverter( argumentType ) ); + } + + /** + * <p>Specifies a converter to use to translate arguments of this spec's option into Java objects. This is useful + * when converting to types that do not have the requisite factory method or constructor for + * {@link #ofType(Class)}.</p> + * + * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}. + * + * @param <T> represents the runtime class of the desired option argument type + * @param aConverter the converter to use + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the converter is {@code null} + */ + @SuppressWarnings( "unchecked" ) + public final <T> ArgumentAcceptingOptionSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) { + if ( aConverter == null ) + throw new NullPointerException( "illegal null converter" ); + + converter = (ValueConverter<V>) aConverter; + return (ArgumentAcceptingOptionSpec<T>) this; + } + + /** + * <p>Specifies a description for the argument of the option that this spec represents. This description is used + * when generating help information about the parser.</p> + * + * @param description describes the nature of the argument of this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public final ArgumentAcceptingOptionSpec<V> describedAs( String description ) { + argumentDescription = description; + return this; + } + + /** + * <p>Specifies a value separator for the argument of the option that this spec represents. This allows a single + * option argument to represent multiple values for the option. For example:</p> + * + * <pre> + * <code> + * parser.accepts( "z" ).withRequiredArg() + * .<strong>withValuesSeparatedBy( ',' )</strong>; + * OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z", + * "fizz", "-z", "buzz" } ); + * </code> + * </pre> + * + * <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p> + * + * <p>You cannot use Unicode U+0000 as the separator.</p> + * + * @param separator a character separator + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws IllegalArgumentException if the separator is Unicode U+0000 + */ + public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( char separator ) { + if ( separator == NIL_VALUE_SEPARATOR ) + throw new IllegalArgumentException( "cannot use U+0000 as separator" ); + + valueSeparator = String.valueOf( separator ); + return this; + } + + /** + * <p>Specifies a value separator for the argument of the option that this spec represents. This allows a single + * option argument to represent multiple values for the option. For example:</p> + * + * <pre> + * <code> + * parser.accepts( "z" ).withRequiredArg() + * .<strong>withValuesSeparatedBy( ":::" )</strong>; + * OptionSet options = parser.parse( new String[] { "-z", "foo:::bar:::baz", "-z", + * "fizz", "-z", "buzz" } ); + * </code> + * </pre> + * + * <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p> + * + * <p>You cannot use Unicode U+0000 in the separator.</p> + * + * @param separator a string separator + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws IllegalArgumentException if the separator contains Unicode U+0000 + */ + public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( String separator ) { + if ( separator.indexOf( NIL_VALUE_SEPARATOR ) != -1 ) + throw new IllegalArgumentException( "cannot use U+0000 in separator" ); + + valueSeparator = separator; + return this; + } + + /** + * Specifies a set of default values for the argument of the option that this spec represents. + * + * @param value the first in the set of default argument values for this spec's option + * @param values the (optional) remainder of the set of default argument values for this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if {@code value}, {@code values}, or any elements of {@code values} are + * {@code null} + */ + @SuppressWarnings("unchecked") + public ArgumentAcceptingOptionSpec<V> defaultsTo( V value, V... values ) { + addDefaultValue( value ); + defaultsTo( values ); + + return this; + } + + /** + * Specifies a set of default values for the argument of the option that this spec represents. + * + * @param values the set of default argument values for this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if {@code values} or any elements of {@code values} are {@code null} + */ + public ArgumentAcceptingOptionSpec<V> defaultsTo( V[] values ) { + for ( V each : values ) + addDefaultValue( each ); + + return this; + } + + /** + * Marks this option as required. An {@link OptionException} will be thrown when + * {@link OptionParser#parse(java.lang.String...)} is called, if an option is marked as required and not specified + * on the command line. + * + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public ArgumentAcceptingOptionSpec<V> required() { + optionRequired = true; + return this; + } + + public boolean isRequired() { + return optionRequired; + } + + private void addDefaultValue( V value ) { + ensureNotNull( value ); + defaultValues.add( value ); + } + + @Override + final void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ) { + + if ( isNullOrEmpty( detectedArgument ) ) + detectOptionArgument( parser, arguments, detectedOptions ); + else + addArguments( detectedOptions, detectedArgument ); + } + + protected void addArguments( OptionSet detectedOptions, String detectedArgument ) { + StringTokenizer lexer = new StringTokenizer( detectedArgument, valueSeparator ); + if ( !lexer.hasMoreTokens() ) + detectedOptions.addWithArgument( this, detectedArgument ); + else { + while ( lexer.hasMoreTokens() ) + detectedOptions.addWithArgument( this, lexer.nextToken() ); + } + } + + protected abstract void detectOptionArgument( OptionParser parser, ArgumentList arguments, + OptionSet detectedOptions ); + + @Override + protected final V convert( String argument ) { + return convertWith( converter, argument ); + } + + protected boolean canConvertArgument( String argument ) { + StringTokenizer lexer = new StringTokenizer( argument, valueSeparator ); + + try { + while ( lexer.hasMoreTokens() ) + convert( lexer.nextToken() ); + return true; + } + catch ( OptionException ignored ) { + return false; + } + } + + protected boolean isArgumentOfNumberType() { + return converter != null && Number.class.isAssignableFrom( converter.valueType() ); + } + + public boolean acceptsArguments() { + return true; + } + + public boolean requiresArgument() { + return argumentRequired; + } + + public String argumentDescription() { + return argumentDescription; + } + + public String argumentTypeIndicator() { + return argumentTypeIndicatorFrom( converter ); + } + + public List<V> defaultValues() { + return unmodifiableList( defaultValues ); + } + + @Override + public boolean equals( Object that ) { + if ( !super.equals( that ) ) + return false; + + ArgumentAcceptingOptionSpec<?> other = (ArgumentAcceptingOptionSpec<?>) that; + return requiresArgument() == other.requiresArgument(); + } + + @Override + public int hashCode() { + return super.hashCode() ^ ( argumentRequired ? 0 : 1 ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * <p>Wrapper for an array of command line arguments.</p> + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class ArgumentList { + private final String[] arguments; + private int currentIndex; + + ArgumentList( String... arguments ) { + this.arguments = arguments.clone(); + } + + boolean hasMore() { + return currentIndex < arguments.length; + } + + String next() { + return arguments[ currentIndex++ ]; + } + + String peek() { + return arguments[ currentIndex ]; + } + + void treatNextAsLongOption() { + if ( HYPHEN_CHAR != arguments[ currentIndex ].charAt( 0 ) ) + arguments[ currentIndex ] = DOUBLE_HYPHEN + arguments[ currentIndex ]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import jdk.internal.joptsimple.internal.Rows; +import jdk.internal.joptsimple.internal.Strings; + +import static jdk.internal.joptsimple.ParserRules.*; +import static jdk.internal.joptsimple.internal.Classes.*; +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * <p>A help formatter that allows configuration of overall row width and column separator width.</p> + * + * <p>The formatter produces a two-column output. The left column is for the options, and the right column for their + * descriptions. The formatter will allow as much space as possible for the descriptions, by minimizing the option + * column's width, no greater than slightly less than half the overall desired width.</p> + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public class BuiltinHelpFormatter implements HelpFormatter { + private final Rows nonOptionRows; + private final Rows optionRows; + + /** + * Makes a formatter with a pre-configured overall row width and column separator width. + */ + BuiltinHelpFormatter() { + this( 80, 2 ); + } + + /** + * Makes a formatter with a given overall row width and column separator width. + * + * @param desiredOverallWidth how many characters wide to make the overall help display + * @param desiredColumnSeparatorWidth how many characters wide to make the separation between option column and + * description column + */ + public BuiltinHelpFormatter( int desiredOverallWidth, int desiredColumnSeparatorWidth ) { + nonOptionRows = new Rows( desiredOverallWidth * 2, 0 ); + optionRows = new Rows( desiredOverallWidth, desiredColumnSeparatorWidth ); + } + + public String format( Map<String, ? extends OptionDescriptor> options ) { + Comparator<OptionDescriptor> comparator = + new Comparator<OptionDescriptor>() { + public int compare( OptionDescriptor first, OptionDescriptor second ) { + return first.options().iterator().next().compareTo( second.options().iterator().next() ); + } + }; + + Set<OptionDescriptor> sorted = new TreeSet<OptionDescriptor>( comparator ); + sorted.addAll( options.values() ); + + addRows( sorted ); + + return formattedHelpOutput(); + } + + private String formattedHelpOutput() { + StringBuilder formatted = new StringBuilder(); + String nonOptionDisplay = nonOptionRows.render(); + if ( !Strings.isNullOrEmpty( nonOptionDisplay ) ) + formatted.append( nonOptionDisplay ).append( LINE_SEPARATOR ); + formatted.append( optionRows.render() ); + + return formatted.toString(); + } + + private void addRows( Collection<? extends OptionDescriptor> options ) { + addNonOptionsDescription( options ); + + if ( options.isEmpty() ) + optionRows.add( "No options specified", "" ); + else { + addHeaders( options ); + addOptions( options ); + } + + fitRowsToWidth(); + } + + private void addNonOptionsDescription( Collection<? extends OptionDescriptor> options ) { + OptionDescriptor nonOptions = findAndRemoveNonOptionsSpec( options ); + if ( shouldShowNonOptionArgumentDisplay( nonOptions ) ) { + nonOptionRows.add( "Non-option arguments:", "" ); + nonOptionRows.add(createNonOptionArgumentsDisplay(nonOptions), ""); + } + } + + private boolean shouldShowNonOptionArgumentDisplay( OptionDescriptor nonOptions ) { + return !Strings.isNullOrEmpty( nonOptions.description() ) + || !Strings.isNullOrEmpty( nonOptions.argumentTypeIndicator() ) + || !Strings.isNullOrEmpty( nonOptions.argumentDescription() ); + } + + private String createNonOptionArgumentsDisplay(OptionDescriptor nonOptions) { + StringBuilder buffer = new StringBuilder(); + maybeAppendOptionInfo( buffer, nonOptions ); + maybeAppendNonOptionsDescription( buffer, nonOptions ); + + return buffer.toString(); + } + + private void maybeAppendNonOptionsDescription( StringBuilder buffer, OptionDescriptor nonOptions ) { + buffer.append( buffer.length() > 0 && !Strings.isNullOrEmpty( nonOptions.description() ) ? " -- " : "" ) + .append( nonOptions.description() ); + } + + private OptionDescriptor findAndRemoveNonOptionsSpec( Collection<? extends OptionDescriptor> options ) { + for ( Iterator<? extends OptionDescriptor> it = options.iterator(); it.hasNext(); ) { + OptionDescriptor next = it.next(); + if ( next.representsNonOptions() ) { + it.remove(); + return next; + } + } + + throw new AssertionError( "no non-options argument spec" ); + } + + private void addHeaders( Collection<? extends OptionDescriptor> options ) { + if ( hasRequiredOption( options ) ) { + optionRows.add("Option (* = required)", "Description"); + optionRows.add("---------------------", "-----------"); + } else { + optionRows.add("Option", "Description"); + optionRows.add("------", "-----------"); + } + } + + private boolean hasRequiredOption( Collection<? extends OptionDescriptor> options ) { + for ( OptionDescriptor each : options ) { + if ( each.isRequired() ) + return true; + } + + return false; + } + + private void addOptions( Collection<? extends OptionDescriptor> options ) { + for ( OptionDescriptor each : options ) { + if ( !each.representsNonOptions() ) + optionRows.add( createOptionDisplay( each ), createDescriptionDisplay( each ) ); + } + } + + private String createOptionDisplay( OptionDescriptor descriptor ) { + StringBuilder buffer = new StringBuilder( descriptor.isRequired() ? "* " : "" ); + + for ( Iterator<String> i = descriptor.options().iterator(); i.hasNext(); ) { + String option = i.next(); + buffer.append( option.length() > 1 ? DOUBLE_HYPHEN : HYPHEN ); + buffer.append( option ); + + if ( i.hasNext() ) + buffer.append( ", " ); + } + + maybeAppendOptionInfo( buffer, descriptor ); + + return buffer.toString(); + } + + private void maybeAppendOptionInfo( StringBuilder buffer, OptionDescriptor descriptor ) { + String indicator = extractTypeIndicator( descriptor ); + String description = descriptor.argumentDescription(); + if ( indicator != null || !isNullOrEmpty( description ) ) + appendOptionHelp( buffer, indicator, description, descriptor.requiresArgument() ); + } + + private String extractTypeIndicator( OptionDescriptor descriptor ) { + String indicator = descriptor.argumentTypeIndicator(); + + if ( !isNullOrEmpty( indicator ) && !String.class.getName().equals( indicator ) ) + return shortNameOf( indicator ); + + return null; + } + + private void appendOptionHelp( StringBuilder buffer, String typeIndicator, String description, boolean required ) { + if ( required ) + appendTypeIndicator( buffer, typeIndicator, description, '<', '>' ); + else + appendTypeIndicator( buffer, typeIndicator, description, '[', ']' ); + } + + private void appendTypeIndicator( StringBuilder buffer, String typeIndicator, String description, + char start, char end ) { + buffer.append( ' ' ).append( start ); + if ( typeIndicator != null ) + buffer.append( typeIndicator ); + + if ( !Strings.isNullOrEmpty( description ) ) { + if ( typeIndicator != null ) + buffer.append( ": " ); + + buffer.append( description ); + } + + buffer.append( end ); + } + + private String createDescriptionDisplay( OptionDescriptor descriptor ) { + List<?> defaultValues = descriptor.defaultValues(); + if ( defaultValues.isEmpty() ) + return descriptor.description(); + + String defaultValuesDisplay = createDefaultValuesDisplay( defaultValues ); + return ( descriptor.description() + ' ' + surround( "default: " + defaultValuesDisplay, '(', ')' ) ).trim(); + } + + private String createDefaultValuesDisplay( List<?> defaultValues ) { + return defaultValues.size() == 1 ? defaultValues.get( 0 ).toString() : defaultValues.toString(); + } + + private void fitRowsToWidth() { + nonOptionRows.fitToWidth(); + optionRows.fitToWidth(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Map; + +/** + * <p>Represents objects charged with taking a set of option descriptions and producing some help text from them.</p> + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public interface HelpFormatter { + /** + * Produces help text, given a set of option descriptors. + * + * @param options descriptors for the configured options of a parser + * @return text to be used as help + * @see OptionParser#printHelpOn(java.io.Writer) + * @see OptionParser#formatHelpWith(HelpFormatter) + */ + String format( Map<String, ? extends OptionDescriptor> options ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +/** + * Thrown when the option parser is asked to recognize an option with illegal characters in it. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class IllegalOptionSpecificationException extends OptionException { + private static final long serialVersionUID = -1L; + + IllegalOptionSpecificationException( String option ) { + super( singletonList( option ) ); + } + + @Override + public String getMessage() { + return singleOptionMessage() + " is not a legal option character"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when an option is marked as required, but not specified on the command line. + * + * @author <a href="https://github.com/TC1">Emils Solmanis</a> + */ +class MissingRequiredOptionException extends OptionException { + private static final long serialVersionUID = -1L; + + protected MissingRequiredOptionException( Collection<String> options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Missing required option(s) " + multipleOptionMessage(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when asking an {@link OptionSet} for a single argument of an option when many have been specified. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class MultipleArgumentsForOptionException extends OptionException { + private static final long serialVersionUID = -1L; + + MultipleArgumentsForOptionException( Collection<String> options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Found multiple arguments for option " + multipleOptionMessage() + ", but you asked for only one"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.*; + +/** + * A specification for an option that does not accept arguments. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class NoArgumentOptionSpec extends AbstractOptionSpec<Void> { + NoArgumentOptionSpec( String option ) { + this( singletonList( option ), "" ); + } + + NoArgumentOptionSpec( Collection<String> options, String description ) { + super( options, description ); + } + + @Override + void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ) { + + detectedOptions.add( this ); + } + + public boolean acceptsArguments() { + return false; + } + + public boolean requiresArgument() { + return false; + } + + public boolean isRequired() { + return false; + } + + public String argumentDescription() { + return ""; + } + + public String argumentTypeIndicator() { + return ""; + } + + @Override + protected Void convert( String argument ) { + return null; + } + + public List<Void> defaultValues() { + return emptyList(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.List; + +import static java.util.Arrays.*; +import static java.util.Collections.*; +import static jdk.internal.joptsimple.internal.Reflection.*; + +/** + * <p>Specification of a command line's non-option arguments.</p> + * + * <p>Instances are returned from {@link OptionParser} methods to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:</p> + * + * <pre> + * <code> + * OptionParser parser = new OptionParser(); + * parser.nonOptions( "files to be processed" ).<strong>ofType( File.class )</strong>; + * </code> + * </pre> + * + * <p>If no methods are invoked on an instance of this class, then that instance's option will treat the non-option + * arguments as {@link String}s.</p> + * + * @param <V> represents the type of the non-option arguments + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public class NonOptionArgumentSpec<V> extends AbstractOptionSpec<V> { + static final String NAME = "[arguments]"; + + private ValueConverter<V> converter; + private String argumentDescription = ""; + + NonOptionArgumentSpec() { + this(""); + } + + NonOptionArgumentSpec( String description ) { + super( asList( NAME ), description ); + } + + /** + * <p>Specifies a type to which the non-option arguments are to be converted.</p> + * + * <p>JOpt Simple accepts types that have either:</p> + * + * <ol> + * <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String} + * and whose return type is the same as the class on which the method is declared. The {@code java.lang} + * primitive wrapper classes have such methods.</li> + * + * <li>a public constructor which accepts a single argument of type {@link String}.</li> + * </ol> + * + * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked + * before a one-{@link String}-arg constructor would.</p> + * + * <p>Invoking this method will trump any previous calls to this method or to + * {@link #withValuesConvertedBy(ValueConverter)}.</p> + * + * @param <T> represents the runtime class of the desired option argument type + * @param argumentType desired type of arguments to this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the type is {@code null} + * @throws IllegalArgumentException if the type does not have the standard conversion methods + */ + @SuppressWarnings( "unchecked" ) + public <T> NonOptionArgumentSpec<T> ofType( Class<T> argumentType ) { + converter = (ValueConverter<V>) findConverter( argumentType ); + return (NonOptionArgumentSpec<T>) this; + } + + /** + * <p>Specifies a converter to use to translate non-option arguments into Java objects. This is useful + * when converting to types that do not have the requisite factory method or constructor for + * {@link #ofType(Class)}.</p> + * + * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}. + * + * @param <T> represents the runtime class of the desired non-option argument type + * @param aConverter the converter to use + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the converter is {@code null} + */ + @SuppressWarnings( "unchecked" ) + public final <T> NonOptionArgumentSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) { + if ( aConverter == null ) + throw new NullPointerException( "illegal null converter" ); + + converter = (ValueConverter<V>) aConverter; + return (NonOptionArgumentSpec<T>) this; + } + + /** + * <p>Specifies a description for the non-option arguments that this spec represents. This description is used + * when generating help information about the parser.</p> + * + * @param description describes the nature of the argument of this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public NonOptionArgumentSpec<V> describedAs( String description ) { + argumentDescription = description; + return this; + } + + @Override + protected final V convert( String argument ) { + return convertWith( converter, argument ); + } + + @Override + void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ) { + + detectedOptions.addWithArgument( this, detectedArgument ); + } + + public List<?> defaultValues() { + return emptyList(); + } + + public boolean isRequired() { + return false; + } + + public boolean acceptsArguments() { + return false; + } + + public boolean requiresArgument() { + return false; + } + + public String argumentDescription() { + return argumentDescription; + } + + public String argumentTypeIndicator() { + return argumentTypeIndicatorFrom( converter ); + } + + public boolean representsNonOptions() { + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when a problem occurs converting an argument of an option from {@link String} to another type. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class OptionArgumentConversionException extends OptionException { + private static final long serialVersionUID = -1L; + + private final String argument; + + OptionArgumentConversionException( Collection<String> options, String argument, Throwable cause ) { + super( options, cause ); + + this.argument = argument; + } + + @Override + public String getMessage() { + return "Cannot parse argument '" + argument + "' of option " + multipleOptionMessage(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Trains the option parser. This interface aids integration with other code which may expose declaration of options but + * not actual command-line parsing. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + * @see OptionParser + */ +public interface OptionDeclarer { + /** + * Tells the parser to recognize the given option. + * + * <p>This method returns an instance of {@link OptionSpecBuilder} to allow the formation of parser directives + * as sentences in a fluent interface language. For example:</p> + * + * <pre><code> + * OptionDeclarer parser = new OptionParser(); + * parser.<strong>accepts( "c" )</strong>.withRequiredArg().ofType( Integer.class ); + * </code></pre> + * + * <p>If no methods are invoked on the returned {@link OptionSpecBuilder}, then the parser treats the option as + * accepting no argument.</p> + * + * @param option the option to recognize + * @return an object that can be used to flesh out more detail about the option + * @throws OptionException if the option contains illegal characters + * @throws NullPointerException if the option is {@code null} + */ + OptionSpecBuilder accepts( String option ); + + /** + * Tells the parser to recognize the given option. + * + * @see #accepts(String) + * @param option the option to recognize + * @param description a string that describes the purpose of the option. This is used when generating help + * information about the parser. + * @return an object that can be used to flesh out more detail about the option + * @throws OptionException if the option contains illegal characters + * @throws NullPointerException if the option is {@code null} + */ + OptionSpecBuilder accepts( String option, String description ); + + /** + * Tells the parser to recognize the given options, and treat them as synonymous. + * + * @see #accepts(String) + * @param options the options to recognize and treat as synonymous + * @return an object that can be used to flesh out more detail about the options + * @throws OptionException if any of the options contain illegal characters + * @throws NullPointerException if the option list or any of its elements are {@code null} + */ + OptionSpecBuilder acceptsAll( Collection<String> options ); + + /** + * Tells the parser to recognize the given options, and treat them as synonymous. + * + * @see #acceptsAll(Collection) + * @param options the options to recognize and treat as synonymous + * @param description a string that describes the purpose of the option. This is used when generating help + * information about the parser. + * @return an object that can be used to flesh out more detail about the options + * @throws OptionException if any of the options contain illegal characters + * @throws NullPointerException if the option list or any of its elements are {@code null} + * @throws IllegalArgumentException if the option list is empty + */ + OptionSpecBuilder acceptsAll( Collection<String> options, String description ); + + /** + * Gives an object that represents an access point for non-option arguments on a command line. + * + * @return an object that can be used to flesh out more detail about the non-option arguments + */ + NonOptionArgumentSpec<String> nonOptions(); + + /** + * Gives an object that represents an access point for non-option arguments on a command line. + * + * @see #nonOptions() + * @param description a string that describes the purpose of the non-option arguments. This is used when generating + * help information about the parser. + * @return an object that can be used to flesh out more detail about the non-option arguments + */ + NonOptionArgumentSpec<String> nonOptions( String description ); + + /** + * Tells the parser whether or not to behave "POSIX-ly correct"-ly. + * + * @param setting {@code true} if the parser should behave "POSIX-ly correct"-ly + */ + void posixlyCorrect( boolean setting ); + + /** + * <p>Tells the parser to treat unrecognized options as non-option arguments.</p> + * + * <p>If not called, then the parser raises an {@link OptionException} when it encounters an unrecognized + * option.</p> + */ + void allowsUnrecognizedOptions(); + + /** + * Tells the parser either to recognize or ignore <kbd>"-W"</kbd>-style long options. + * + * @param recognize {@code true} if the parser is to recognize the special style of long options + */ + void recognizeAlternativeLongOptions( boolean recognize ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.List; + +/** + * Describes options that an option parser recognizes, in ways that might be useful to {@linkplain HelpFormatter + * help screens}. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public interface OptionDescriptor { + /** + * A set of options that are mutually synonymous. + * + * @return synonymous options + */ + Collection<String> options(); + + /** + * Description of this option's purpose. + * + * @return a description for the option + */ + String description(); + + /** + * What values will the option take if none are specified on the command line? + * + * @return any default values for the option + */ + List<?> defaultValues(); + + /** + * Is this option {@linkplain ArgumentAcceptingOptionSpec#required() required} on a command line? + * + * @return whether the option is required + */ + boolean isRequired(); + + /** + * Does this option {@linkplain ArgumentAcceptingOptionSpec accept arguments}? + * + * @return whether the option accepts arguments + */ + boolean acceptsArguments(); + + /** + * Does this option {@linkplain OptionSpecBuilder#withRequiredArg() require an argument}? + * + * @return whether the option requires an argument + */ + boolean requiresArgument(); + + /** + * Gives a short {@linkplain ArgumentAcceptingOptionSpec#describedAs(String) description} of the option's argument. + * + * @return a description for the option's argument + */ + String argumentDescription(); + + /** + * Gives an indication of the {@linkplain ArgumentAcceptingOptionSpec#ofType(Class) expected type} of the option's + * argument. + * + * @return a description for the option's argument type + */ + String argumentTypeIndicator(); + + /** + * Tells whether this object represents the non-option arguments of a command line. + * + * @return {@code true} if this represents non-option arguments + */ + boolean representsNonOptions(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * Thrown when a problem occurs during option parsing. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public abstract class OptionException extends RuntimeException { + private static final long serialVersionUID = -1L; + + private final List<String> options = new ArrayList<String>(); + + protected OptionException( Collection<String> options ) { + this.options.addAll( options ); + } + + protected OptionException( Collection<String> options, Throwable cause ) { + super( cause ); + + this.options.addAll( options ); + } + + /** + * Gives the option being considered when the exception was created. + * + * @return the option being considered when the exception was created + */ + public Collection<String> options() { + return unmodifiableCollection( options ); + } + + protected final String singleOptionMessage() { + return singleOptionMessage( options.get( 0 ) ); + } + + protected final String singleOptionMessage( String option ) { + return SINGLE_QUOTE + option + SINGLE_QUOTE; + } + + protected final String multipleOptionMessage() { + StringBuilder buffer = new StringBuilder( "[" ); + + for ( Iterator<String> iter = options.iterator(); iter.hasNext(); ) { + buffer.append( singleOptionMessage( iter.next() ) ); + if ( iter.hasNext() ) + buffer.append( ", " ); + } + + buffer.append( ']' ); + + return buffer.toString(); + } + + static OptionException unrecognizedOption( String option ) { + return new UnrecognizedOptionException( option ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when the option parser discovers an option that requires an argument, but that argument is missing. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class OptionMissingRequiredArgumentException extends OptionException { + private static final long serialVersionUID = -1L; + + OptionMissingRequiredArgumentException( Collection<String> options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Option " + multipleOptionMessage() + " requires an argument"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import jdk.internal.joptsimple.internal.AbbreviationMap; +import jdk.internal.joptsimple.util.KeyValuePair; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.*; +import static jdk.internal.joptsimple.OptionException.*; +import static jdk.internal.joptsimple.OptionParserState.*; +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * <p>Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()} + * and GNU {@code getopt_long()}.</p> + * + * <p>This parser supports short options and long options.</p> + * + * <ul> + * <li><dfn>Short options</dfn> begin with a single hyphen ("<kbd>-</kbd>") followed by a single letter or digit, + * or question mark ("<kbd>?</kbd>"), or dot ("<kbd>.</kbd>").</li> + * + * <li>Short options can accept single arguments. The argument can be made required or optional. The option's + * argument can occur: + * <ul> + * <li>in the slot after the option, as in <kbd>-d /tmp</kbd></li> + * <li>right up against the option, as in <kbd>-d/tmp</kbd></li> + * <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in <kbd>-d=/tmp</kbd></li> + * </ul> + * To specify <em>n</em> arguments for an option, specify the option <em>n</em> times, once for each argument, + * as in <kbd>-d /tmp -d /var -d /opt</kbd>; or, when using the + * {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent + * interface" (see below), give multiple values separated by a given character as a single argument to the + * option.</li> + * + * <li>Short options can be clustered, so that <kbd>-abc</kbd> is treated as <kbd>-a -b -c</kbd>. If a short option + * in the cluster can accept an argument, the remaining characters are interpreted as the argument for that + * option.</li> + * + * <li>An argument consisting only of two hyphens (<kbd>"--"</kbd>) signals that the remaining arguments are to be + * treated as non-options.</li> + * + * <li>An argument consisting only of a single hyphen is considered a non-option argument (though it can be an + * argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard + * output streams.</li> + * + * <li><dfn>Long options</dfn> begin with two hyphens (<kbd>"--"</kbd>), followed by multiple letters, digits, + * hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when + * configuring the parser.</li> + * + * <li>You can abbreviate long options, so long as the abbreviation is unique.</li> + * + * <li>Long options can accept single arguments. The argument can be made required or optional. The option's + * argument can occur: + * <ul> + * <li>in the slot after the option, as in <kbd>--directory /tmp</kbd></li> + * <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in + * <kbd>--directory=/tmp</kbd> + * </ul> + * Specify multiple arguments for a long option in the same manner as for short options (see above).</li> + * + * <li>You can use a single hyphen (<kbd>"-"</kbd>) instead of a double hyphen (<kbd>"--"</kbd>) for a long + * option.</li> + * + * <li>The option <kbd>-W</kbd> is reserved. If you tell the parser to {@linkplain + * #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example, + * <kbd>-W foo=bar</kbd> as the long option <kbd>foo</kbd> with argument <kbd>bar</kbd>, as though you had written + * <kbd>--foo=bar</kbd>.</li> + * + * <li>You can specify <kbd>-W</kbd> as a valid short option, or use it as an abbreviation for a long option, but + * {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede + * this behavior.</li> + * + * <li>You can specify a given short or long option multiple times on a single command line. The parser collects + * any arguments specified for those options as a list.</li> + * + * <li>If the parser detects an option whose argument is optional, and the next argument "looks like" an option, + * that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other + * hand, the optional argument is typed as a derivative of {@link Number}, then that argument is treated as the + * negative number argument of the option, even if the parser recognizes the corresponding numeric option. + * For example: + * <pre><code> + * OptionParser parser = new OptionParser(); + * parser.accepts( "a" ).withOptionalArg().ofType( Integer.class ); + * parser.accepts( "2" ); + * OptionSet options = parser.parse( "-a", "-2" ); + * </code></pre> + * In this case, the option set contains <kbd>"a"</kbd> with argument <kbd>-2</kbd>, not both <kbd>"a"</kbd> and + * <kbd>"2"</kbd>. Swapping the elements in the <em>args</em> array gives the latter.</li> + * </ul> + * + * <p>There are two ways to tell the parser what options to recognize:</p> + * + * <ol> + * <li>A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent + * interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(Collection) + * acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument, + * whether the argument is required or optional, to what type arguments of the options should be converted if any, + * etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to + * retrieve the arguments of the associated option in a type-safe manner.</li> + * + * <li>Since version 1, a more concise way of specifying short options has been to use the special {@linkplain + * #OptionParser(String) constructor}. Arguments of options specified in this manner will be of type {@link String}. + * Here are the rules for the format of the specification strings this constructor accepts: + * + * <ul> + * <li>Any letter or digit is treated as an option character.</li> + * + * <li>An option character can be immediately followed by an asterisk (*) to indicate that the option is a + * "help" option.</li> + * + * <li>If an option character (with possible trailing asterisk) is followed by a single colon (<kbd>":"</kbd>), + * then the option requires an argument.</li> + * + * <li>If an option character (with possible trailing asterisk) is followed by two colons (<kbd>"::"</kbd>), + * then the option accepts an optional argument.</li> + * + * <li>Otherwise, the option character accepts no argument.</li> + * + * <li>If the option specification string begins with a plus sign (<kbd>"+"</kbd>), the parser will behave + * "POSIX-ly correct".</li> + * + * <li>If the option specification string contains the sequence <kbd>"W;"</kbd> (capital W followed by a + * semicolon), the parser will recognize the alternative form of long options.</li> + * </ul> + * </li> + * </ol> + * + * <p>Each of the options in a list of options given to {@link #acceptsAll(Collection) acceptsAll} is treated as a + * synonym of the others. For example: + * <pre> + * <code> + * OptionParser parser = new OptionParser(); + * parser.acceptsAll( asList( "w", "interactive", "confirmation" ) ); + * OptionSet options = parser.parse( "-w" ); + * </code> + * </pre> + * In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments + * <kbd>"w"</kbd>, <kbd>"interactive"</kbd>, and <kbd>"confirmation"</kbd>. The {@link OptionSet} would give the same + * responses to these arguments for its other methods as well.</p> + * + * <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however, + * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an + * option, and is not a required argument of a preceding option, signals the end of options. You can still bind + * optional arguments to their options using the abutting (for short options) or <kbd>=</kbd> syntax.</p> + * + * <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}. + * "POSIX-ly correct" parsers are configured by either:</p> + * + * <ol> + * <li>using the method {@link #posixlyCorrect(boolean)}, or</li> + * + * <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign + * (<kbd>"+"</kbd>)</li> + * </ol> + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + * @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library</a> + */ +public class OptionParser implements OptionDeclarer { + private final AbbreviationMap<AbstractOptionSpec<?>> recognizedOptions; + private final Map<Collection<String>, Set<OptionSpec<?>>> requiredIf; + private final Map<Collection<String>, Set<OptionSpec<?>>> requiredUnless; + private OptionParserState state; + private boolean posixlyCorrect; + private boolean allowsUnrecognizedOptions; + private HelpFormatter helpFormatter = new BuiltinHelpFormatter(); + + /** + * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct" + * behavior. + */ + public OptionParser() { + recognizedOptions = new AbbreviationMap<AbstractOptionSpec<?>>(); + requiredIf = new HashMap<Collection<String>, Set<OptionSpec<?>>>(); + requiredUnless = new HashMap<Collection<String>, Set<OptionSpec<?>>>(); + state = moreOptions( false ); + + recognize( new NonOptionArgumentSpec<String>() ); + } + + /** + * Creates an option parser and configures it to recognize the short options specified in the given string. + * + * Arguments of options specified this way will be of type {@link String}. + * + * @param optionSpecification an option specification + * @throws NullPointerException if {@code optionSpecification} is {@code null} + * @throws OptionException if the option specification contains illegal characters or otherwise cannot be + * recognized + */ + public OptionParser( String optionSpecification ) { + this(); + + new OptionSpecTokenizer( optionSpecification ).configure( this ); + } + + public OptionSpecBuilder accepts( String option ) { + return acceptsAll( singletonList( option ) ); + } + + public OptionSpecBuilder accepts( String option, String description ) { + return acceptsAll( singletonList( option ), description ); + } + + public OptionSpecBuilder acceptsAll( Collection<String> options ) { + return acceptsAll( options, "" ); + } + + public OptionSpecBuilder acceptsAll( Collection<String> options, String description ) { + if ( options.isEmpty() ) + throw new IllegalArgumentException( "need at least one option" ); + + ensureLegalOptions( options ); + + return new OptionSpecBuilder( this, options, description ); + } + + public NonOptionArgumentSpec<String> nonOptions() { + NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>(); + + recognize( spec ); + + return spec; + } + + public NonOptionArgumentSpec<String> nonOptions( String description ) { + NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>( description ); + + recognize( spec ); + + return spec; + } + + public void posixlyCorrect( boolean setting ) { + posixlyCorrect = setting; + state = moreOptions( setting ); + } + + boolean posixlyCorrect() { + return posixlyCorrect; + } + + public void allowsUnrecognizedOptions() { + allowsUnrecognizedOptions = true; + } + + boolean doesAllowsUnrecognizedOptions() { + return allowsUnrecognizedOptions; + } + + public void recognizeAlternativeLongOptions( boolean recognize ) { + if ( recognize ) + recognize( new AlternativeLongOptionSpec() ); + else + recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) ); + } + + void recognize( AbstractOptionSpec<?> spec ) { + recognizedOptions.putAll( spec.options(), spec ); + } + + /** + * Writes information about the options this parser recognizes to the given output sink. + * + * The output sink is flushed, but not closed. + * + * @param sink the sink to write information to + * @throws IOException if there is a problem writing to the sink + * @throws NullPointerException if {@code sink} is {@code null} + * @see #printHelpOn(Writer) + */ + public void printHelpOn( OutputStream sink ) throws IOException { + printHelpOn( new OutputStreamWriter( sink ) ); + } + + /** + * Writes information about the options this parser recognizes to the given output sink. + * + * The output sink is flushed, but not closed. + * + * @param sink the sink to write information to + * @throws IOException if there is a problem writing to the sink + * @throws NullPointerException if {@code sink} is {@code null} + * @see #printHelpOn(OutputStream) + */ + public void printHelpOn( Writer sink ) throws IOException { + sink.write( helpFormatter.format( recognizedOptions.toJavaUtilMap() ) ); + sink.flush(); + } + + /** + * Tells the parser to use the given formatter when asked to {@linkplain #printHelpOn(java.io.Writer) print help}. + * + * @param formatter the formatter to use for printing help + * @throws NullPointerException if the formatter is {@code null} + */ + public void formatHelpWith( HelpFormatter formatter ) { + if ( formatter == null ) + throw new NullPointerException(); + + helpFormatter = formatter; + } + + /** + * Retrieves all the options which have been configured for the parser. + * + * @return a {@link Map} containing all the configured options and their corresponding {@link OptionSpec} + */ + public Map<String, OptionSpec<?>> recognizedOptions() { + return new HashMap<String, OptionSpec<?>>( recognizedOptions.toJavaUtilMap() ); + } + + /** + * Parses the given command line arguments according to the option specifications given to the parser. + * + * @param arguments arguments to parse + * @return an {@link OptionSet} describing the parsed options, their arguments, and any non-option arguments found + * @throws OptionException if problems are detected while parsing + * @throws NullPointerException if the argument list is {@code null} + */ + public OptionSet parse( String... arguments ) { + ArgumentList argumentList = new ArgumentList( arguments ); + OptionSet detected = new OptionSet( recognizedOptions.toJavaUtilMap() ); + detected.add( recognizedOptions.get( NonOptionArgumentSpec.NAME ) ); + + while ( argumentList.hasMore() ) + state.handleArgument( this, argumentList, detected ); + + reset(); + + ensureRequiredOptions( detected ); + + return detected; + } + + private void ensureRequiredOptions( OptionSet options ) { + Collection<String> missingRequiredOptions = missingRequiredOptions( options ); + boolean helpOptionPresent = isHelpOptionPresent( options ); + + if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent ) + throw new MissingRequiredOptionException( missingRequiredOptions ); + } + + private Collection<String> missingRequiredOptions( OptionSet options ) { + Collection<String> missingRequiredOptions = new HashSet<String>(); + + for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) { + if ( each.isRequired() && !options.has( each ) ) + missingRequiredOptions.addAll( each.options() ); + } + + for ( Map.Entry<Collection<String>, Set<OptionSpec<?>>> eachEntry : requiredIf.entrySet() ) { + AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() ); + + if ( optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) { + missingRequiredOptions.addAll( required.options() ); + } + } + + for ( Map.Entry<Collection<String>, Set<OptionSpec<?>>> eachEntry : requiredUnless.entrySet() ) { + AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() ); + + if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) { + missingRequiredOptions.addAll( required.options() ); + } + } + + return missingRequiredOptions; + } + + private boolean optionsHasAnyOf( OptionSet options, Collection<OptionSpec<?>> specs ) { + for ( OptionSpec<?> each : specs ) { + if ( options.has( each ) ) + return true; + } + + return false; + } + + private boolean isHelpOptionPresent( OptionSet options ) { + boolean helpOptionPresent = false; + for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) { + if ( each.isForHelp() && options.has( each ) ) { + helpOptionPresent = true; + break; + } + } + return helpOptionPresent; + } + + void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) { + KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate ); + + if ( !isRecognized( optionAndArgument.key ) ) + throw unrecognizedOption( optionAndArgument.key ); + + AbstractOptionSpec<?> optionSpec = specFor( optionAndArgument.key ); + optionSpec.handleOption( this, arguments, detected, optionAndArgument.value ); + } + + void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) { + KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate ); + + if ( isRecognized( optionAndArgument.key ) ) { + specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value ); + } + else + handleShortOptionCluster( candidate, arguments, detected ); + } + + private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) { + char[] options = extractShortOptionsFrom( candidate ); + validateOptionCharacters( options ); + + for ( int i = 0; i < options.length; i++ ) { + AbstractOptionSpec<?> optionSpec = specFor( options[ i ] ); + + if ( optionSpec.acceptsArguments() && options.length > i + 1 ) { + String detectedArgument = String.valueOf( options, i + 1, options.length - 1 - i ); + optionSpec.handleOption( this, arguments, detected, detectedArgument ); + break; + } + + optionSpec.handleOption( this, arguments, detected, null ); + } + } + + void handleNonOptionArgument( String candidate, ArgumentList arguments, OptionSet detectedOptions ) { + specFor( NonOptionArgumentSpec.NAME ).handleOption( this, arguments, detectedOptions, candidate ); + } + + void noMoreOptions() { + state = OptionParserState.noMoreOptions(); + } + + boolean looksLikeAnOption( String argument ) { + return isShortOptionToken( argument ) || isLongOptionToken( argument ); + } + + boolean isRecognized( String option ) { + return recognizedOptions.contains( option ); + } + + void requiredIf( Collection<String> precedentSynonyms, String required ) { + requiredIf( precedentSynonyms, specFor( required ) ); + } + + void requiredIf( Collection<String> precedentSynonyms, OptionSpec<?> required ) { + putRequiredOption( precedentSynonyms, required, requiredIf ); + } + + void requiredUnless( Collection<String> precedentSynonyms, String required ) { + requiredUnless( precedentSynonyms, specFor( required ) ); + } + + void requiredUnless( Collection<String> precedentSynonyms, OptionSpec<?> required ) { + putRequiredOption( precedentSynonyms, required, requiredUnless ); + } + + private void putRequiredOption( Collection<String> precedentSynonyms, OptionSpec<?> required, + Map<Collection<String>, Set<OptionSpec<?>>> target ) { + + for ( String each : precedentSynonyms ) { + AbstractOptionSpec<?> spec = specFor( each ); + if ( spec == null ) + throw new UnconfiguredOptionException( precedentSynonyms ); + } + + Set<OptionSpec<?>> associated = target.get( precedentSynonyms ); + if ( associated == null ) { + associated = new HashSet<OptionSpec<?>>(); + target.put( precedentSynonyms, associated ); + } + + associated.add( required ); + } + + private AbstractOptionSpec<?> specFor( char option ) { + return specFor( String.valueOf( option ) ); + } + + private AbstractOptionSpec<?> specFor( String option ) { + return recognizedOptions.get( option ); + } + + private void reset() { + state = moreOptions( posixlyCorrect ); + } + + private static char[] extractShortOptionsFrom( String argument ) { + char[] options = new char[ argument.length() - 1 ]; + argument.getChars( 1, argument.length(), options, 0 ); + + return options; + } + + private void validateOptionCharacters( char[] options ) { + for ( char each : options ) { + String option = String.valueOf( each ); + + if ( !isRecognized( option ) ) + throw unrecognizedOption( option ); + + if ( specFor( option ).acceptsArguments() ) + return; + } + } + + private static KeyValuePair parseLongOptionWithArgument( String argument ) { + return KeyValuePair.valueOf( argument.substring( 2 ) ); + } + + private static KeyValuePair parseShortOptionWithArgument( String argument ) { + return KeyValuePair.valueOf( argument.substring( 1 ) ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * Abstraction of parser state; mostly serves to model how a parser behaves depending on whether end-of-options + * has been detected. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +abstract class OptionParserState { + static OptionParserState noMoreOptions() { + return new OptionParserState() { + @Override + protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + parser.handleNonOptionArgument( arguments.next(), arguments, detectedOptions ); + } + }; + } + + static OptionParserState moreOptions( final boolean posixlyCorrect ) { + return new OptionParserState() { + @Override + protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + String candidate = arguments.next(); + try { + if ( isOptionTerminator( candidate ) ) { + parser.noMoreOptions(); + return; + } else if ( isLongOptionToken( candidate ) ) { + parser.handleLongOptionToken( candidate, arguments, detectedOptions ); + return; + } else if ( isShortOptionToken( candidate ) ) { + parser.handleShortOptionToken( candidate, arguments, detectedOptions ); + return; + } + } catch ( UnrecognizedOptionException e ) { + if ( !parser.doesAllowsUnrecognizedOptions() ) + throw e; + } + + if ( posixlyCorrect ) + parser.noMoreOptions(); + + parser.handleNonOptionArgument( candidate, arguments, detectedOptions ); + } + }; + } + + protected abstract void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.*; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.internal.Objects.*; + +/** + * Representation of a group of detected command line options, their arguments, and non-option arguments. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public class OptionSet { + private final List<OptionSpec<?>> detectedSpecs; + private final Map<String, AbstractOptionSpec<?>> detectedOptions; + private final Map<AbstractOptionSpec<?>, List<String>> optionsToArguments; + private final Map<String, AbstractOptionSpec<?>> recognizedSpecs; + private final Map<String, List<?>> defaultValues; + + /* + * Package-private because clients don't create these. + */ + OptionSet( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) { + detectedSpecs = new ArrayList<OptionSpec<?>>(); + detectedOptions = new HashMap<String, AbstractOptionSpec<?>>(); + optionsToArguments = new IdentityHashMap<AbstractOptionSpec<?>, List<String>>(); + defaultValues = defaultValues( recognizedSpecs ); + this.recognizedSpecs = recognizedSpecs; + } + + /** + * Tells whether any options were detected. + * + * @return {@code true} if any options were detected + */ + public boolean hasOptions() { + return !detectedOptions.isEmpty(); + } + + /** + * Tells whether the given option was detected. + * + * @param option the option to search for + * @return {@code true} if the option was detected + * @see #has(OptionSpec) + */ + public boolean has( String option ) { + return detectedOptions.containsKey( option ); + } + + /** + * Tells whether the given option was detected. + * + * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> + * + * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value} + * for an option does not cause this method to return {@code true} if the option was not detected on the command + * line.</p> + * + * @param option the option to search for + * @return {@code true} if the option was detected + * @see #has(String) + */ + public boolean has( OptionSpec<?> option ) { + return optionsToArguments.containsKey( option ); + } + + /** + * Tells whether there are any arguments associated with the given option. + * + * @param option the option to search for + * @return {@code true} if the option was detected and at least one argument was detected for the option + * @see #hasArgument(OptionSpec) + */ + public boolean hasArgument( String option ) { + AbstractOptionSpec<?> spec = detectedOptions.get( option ); + return spec != null && hasArgument( spec ); + } + + /** + * Tells whether there are any arguments associated with the given option. + * + * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> + * + * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for an option does not cause this method to return {@code true} if the option was not detected on the command + * line, or if the option can take an optional argument but did not have one on the command line.</p> + * + * @param option the option to search for + * @return {@code true} if the option was detected and at least one argument was detected for the option + * @throws NullPointerException if {@code option} is {@code null} + * @see #hasArgument(String) + */ + public boolean hasArgument( OptionSpec<?> option ) { + ensureNotNull( option ); + + List<String> values = optionsToArguments.get( option ); + return values != null && !values.isEmpty(); + } + + /** + * Gives the argument associated with the given option. If the option was given an argument type, the argument + * will take on that type; otherwise, it will be a {@link String}. + * + * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for an option will cause this method to return that default value even if the option was not detected on the + * command line, or if the option can take an optional argument but did not have one on the command line.</p> + * + * @param option the option to search for + * @return the argument of the given option; {@code null} if no argument is present, or that option was not + * detected + * @throws NullPointerException if {@code option} is {@code null} + * @throws OptionException if more than one argument was detected for the option + */ + public Object valueOf( String option ) { + ensureNotNull( option ); + + AbstractOptionSpec<?> spec = detectedOptions.get( option ); + if ( spec == null ) { + List<?> defaults = defaultValuesFor( option ); + return defaults.isEmpty() ? null : defaults.get( 0 ); + } + + return valueOf( spec ); + } + + /** + * Gives the argument associated with the given option. + * + * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> + * + * @param <V> represents the type of the arguments the given option accepts + * @param option the option to search for + * @return the argument of the given option; {@code null} if no argument is present, or that option was not + * detected + * @throws OptionException if more than one argument was detected for the option + * @throws NullPointerException if {@code option} is {@code null} + * @throws ClassCastException if the arguments of this option are not of the expected type + */ + public <V> V valueOf( OptionSpec<V> option ) { + ensureNotNull( option ); + + List<V> values = valuesOf( option ); + switch ( values.size() ) { + case 0: + return null; + case 1: + return values.get( 0 ); + default: + throw new MultipleArgumentsForOptionException( option.options() ); + } + } + + /** + * <p>Gives any arguments associated with the given option. If the option was given an argument type, the + * arguments will take on that type; otherwise, they will be {@link String}s.</p> + * + * @param option the option to search for + * @return the arguments associated with the option, as a list of objects of the type given to the arguments; an + * empty list if no such arguments are present, or if the option was not detected + * @throws NullPointerException if {@code option} is {@code null} + */ + public List<?> valuesOf( String option ) { + ensureNotNull( option ); + + AbstractOptionSpec<?> spec = detectedOptions.get( option ); + return spec == null ? defaultValuesFor( option ) : valuesOf( spec ); + } + + /** + * <p>Gives any arguments associated with the given option. If the option was given an argument type, the + * arguments will take on that type; otherwise, they will be {@link String}s.</p> + * + * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> + * + * @param <V> represents the type of the arguments the given option accepts + * @param option the option to search for + * @return the arguments associated with the option; an empty list if no such arguments are present, or if the + * option was not detected + * @throws NullPointerException if {@code option} is {@code null} + * @throws OptionException if there is a problem converting the option's arguments to the desired type; for + * example, if the type does not implement a correct conversion constructor or method + */ + public <V> List<V> valuesOf( OptionSpec<V> option ) { + ensureNotNull( option ); + + List<String> values = optionsToArguments.get( option ); + if ( values == null || values.isEmpty() ) + return defaultValueFor( option ); + + AbstractOptionSpec<V> spec = (AbstractOptionSpec<V>) option; + List<V> convertedValues = new ArrayList<V>(); + for ( String each : values ) + convertedValues.add( spec.convert( each ) ); + + return unmodifiableList( convertedValues ); + } + + /** + * Gives the set of options that were detected, in the form of {@linkplain OptionSpec}s, in the order in which the + * options were found on the command line. + * + * @return the set of detected command line options + */ + public List<OptionSpec<?>> specs() { + List<OptionSpec<?>> specs = detectedSpecs; + specs.remove( detectedOptions.get( NonOptionArgumentSpec.NAME ) ); + + return unmodifiableList( specs ); + } + + /** + * Gives all declared options as a map of string to {@linkplain OptionSpec}. + * + * @return the declared options as a map + */ + public Map<OptionSpec<?>, List<?>> asMap() { + Map<OptionSpec<?>, List<?>> map = new HashMap<OptionSpec<?>, List<?>>(); + for ( AbstractOptionSpec<?> spec : recognizedSpecs.values() ) + if ( !spec.representsNonOptions() ) + map.put( spec, valuesOf( spec ) ); + return unmodifiableMap( map ); + } + + /** + * @return the detected non-option arguments + */ + public List<?> nonOptionArguments() { + return unmodifiableList( valuesOf( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) ); + } + + void add( AbstractOptionSpec<?> spec ) { + addWithArgument( spec, null ); + } + + void addWithArgument( AbstractOptionSpec<?> spec, String argument ) { + detectedSpecs.add( spec ); + + for ( String each : spec.options() ) + detectedOptions.put( each, spec ); + + List<String> optionArguments = optionsToArguments.get( spec ); + + if ( optionArguments == null ) { + optionArguments = new ArrayList<String>(); + optionsToArguments.put( spec, optionArguments ); + } + + if ( argument != null ) + optionArguments.add( argument ); + } + + @Override + public boolean equals( Object that ) { + if ( this == that ) + return true; + + if ( that == null || !getClass().equals( that.getClass() ) ) + return false; + + OptionSet other = (OptionSet) that; + Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments = + new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments ); + Map<AbstractOptionSpec<?>, List<String>> otherOptionsToArguments = + new HashMap<AbstractOptionSpec<?>, List<String>>( other.optionsToArguments ); + return detectedOptions.equals( other.detectedOptions ) + && thisOptionsToArguments.equals( otherOptionsToArguments ); + } + + @Override + public int hashCode() { + Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments = + new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments ); + return detectedOptions.hashCode() ^ thisOptionsToArguments.hashCode(); + } + + @SuppressWarnings( "unchecked" ) + private <V> List<V> defaultValuesFor( String option ) { + if ( defaultValues.containsKey( option ) ) + return (List<V>) defaultValues.get( option ); + + return emptyList(); + } + + private <V> List<V> defaultValueFor( OptionSpec<V> option ) { + return defaultValuesFor( option.options().iterator().next() ); + } + + private static Map<String, List<?>> defaultValues( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) { + Map<String, List<?>> defaults = new HashMap<String, List<?>>(); + for ( Map.Entry<String, AbstractOptionSpec<?>> each : recognizedSpecs.entrySet() ) + defaults.put( each.getKey(), each.getValue().defaultValues() ); + return defaults; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.List; + +/** + * Describes options that an option parser recognizes. + * + * <p>Instances of this interface are returned by the "fluent interface" methods to allow retrieval of option arguments + * in a type-safe manner. Here's an example:</p> + * + * <pre><code> + * OptionParser parser = new OptionParser(); + * <strong>OptionSpec<Integer></strong> count = + * parser.accepts( "count" ).withRequiredArg().ofType( Integer.class ); + * OptionSet options = parser.parse( "--count", "2" ); + * assert options.has( count ); + * int countValue = options.valueOf( count ); + * assert countValue == count.value( options ); + * List<Integer> countValues = options.valuesOf( count ); + * assert countValues.equals( count.values( options ) ); + * </code></pre> + * + * @param <V> represents the type of the arguments this option accepts + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public interface OptionSpec<V> { + /** + * Gives any arguments associated with the given option in the given set of detected options. + * + * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for this option will cause this method to return that default value even if this option was not detected on the + * command line, or if this option can take an optional argument but did not have one on the command line.</p> + * + * @param detectedOptions the detected options to search in + * @return the arguments associated with this option; an empty list if no such arguments are present, or if this + * option was not detected + * @throws OptionException if there is a problem converting this option's arguments to the desired type; for + * example, if the type does not implement a correct conversion constructor or method + * @throws NullPointerException if {@code detectedOptions} is {@code null} + * @see OptionSet#valuesOf(OptionSpec) + */ + List<V> values( OptionSet detectedOptions ); + + /** + * Gives the argument associated with the given option in the given set of detected options. + * + * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for this option will cause this method to return that default value even if this option was not detected on the + * command line, or if this option can take an optional argument but did not have one on the command line.</p> + * + * @param detectedOptions the detected options to search in + * @return the argument of the this option; {@code null} if no argument is present, or that option was not detected + * @throws OptionException if more than one argument was detected for the option + * @throws NullPointerException if {@code detectedOptions} is {@code null} + * @throws ClassCastException if the arguments of this option are not of the expected type + * @see OptionSet#valueOf(OptionSpec) + */ + V value( OptionSet detectedOptions ); + + /** + * @return the string representations of this option + */ + Collection<String> options(); + + /** + * Tells whether this option is designated as a "help" option. The presence of a "help" option on a command line + * means that missing "required" options will not cause parsing to fail. + * + * @return whether this option is designated as a "help" option + */ + boolean isForHelp(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Allows callers to specify whether a given option accepts arguments (required or optional). + * + * <p>Instances are returned from {@link OptionParser#accepts(String)} to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:</p> + * + * <pre><code> + * OptionParser parser = new OptionParser(); + * parser.accepts( "c" ).<strong>withRequiredArg()</strong>.ofType( Integer.class ); + * </code></pre> + * + * <p>If no methods are invoked on an instance of this class, then that instance's option will accept no argument.</p> + * + * <p>Note that you should not use the fluent interface clauses in a way that would defeat the typing of option + * arguments:</p> + * + * <pre><code> + * OptionParser parser = new OptionParser(); + * ArgumentAcceptingOptionSpec<String> optionC = + * parser.accepts( "c" ).withRequiredArg(); + * <strong>optionC.ofType( Integer.class ); // DON'T THROW AWAY THE TYPE!</strong> + * + * String value = parser.parse( "-c", "2" ).valueOf( optionC ); // ClassCastException + * </code></pre> + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +public class OptionSpecBuilder extends NoArgumentOptionSpec { + private final OptionParser parser; + + OptionSpecBuilder( OptionParser parser, Collection<String> options, String description ) { + super( options, description ); + + this.parser = parser; + attachToParser(); + } + + private void attachToParser() { + parser.recognize( this ); + } + + /** + * Informs an option parser that this builder's option requires an argument. + * + * @return a specification for the option + */ + public ArgumentAcceptingOptionSpec<String> withRequiredArg() { + ArgumentAcceptingOptionSpec<String> newSpec = + new RequiredArgumentOptionSpec<String>( options(), description() ); + parser.recognize( newSpec ); + + return newSpec; + } + + /** + * Informs an option parser that this builder's option accepts an optional argument. + * + * @return a specification for the option + */ + public ArgumentAcceptingOptionSpec<String> withOptionalArg() { + ArgumentAcceptingOptionSpec<String> newSpec = + new OptionalArgumentOptionSpec<String>( options(), description() ); + parser.recognize( newSpec ); + + return newSpec; + } + + /** + * <p>Informs an option parser that this builder's option is required if the given option is present on the command + * line.</p> + * + * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(String, String...) + * requiredUnless} to avoid conflicts.</p> + * + * @param dependent an option whose presence on a command line makes this builder's option required + * @param otherDependents other options whose presence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws OptionException if any of the dependent options haven't been configured in the parser yet + */ + public OptionSpecBuilder requiredIf( String dependent, String... otherDependents ) { + List<String> dependents = validatedDependents( dependent, otherDependents ); + for ( String each : dependents ) { + parser.requiredIf( options(), each ); + } + + return this; + } + + /** + * <p>Informs an option parser that this builder's option is required if the given option is present on the command + * line.</p> + * + * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(OptionSpec, OptionSpec[]) + * requiredUnless} to avoid conflicts.</p> + * + * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> + * + * @param dependent the option whose presence on a command line makes this builder's option required + * @param otherDependents other options whose presence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public OptionSpecBuilder requiredIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) { + parser.requiredIf( options(), dependent ); + for ( OptionSpec<?> each : otherDependents ) + parser.requiredIf( options(), each ); + + return this; + } + + /** + * <p>Informs an option parser that this builder's option is required if the given option is absent on the command + * line.</p> + * + * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[]) + * requiredIf} to avoid conflicts.</p> + * + * @param dependent an option whose absence on a command line makes this builder's option required + * @param otherDependents other options whose absence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws OptionException if any of the dependent options haven't been configured in the parser yet + */ + public OptionSpecBuilder requiredUnless( String dependent, String... otherDependents ) { + List<String> dependents = validatedDependents( dependent, otherDependents ); + for ( String each : dependents ) { + parser.requiredUnless( options(), each ); + } + return this; + } + + /** + * <p>Informs an option parser that this builder's option is required if the given option is absent on the command + * line.</p> + * + * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[]) + * requiredIf} to avoid conflicts.</p> + * + * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> + * + * @param dependent the option whose absence on a command line makes this builder's option required + * @param otherDependents other options whose absence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public OptionSpecBuilder requiredUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) { + parser.requiredUnless( options(), dependent ); + for ( OptionSpec<?> each : otherDependents ) + parser.requiredUnless( options(), each ); + + return this; + } + + private List<String> validatedDependents( String dependent, String... otherDependents ) { + List<String> dependents = new ArrayList<String>(); + dependents.add( dependent ); + Collections.addAll( dependents, otherDependents ); + + for ( String each : dependents ) { + if ( !parser.isRecognized( each ) ) + throw new UnconfiguredOptionException( each ); + } + + return dependents; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.NoSuchElementException; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * Tokenizes a short option specification string. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class OptionSpecTokenizer { + private static final char POSIXLY_CORRECT_MARKER = '+'; + private static final char HELP_MARKER = '*'; + + private String specification; + private int index; + + OptionSpecTokenizer( String specification ) { + if ( specification == null ) + throw new NullPointerException( "null option specification" ); + + this.specification = specification; + } + + boolean hasMore() { + return index < specification.length(); + } + + AbstractOptionSpec<?> next() { + if ( !hasMore() ) + throw new NoSuchElementException(); + + + String optionCandidate = String.valueOf( specification.charAt( index ) ); + index++; + + AbstractOptionSpec<?> spec; + if ( RESERVED_FOR_EXTENSIONS.equals( optionCandidate ) ) { + spec = handleReservedForExtensionsToken(); + + if ( spec != null ) + return spec; + } + + ensureLegalOption( optionCandidate ); + + if ( hasMore() ) { + boolean forHelp = false; + if ( specification.charAt( index ) == HELP_MARKER ) { + forHelp = true; + ++index; + } + spec = hasMore() && specification.charAt( index ) == ':' + ? handleArgumentAcceptingOption( optionCandidate ) + : new NoArgumentOptionSpec( optionCandidate ); + if ( forHelp ) + spec.forHelp(); + } else + spec = new NoArgumentOptionSpec( optionCandidate ); + + return spec; + } + + void configure( OptionParser parser ) { + adjustForPosixlyCorrect( parser ); + + while ( hasMore() ) + parser.recognize( next() ); + } + + private void adjustForPosixlyCorrect( OptionParser parser ) { + if ( POSIXLY_CORRECT_MARKER == specification.charAt( 0 ) ) { + parser.posixlyCorrect( true ); + specification = specification.substring( 1 ); + } + } + + private AbstractOptionSpec<?> handleReservedForExtensionsToken() { + if ( !hasMore() ) + return new NoArgumentOptionSpec( RESERVED_FOR_EXTENSIONS ); + + if ( specification.charAt( index ) == ';' ) { + ++index; + return new AlternativeLongOptionSpec(); + } + + return null; + } + + private AbstractOptionSpec<?> handleArgumentAcceptingOption( String candidate ) { + index++; + + if ( hasMore() && specification.charAt( index ) == ':' ) { + index++; + return new OptionalArgumentOptionSpec<String>( candidate ); + } + + return new RequiredArgumentOptionSpec<String>( candidate ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Specification of an option that accepts an optional argument. + * + * @param <V> represents the type of the arguments this option accepts + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class OptionalArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> { + OptionalArgumentOptionSpec( String option ) { + super( option, false ); + } + + OptionalArgumentOptionSpec( Collection<String> options, String description ) { + super( options, false, description ); + } + + @Override + protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + if ( arguments.hasMore() ) { + String nextArgument = arguments.peek(); + + if ( !parser.looksLikeAnOption( nextArgument ) ) + handleOptionArgument( parser, detectedOptions, arguments ); + else if ( isArgumentOfNumberType() && canConvertArgument( nextArgument ) ) + addArguments( detectedOptions, arguments.next() ); + else + detectedOptions.add( this ); + } + else + detectedOptions.add( this ); + } + + private void handleOptionArgument( OptionParser parser, OptionSet detectedOptions, ArgumentList arguments ) { + if ( parser.posixlyCorrect() ) { + detectedOptions.add( this ); + parser.noMoreOptions(); + } + else + addArguments( detectedOptions, arguments.next() ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +import static java.lang.Character.*; + +/** + * Can tell whether or not options are well-formed. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +final class ParserRules { + static final char HYPHEN_CHAR = '-'; + static final String HYPHEN = String.valueOf( HYPHEN_CHAR ); + static final String DOUBLE_HYPHEN = "--"; + static final String OPTION_TERMINATOR = DOUBLE_HYPHEN; + static final String RESERVED_FOR_EXTENSIONS = "W"; + + private ParserRules() { + throw new UnsupportedOperationException(); + } + + static boolean isShortOptionToken( String argument ) { + return argument.startsWith( HYPHEN ) + && !HYPHEN.equals( argument ) + && !isLongOptionToken( argument ); + } + + static boolean isLongOptionToken( String argument ) { + return argument.startsWith( DOUBLE_HYPHEN ) && !isOptionTerminator( argument ); + } + + static boolean isOptionTerminator( String argument ) { + return OPTION_TERMINATOR.equals( argument ); + } + + static void ensureLegalOption( String option ) { + if ( option.startsWith( HYPHEN ) ) + throw new IllegalOptionSpecificationException( String.valueOf( option ) ); + + for ( int i = 0; i < option.length(); ++i ) + ensureLegalOptionCharacter( option.charAt( i ) ); + } + + static void ensureLegalOptions( Collection<String> options ) { + for ( String each : options ) + ensureLegalOption( each ); + } + + private static void ensureLegalOptionCharacter( char option ) { + if ( !( isLetterOrDigit( option ) || isAllowedPunctuation( option ) ) ) + throw new IllegalOptionSpecificationException( String.valueOf( option ) ); + } + + private static boolean isAllowedPunctuation( char option ) { + String allowedPunctuation = "?." + HYPHEN_CHAR; + return allowedPunctuation.indexOf( option ) != -1; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,3 @@ +JOpt Simple, Version 4.6 +https://pholser.github.io/jopt-simple/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Specification of an option that accepts a required argument. + * + * @param <V> represents the type of the arguments this option accepts + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class RequiredArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> { + RequiredArgumentOptionSpec( String option ) { + super( option, true ); + } + + RequiredArgumentOptionSpec( Collection<String> options, String description ) { + super( options, true, description ); + } + + @Override + protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + if ( !arguments.hasMore() ) + throw new OptionMissingRequiredArgumentException( options() ); + + addArguments( detectedOptions, arguments.next() ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java Wed Jul 05 21:16:36 2017 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +/** + * Thrown when the option parser detects an unacceptable number of {@code linkplain NonOptionArgumentSpec + * non-option arguments}. + * + * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> + */ +class UnacceptableNumberOfNonOptionsException extends OptionException { + private static final long serialVersionUID = -1L; + private final int minimum;