# bash completion for java, javac and javadoc              -*- shell-script -*-

# available path elements completion
_java_path()
{
    cur=${cur##*:}
    _filedir '@(jar|zip)'
}

# exact classpath determination
_java_find_classpath()
{
    local i

    # search first in current options
    for (( i=1; i < cword; i++ )); do
        if [[ "${words[i]}" == -@(cp|classpath) ]]; then
            classpath=${words[i+1]}
            break
        fi
    done

    # default to environment
    [[ -z $classpath ]] && classpath=$CLASSPATH

    # default to current directory
    [[ -z $classpath ]] && classpath=.
}

# exact sourcepath determination
_java_find_sourcepath()
{
    local i

    # search first in current options
    for (( i=1; i < cword; i++ )); do
        if [[ "${words[i]}" == -sourcepath ]]; then
            sourcepath=${words[i+1]}
            break
        fi
    done

    # default to classpath
    if [[ -z $sourcepath ]]; then
        local classpath
        _java_find_classpath
        sourcepath=$classpath
    fi
}

# available classes completion
_java_classes()
{
    local classpath i

    # find which classpath to use
    _java_find_classpath

    # convert package syntax to path syntax
    cur=${cur//.//}
    # parse each classpath element for classes
    for i in ${classpath//:/ }; do
        if [[ "$i" == *.@(jar|zip) && -r $i ]]; then
            if type zipinfo &>/dev/null; then
                COMPREPLY+=( $( zipinfo -1 "$i" "$cur*" 2>/dev/null | \
                    command grep '^[^$]*\.class$' ) )
            else
                COMPREPLY+=( $( jar tf "$i" "$cur" | \
                    command grep '^[^$]*\.class$' ) )
            fi

        elif [[ -d $i ]]; then
            COMPREPLY+=(
                $( compgen -d -- "$i/$cur" | command sed -e "s|^$i/\(.*\)|\1.|" )
                $( compgen -f -X '!*.class' -- "$i/$cur" | \
                    command sed -e '/\$/d' -e "s|^$i/||" )
            )
            [[ $COMPREPLY == *.class ]] || compopt -o nospace

            # FIXME: if we have foo.class and foo/, the completion
            # returns "foo/"... how to give precedence to files
            # over directories?
        fi
    done

    # remove class extension
    COMPREPLY=( ${COMPREPLY[@]%.class} )
    # convert path syntax to package syntax
    COMPREPLY=( ${COMPREPLY[@]//\//.} )
}

# available packages completion
_java_packages()
{
    local sourcepath i

    # find which sourcepath to use
    _java_find_sourcepath

    # convert package syntax to path syntax
    cur=${cur//.//}
    # parse each sourcepath element for packages
    for i in ${sourcepath//:/ }; do
        if [[ -d $i ]]; then
            COMPREPLY+=( $( command ls -F -d $i/$cur* 2>/dev/null | \
                command sed -e 's|^'$i'/||' ) )
        fi
    done
    # keep only packages
    COMPREPLY=( $( tr " " "\n" <<<"${COMPREPLY[@]}" | command grep "/$" ) )
    # remove packages extension
    COMPREPLY=( ${COMPREPLY[@]%/} )
    # convert path syntax to package syntax
    cur=${COMPREPLY[@]//\//.}
}

# java completion
#
_java()
{
    local cur prev words cword
    _init_completion -n : || return

    local i

    for ((i=1; i < $cword; i++)); do
        case ${words[$i]} in
            -cp|-classpath)
                ((i++)) # skip the classpath string.
                ;;
            -*)
                # this is an option, not a class/jarfile name.
                ;;
            *)
                # once we've seen a class, just do filename completion
                _filedir
                return 0
                ;;
        esac
    done

    case $cur in
        # standard option completions
        -verbose:*)
            COMPREPLY=( $( compgen -W 'class gc jni' -- "${cur#*:}" ) )
            return 0
            ;;
        -javaagent:*)
            cur=${cur#*:}
            _filedir '@(jar|zip)'
            return 0
            ;;
        -agentpath:*)
            cur=${cur#*:}
            _filedir so
            return 0
            ;;
        # various non-standard option completions
        -splash:*)
            cur=${cur#*:}
            _filedir '@(gif|jp?(e)g|png)'
            return 0
            ;;
        -Xbootclasspath*:*)
            _java_path
            return 0
            ;;
        -Xcheck:*)
            COMPREPLY=( $( compgen -W 'jni' -- "${cur#*:}" ) )
            return 0
            ;;
        -Xgc:*)
            COMPREPLY=( $( compgen -W 'singlecon gencon singlepar genpar' \
                -- "${cur#*:}" ) )
            return 0
            ;;
        -Xgcprio:*)
            COMPREPLY=( $( compgen -W 'throughput pausetime deterministic' \
                -- "${cur#*:}" ) )
            return 0
            ;;
        -Xloggc:*|-Xverboselog:*)
            cur=${cur#*:}
            _filedir
            return 0
            ;;
        -Xshare:*)
            COMPREPLY=( $( compgen -W 'auto off on' -- "${cur#*:}" ) )
            return 0
            ;;
        -Xverbose:*)
            COMPREPLY=( $( compgen -W 'memory load jni cpuinfo codegen opt
                gcpause gcreport' -- "${cur#*:}" ) )
            return 0
            ;;
        -Xverify:*)
            COMPREPLY=( $( compgen -W 'all none remote' -- "${cur#*:}" ) )
            return 0
            ;;
        # the rest that we have no completions for
        -D*|-*:*)
            return 0
            ;;
    esac

    case $prev in
        -cp|-classpath)
            _java_path
            return 0
            ;;
    esac

    if [[ "$cur" == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" -help )' -- "$cur" ) )
        [[ $cur == -X* ]] && \
            COMPREPLY+=( $( compgen -W '$( _parse_help "$1" -X )' -- "$cur" ) )
    else
        if [[ "$prev" == -jar ]]; then
            # jar file completion
            _filedir jar
        else
            # classes completion
            _java_classes
        fi
    fi

    [[ $COMPREPLY == -*[:=] ]] && compopt -o nospace

    __ltrim_colon_completions "$cur"
} &&
complete -F _java java

_javadoc()
{
    local cur prev words cword
    _init_completion || return

    case $prev in
        -overview|-helpfile)
            _filedir '?(x)htm?(l)'
            return 0
            ;;
        -stylesheetfile)
            _filedir css
            return 0
            ;;
        -d|-link|-linkoffline)
            _filedir -d
            return 0
            ;;
        -classpath|-bootclasspath|-docletpath|-sourcepath|-extdirs)
            _java_path
            return 0
            ;;
    esac

    # -linkoffline takes two arguments
    if [[ $cword -gt 2 && ${words[$cword-2]} == -linkoffline ]]; then
        _filedir -d
        return
    fi

    if [[ "$cur" == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" -help )' -- "$cur" ) )
    else
        # source files completion
        _filedir java
        # packages completion
        _java_packages
    fi
} &&
complete -F _javadoc javadoc

_javac()
{
    local cur prev words cword
    _init_completion -n : || return

    case $prev in
        -d)
            _filedir -d
            return 0
            ;;
        -classpath|-bootclasspath|-sourcepath|-extdirs)
            _java_path
            return 0
            ;;
    esac

    if [[ $cur == -+([a-zA-z0-9-_]):* ]]; then
        # Parse required options from -foo:{bar,quux,baz}
        local helpopt=-help
        [[ $cur == -X* ]] && helpopt=-X
        # For some reason there may be -g:none AND -g:{lines,source,vars};
        # convert the none case to the curly brace format so it parses like
        # the others.
        local opts=$( "$1" $helpopt 2>&1 | command sed -e 's/-g:none/-g:{none}/' -ne \
            "s/^[[:space:]]*${cur%%:*}:{\([^}]\{1,\}\)}.*/\1/p" )
        COMPREPLY=( $( compgen -W "${opts//,/ }" -- "${cur#*:}" ) )
        return
    fi

    if [[ "$cur" == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" -help )' -- "$cur" ) )
        [[ $cur == -X* ]] && \
            COMPREPLY+=( $( compgen -W '$( _parse_help "$1" -X )' -- "$cur" ) )
    else
        # source files completion
        _filedir java
    fi

    [[ $COMPREPLY == -*[:=] ]] && compopt -o nospace

    __ltrim_colon_completions "$cur"
} &&
complete -F _javac javac

# ex: ts=4 sw=4 et filetype=sh
