pax_global_header00006660000000000000000000000064150167476670014535gustar00rootroot0000000000000052 comment=2476054f99a398768d1bc2e85d659cbd26c7114e jpype-1.6.0/000077500000000000000000000000001501674766700126705ustar00rootroot00000000000000jpype-1.6.0/.azure/000077500000000000000000000000001501674766700140745ustar00rootroot00000000000000jpype-1.6.0/.azure/build.yml000066400000000000000000000073141501674766700157230ustar00rootroot00000000000000# JPype CI pipeline # NOTE: # - Some commented-out sections (e.g., Python 3.14, JDK 22) are placeholders for future versions. # These will be activated once the corresponding tools are available on Azure (usually ~4 months after release). # - Debug jobs are only activated when issues cannot be replicated locally; otherwise, they remain disabled. # - macOS coverage is limited to the oldest and newest supported Python/JDK versions, as most issues are platform-independent and our primary support is for Linux. # - We only test against LTS (Long-Term Support) JDK releases, as newer versions (e.g., JDK 22) are not yet available on Azure. # - Fast tests are enabled on macOS to avoid long build times, especially for tests with high overhead (e.g., leak checkers). # - Documentation jobs are run only on Ubuntu, since documentation generation is OS-agnostic and only needs to be validated for HTML publication. trigger: branches: include: - master - releases/* paths: include: - .azure/build.yml - doc/* - setup.py - setupext/* - jpype/* - native/* - test/* variables: # indicate whether the testsuite should skip long-running tests or not. - name: jpypetest.fast value: 'false' - name: system.debug value: 'true' jobs: - job: Deps pool: vmImage: "ubuntu-latest" steps: - template: scripts/ivy.yml - job: Documentation pool: vmImage: "ubuntu-latest" steps: - template: scripts/documentation.yml - job: Coverage pool: vmImage: "ubuntu-latest" dependsOn: Deps steps: - template: scripts/deps.yml - template: scripts/coverage.yml - job: Tracing pool: vmImage: "ubuntu-latest" steps: - template: scripts/tracing.yml - job: Test dependsOn: Deps strategy: matrix: # Linux linux_py39_jdk11: imageName: "ubuntu-latest" python.version: '3.9' jdk.version: '11' linux_py310_jdk17: imageName: "ubuntu-latest" python.version: '3.10' jdk.version: '17' linux_py311_jdk17: imageName: "ubuntu-latest" python.version: '3.11' jdk.version: '17' linux_py312_jdk17: imageName: "ubuntu-latest" python.version: '3.12' jdk.version: '17' linux_py313_jdk17: imageName: "ubuntu-latest" python.version: "3.13" jdk.version: '17' # jdk 22 is not there yet. #linux_py314_jdk22: # imageName: "ubuntu-latest" # python.version: "3.14.0-alpha.0" # jdk.version: '22' # Windows windows_py39_jdk11: imageName: "windows-2022" python.version: '3.9' jdk.version: '11' windows_py310_jdk11: imageName: "windows-2022" python.version: '3.10' jdk.version: '11' windows_py311_jdk17: imageName: "windows-2022" python.version: '3.11' jdk.version: '17' windows_py312_jdk21: imageName: "windows-2022" python.version: '3.12' jdk.version: '21' # OSX, we only test an old Python version with JDK8 and recent Py with recent JDK. mac_py39_jdk11: imageName: "macos-13" python.version: '3.9' jpypetest.fast: 'true' jdk.version: '11' mac_py312_jdk17: imageName: "macos-13" python.version: '3.12' jpypetest.fast: 'true' jdk.version: '17' pool: vmImage: $(imageName) steps: - template: scripts/deps.yml - template: scripts/test.yml - job: Debug condition: eq(1,0) dependsOn: Deps strategy: matrix: linux_py38_jdk11: imageName: "ubuntu-16.04" jdk.version: "11" python.version: '3.9' pool: vmImage: $(imageName) steps: - template: scripts/deps.yml - template: scripts/debug.yml jpype-1.6.0/.azure/doc-requirements.txt000066400000000000000000000001731501674766700201240ustar00rootroot00000000000000# TODO: consider unpinning these? Pygments docutils commonmark recommonmark sphinx sphinx-rtd-theme readthedocs-sphinx-ext jpype-1.6.0/.azure/release.yml000066400000000000000000000143561501674766700162500ustar00rootroot00000000000000# JPype Release pipeline # NOTES: # - This pipeline triggers only on changes to release branches or versioning files. # - ManyLinux builds cover major architectures (x86_64, aarch64, i686). # - ARM and musllinux jobs are included as commented placeholders for future support. # - All jobs use JDK 11 (LTS); newer JDKs (e.g., JDK 22) are not yet available on Azure. # - Python versions are updated as new releases become available on Azure-hosted agents. # - macOS and Windows wheels are built for all supported Python versions. # - On macOS, Python is installed via a custom script for compatibility. # - Artifact publishing is handled consistently across all platforms. # - To debug a specific build job, set its `condition` to `eq(1,1)` and others to `eq(1,0)`. trigger: none pr: branches: include: - releases/* paths: include: - .bumpversion.cfg - .azure/release.yml variables: package_name: jpype1 run_goal: 1 # Set to 1 to enable, 0 to disable, 2 on for special targets stages: - stage: Initial jobs: - job: SourceDistribution pool: vmImage: "ubuntu-latest" steps: - template: scripts/sdist.yml parameters: artifact: true - template: scripts/ivy.yml - stage: Package jobs: # Consider switch to manylinux_2_28 for next release # x86_64 build - job: ManyLinux_x86_64 condition: eq(variables['run_goal'], 1) timeoutInMinutes: 360 displayName: "Build manylinux2014_x86_64" pool: vmImage: "ubuntu-latest" variables: arch: x86_64 plat: manylinux2014_x86_64 image: quay.io/pypa/manylinux2014_x86_64 python.architecture: x64 steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml # aarch64 build - job: ManyLinux_aarch64 condition: eq(variables['run_goal'], 1) timeoutInMinutes: 360 displayName: "Build manylinux2014_aarch64" pool: vmImage: "ubuntu-latest" variables: arch: aarch64 plat: manylinux2014_aarch64 image: quay.io/pypa/manylinux2014_aarch64 python.architecture: aarch64 steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml # i686 build - job: ManyLinux_i686 condition: eq(variables['run_goal'], 1) timeoutInMinutes: 360 displayName: "Build manylinux2014_i686" pool: vmImage: "ubuntu-latest" variables: arch: i686 plat: manylinux2014_i686 image: quay.io/pypa/manylinux2014_i686 python.architecture: x86 steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml # Consider adding these after we have the reverse bridge. # I don't want to promise something I can't deliver long term. - job: Musllinux_x86_64 condition: eq(variables['run_goal'], 2) timeoutInMinutes: 360 displayName: "Build musllinux_1_1_x86_64" pool: vmImage: "ubuntu-latest" variables: arch: x86_64 plat: musllinux_1_1_x86_64 image: quay.io/pypa/musllinux_1_1_x86_64 python.architecture: x64 steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml - job: Musllinux_aarch64 condition: eq(variables['run_goal'], 3) timeoutInMinutes: 360 displayName: "Build musllinux_1_1_aarch64" pool: vmImage: "ubuntu-latest" variables: arch: aarch64 plat: musllinux_1_1_aarch64 image: quay.io/pypa/musllinux_1_1_aarch64 python.architecture: aarch64 steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml - job: Manylinux2014_ppc64le condition: eq(variables['run_goal'], 4) timeoutInMinutes: 360 displayName: "Build manylinux2014_ppc64le" pool: vmImage: "ubuntu-latest" variables: arch: ppc64le plat: manylinux2014_ppc64le image: quay.io/pypa/manylinux2014_ppc64le python.architecture: ppc64le steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml - job: Manylinux2014_s390x condition: eq(variables['run_goal'], 5) timeoutInMinutes: 360 displayName: "Build manylinux2014_s390x" pool: vmImage: "ubuntu-latest" variables: arch: s390x plat: manylinux2014_s390x image: quay.io/pypa/manylinux2014_s390x python.architecture: s390x steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml - job: Windows_x64 condition: eq(variables['run_goal'], 1) strategy: matrix: Python39: python.version: '3.9' python.architecture: 'x64' Python310: python.version: '3.10' python.architecture: 'x64' Python311: python.version: '3.11' python.architecture: 'x64' Python312: python.version: '3.12' python.architecture: 'x64' Python313: python.version: '3.13' python.architecture: 'x64' pool: vmImage: "windows-2022" steps: - template: scripts/deps.yml - template: scripts/python.yml parameters: version: '$(python.version)' architecture: '$(python.architecture)' - template: scripts/jdk.yml parameters: version: '11' - template: scripts/wheels.yml - template: scripts/publish-dist.yml - job: OSX condition: eq(variables['run_goal'], 1) variables: python.architecture: 'x64' strategy: matrix: Python39: python.version: '3.9' Python310: python.version: '3.10' Python311: python.version: '3.11' Python312: python.version: '3.12' Python313: python.version: '3.13' pool: vmImage: "macos-13" steps: - template: scripts/deps.yml - script: .azure/scripts/osx-python.sh '$(python.version)' displayName: Install Python.org Python - template: scripts/jdk.yml parameters: version: '11' - template: scripts/wheels.yml - template: scripts/publish-dist.yml jpype-1.6.0/.azure/scripts/000077500000000000000000000000001501674766700155635ustar00rootroot00000000000000jpype-1.6.0/.azure/scripts/build-wheels.sh000077500000000000000000000023031501674766700205040ustar00rootroot00000000000000#!/bin/bash set -e -x pys=() echo "Available Python bins:" ls -d /opt/python/cp*/bin for pybin in /opt/python/cp*/bin; do # Exclude 3.6, 3.7, 3.8 and any alpha/beta/rc release if [[ "$dir" =~ ^cp3(6|7|8|9|14)-cp3(6|7|8|9|14)t?$ ]]; then continue fi pys+=("$pybin") done # Show what you found for debugging echo "Found Python bins:" printf '%s\n' "${pys[@]}" # Compile wheels for PYBIN in "${pys[@]}"; do echo "Compile $PYBIN" ls -l /io/dist "${PYBIN}/pip" install -r /io/dev-requirements.txt "${PYBIN}/pip" wheel /io/dist/$package_name-*.tar.gz -w wheelhouse/ -v done echo "==============" # Bundle external shared libraries into the wheels for whl in wheelhouse/$package_name-*.whl; do echo "Audit $whl" auditwheel repair --plat $PLAT "$whl" -w /io/wheelhouse/ done echo "==============" # Install packages and test for PYBIN in "${pys[@]}"; do echo "Test install $PYBIN $package_name" "${PYBIN}/python" -m pip install $package_name --no-index -f /io/wheelhouse # Manylinux does not have a JVM so there is no way to test the wheel in the docker # "${PYBIN}/pip" install -r /io/test-requirements.txt # "${PYBIN}/pytest" /io/test/jpypetest done jpype-1.6.0/.azure/scripts/coverage.yml000066400000000000000000000026271501674766700201100ustar00rootroot00000000000000# This task produces the coverage reports and publishes them to codecov. steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - template: jdk.yml parameters: version: 11 - script: | python setup.py test_java pip install gcovr pytest-cov -r test-requirements.txt pip install numpy setuptools displayName: 'Install requirements' - script: | python setup.py develop --enable-coverage --enable-build-jar displayName: 'Build' - script: | python -m pytest -v -s test/jpypetest --cov=jpype --cov-report=xml:coverage_py.xml --classpath="build/classes" --jacoco --checkjni displayName: 'Test' - script: | gcovr -r . --xml -o coverage.xml --exclude-unreachable-branches --exclude-throw-branches java -jar lib/org.jacoco.cli-0.8.5-nodeps.jar report build/coverage/jacoco.exec --classfiles build/classes/ --xml coverage_java.xml --sourcefiles native/java bash <(curl -s https://codecov.io/bash) -f coverage.xml -f coverage_py.xml -f coverage_java.xml -X gcov displayName: 'Report' - task: PublishCodeCoverageResults@2 inputs: codeCoverageTool: 'JaCoCo' summaryFileLocation: coverage_java.xml pathToSources: native/java - task: PublishCodeCoverageResults@2 inputs: codeCoverageTool: 'cobertura' summaryFileLocation: coverage.xml - task: PublishCodeCoverageResults@2 inputs: codeCoverageTool: 'cobertura' summaryFileLocation: coverage_py.xml jpype-1.6.0/.azure/scripts/debug.yml000066400000000000000000000012401501674766700173710ustar00rootroot00000000000000# This task is used when there is an error in the CI that can't be # replicated locally. steps: - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' - template: jdk.yml parameters: version: '$(jdk.version)' - script: | sudo apt install gdb pip install ./ displayName: 'Build module' - script: python -c "import jpype" displayName: 'Check module' - script: | python setup.py test_java pip install -r test-requirements.txt pip install numpy jedi gdb -ex 'handle SIGSEGV nostop noprint pass' -ex "run -m pytest -v test/jpypetest/test_jstring.py --checkjni" -ex "bt" -ex "quit" python displayName: 'Debug module' jpype-1.6.0/.azure/scripts/deps.yml000066400000000000000000000001631501674766700172410ustar00rootroot00000000000000steps: - task: DownloadPipelineArtifact@2 inputs: source: current artifact: artifact_Deps path: lib jpype-1.6.0/.azure/scripts/documentation.yml000066400000000000000000000012641501674766700211620ustar00rootroot00000000000000# This task tries to verify if the documentation will build properly on RTD steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | pip install --exists-action=w --no-cache-dir -r test-requirements.txt -r .azure/doc-requirements.txt displayName: 'Install requirements' - script: | python -m sphinx -T -b readthedocs -d _build/doctrees-readthedocs -D language=en doc build/html displayName: 'Check documentation' - task: CopyFiles@2 inputs: contents: 'build/html/**' targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishBuildArtifacts@1 inputs: pathToPublish: $(Build.ArtifactStagingDirectory) artifactName: documentation jpype-1.6.0/.azure/scripts/ivy.yml000066400000000000000000000005551501674766700171220ustar00rootroot00000000000000steps: - script: | wget "https://repo1.maven.org/maven2/org/apache/ivy/ivy/2.4.0/ivy-2.4.0.jar" -P lib java -jar lib/ivy-2.4.0.jar -ivy ivy.xml -retrieve 'lib/[artifact]-[revision](-[classifier]).[ext]' displayName: 'Resolve' - task: PublishPipelineArtifact@0 inputs: artifactName: 'artifact_Deps' targetPath: 'lib' displayName: Publish deps jpype-1.6.0/.azure/scripts/jdk.yml000066400000000000000000000006511501674766700170600ustar00rootroot00000000000000parameters: - name: version type: string default: '8' steps: - task: JavaToolInstaller@0 inputs: versionSpec: ${{ parameters.version }} jdkArchitectureOption: 'x64' jdkSourceOption: 'PreInstalled' - bash: | echo AGENT_JOBSTATUS = $AGENT_JOBSTATUS if [[ "$AGENT_JOBSTATUS" == "SucceededWithIssues" ]]; then exit 1; fi displayName: JDK ${{ parameters.version }} set as JAVA_HOME. jpype-1.6.0/.azure/scripts/osx-python.sh000077500000000000000000000017751501674766700202640ustar00rootroot00000000000000#!/usr/bin/env bash PYTHON_VERSION="$1" case $PYTHON_VERSION in 3.7) FULL_VERSION=3.7.9 INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.8) FULL_VERSION=3.8.10 INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.9) FULL_VERSION=3.9.12 INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.10) FULL_VERSION=3.10.11 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; 3.11) FULL_VERSION=3.11.7 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; 3.12) FULL_VERSION=3.12.0 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; 3.13) FULL_VERSION=3.13.0 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg esac URL=https://www.python.org/ftp/python/$FULL_VERSION/$INSTALLER_NAME PY_PREFIX=/Library/Frameworks/Python.framework/Versions set -e -x curl $URL > $INSTALLER_NAME sudo installer -pkg $INSTALLER_NAME -target / sudo rm -f /usr/local/bin/python sudo ln -s /usr/local/bin/python$PYTHON_VERSION /usr/local/bin/python which python python --version python -m ensurepip jpype-1.6.0/.azure/scripts/publish-dist.yml000066400000000000000000000002331501674766700207130ustar00rootroot00000000000000steps: - task: PublishPipelineArtifact@0 inputs: artifactName: 'artifact_$(Agent.JobName)_$(Agent.OS)_$(python.architecture)' targetPath: 'dist' jpype-1.6.0/.azure/scripts/python.yml000066400000000000000000000015521501674766700176320ustar00rootroot00000000000000parameters: - name: version type: string default: '3.12' - name: architecture type: string default: 'x64' steps: - task: UsePythonVersion@0 inputs: architecture: ${{ parameters.architecture }} versionSpec: ${{ parameters.version }} disableDownloadFromRegistry: false # boolean. Disable downloading releases from the GitHub registry. Default: false. allowUnstable: true # boolean. Optional. Use when disableDownloadFromRegistry = false. Allow downloading unstable releases. Default: false. githubToken: $(githubToken) # global (secret) variable to allow API access to Github (for not hitting a rate limit while downloading). - bash: | echo AGENT_JOBSTATUS = $AGENT_JOBSTATUS if [[ "$AGENT_JOBSTATUS" == "SucceededWithIssues" ]]; then exit 1; fi displayName: Python ${{ parameters.version }} set as interpreter. jpype-1.6.0/.azure/scripts/sdist.yml000066400000000000000000000007621501674766700174410ustar00rootroot00000000000000parameters: - name: artifact type: boolean default: false steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.10' - script: | python -m pip install build twine python -m build ./ --sdist twine check dist/* displayName: Build sdist and check with twine - task: PublishPipelineArtifact@0 condition: and(succeeded(), eq('${{ parameters.artifact }}', true)) inputs: artifactName: 'artifact_SourceDistribution' targetPath: 'dist' displayName: Publish sdist jpype-1.6.0/.azure/scripts/test.yml000066400000000000000000000027411501674766700172710ustar00rootroot00000000000000# This task tests individual platforms and versions steps: - template: python.yml parameters: version: '$(python.version)' - template: jdk.yml parameters: version: '$(jdk.version)' - template: sdist.yml - script: | python -m pip install -e . displayName: 'Build/install module' - script: | pip install numpy jedi typing_extensions python -c "import jpype" displayName: 'Check module' - script: | pip install setuptools python setup.py test_java pip install -r test-requirements.txt displayName: 'Install test' - script: | python -m pip install -U pytest python -m pytest -v --junit-xml=build/test/test.xml test/jpypetest --checkjni displayName: 'Test JDK $(jdk.version) and Python $(python.version)' condition: eq(variables['jpypetest.fast'], 'false') - script: | python -m pytest -v --junit-xml=build/test/test.xml test/jpypetest --checkjni --fast displayName: 'Test JDK $(jdk.version) and Python $(python.version) (fast)' condition: eq(variables['jpypetest.fast'], 'true') # presence of jpype/ seems to confuse entry_points so `cd` elsewhere - script: | pip install . mkdir empty cd empty python -m PyInstaller.utils.run_tests --include_only jpype._pyinstaller. displayName: 'Test PyInstaller result' - task: PublishTestResults@2 condition: succeededOrFailed() inputs: testResultsFiles: 'build/test/test.xml' testRunTitle: 'Publish test results for Python $(python.version) with JDK $(jdk.version)' jpype-1.6.0/.azure/scripts/tracing.yml000066400000000000000000000003511501674766700177340ustar00rootroot00000000000000# This job verifies that nothing is broken in the tracing compile steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | python setup.py develop --enable-tracing --enable-build-jar displayName: 'Build' jpype-1.6.0/.azure/scripts/wheels-linux.yml000066400000000000000000000013741501674766700207370ustar00rootroot00000000000000steps: - task: DownloadPipelineArtifact@2 inputs: source: current artifact: artifact_SourceDistribution path: dist - script: | sudo apt-get update sudo apt-get install -y qemu qemu-user-static qemu-user binfmt-support sudo docker run --rm --privileged hypriot/qemu-register condition: and(succeeded(), eq(variables['arch'], 'aarch64')) displayName: 'Install QEMU' - script: | ls -l ls -l dist displayName: Sanity check - script: | set -ex docker run -e PLAT=$(plat) -e package_name=$(package_name) --rm -v `pwd`:/io $(image) /io/.azure/scripts/build-wheels.sh displayName: Build wheels - script: | ls -lh wheelhouse/ rm dist/* cp wheelhouse/$(package_name)*.whl dist/. displayName: Copy wheels jpype-1.6.0/.azure/scripts/wheels.yml000066400000000000000000000016371501674766700176040ustar00rootroot00000000000000# This job creates wheels for Windows/OSX steps: - script: | mkdir -p dist python -m pip install --upgrade pip setuptools -r test-requirements.txt displayName: 'Install dependencies' - script: | python -m pip wheel . -w wheelhouse/ displayName: 'Build wheel' - script: | ls -lh wheelhouse cp wheelhouse/$(package_name)* dist/. displayName: 'Show wheelhouse' - script: | python -m pip install jpype1 --no-index -f wheelhouse displayName: 'Install module' - script: | python setup.py test_java displayName: 'Build java tests' - script: | ls -l ls lib/ displayName: 'Check deps' - task: PublishPipelineArtifact@0 inputs: artifactName: 'artifact_$(Agent.JobName)_$(Agent.OS)_$(python.architecture)' targetPath: 'dist' - script: | rm -Rf jpype python -m pytest -v --junit-xml=build/test/test.xml test/jpypetest --checkjni --fast displayName: 'Test module' jpype-1.6.0/.bumpversion.cfg000066400000000000000000000013341501674766700160010ustar00rootroot00000000000000[bumpversion] current_version = 1.6.0 commit = True tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}.{release}{build} {major}.{minor}.{patch} [bumpversion:part:release] first_value = dev optional_value = prod values = dev prod [bumpversion:part:build] [bumpversion:file:pyproject.toml] [bumpversion:file:native/python/pyjp_module.cpp] [bumpversion:file:jpype/__init__.py] [bumpversion:file:native/jpype_module/pom.xml] [bumpversion:file:native/jpype_module/src/main/java/org/jpype/JPypeContext.java] [bumpversion:file:doc/CHANGELOG.rst] search = Latest Changes: replace = Latest Changes: - **{new_version} - {now:%Y-%m-%d}** jpype-1.6.0/.gitattributes000066400000000000000000000007661501674766700155740ustar00rootroot00000000000000* text=auto # Specify lf for all source files *.py text eol=lf *.cpp text eol=lf *.c text eol=lf *.h text eol=lf *.java text eol=lf *.rst text eol=lf *.sh text eol=lf *.cfg text eol=lf *.yml text eol=lf *.xsl text eol=lf *.css text eol=lf *.ps1 text eol=crlf *.cmd text eol=crlf *.bat text eol=crlf *.properties text eol=crlf README text eol=lf LICENSE text eol=lf Makefile text eol=lf Manifest.in text eol=lf *.xml text eol=lf *.md text *.txt text .gitignore text eol=lf .gitattributes text eol=lf jpype-1.6.0/.github/000077500000000000000000000000001501674766700142305ustar00rootroot00000000000000jpype-1.6.0/.github/workflows/000077500000000000000000000000001501674766700162655ustar00rootroot00000000000000jpype-1.6.0/.github/workflows/codeql.yml000066400000000000000000000021241501674766700202560ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "master" ] pull_request: branches: [ "master" ] schedule: - cron: "27 1 * * 1" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ java, cpp, python ] steps: - name: Checkout uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v3 if: ${{ matrix.language == 'python' }} - name: Build cpp run: python3 setup.py build if: ${{ matrix.language == 'cpp' }} - name: Build java run: ant -f native/build.xml if: ${{ matrix.language == 'java' }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{ matrix.language }}" jpype-1.6.0/.github/workflows/mypy.yml000066400000000000000000000006541501674766700200130ustar00rootroot00000000000000name: Mypy on: [push, pull_request] jobs: build: runs-on: ubuntu-latest name: mypy steps: - uses: actions/checkout@v1 - name: Set up Python 3.11 uses: actions/setup-python@v1 with: python-version: 3.11 - name: Install Dependencies run: | pip install mypy numpy types-pyinstaller pytest packaging - name: mypy run: | mypy ./jpype/ ./test/jpypetest/ jpype-1.6.0/.gitignore000077500000000000000000000025301501674766700146630ustar00rootroot00000000000000MANIFEST Makefile* *~ /test/classes/ /test/jars/ **/test.pic build/ .buildozer bin native/*.jar org.jpype.jar # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so *.dll # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage jacoco.exec .cache nosetests.xml coverage*.xml # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ /project/jpype_java/nbproject/private/ /project/jpype_cpython/nbproject/private/ /project/jpype_cpython/nbproject/Makefile* /project/jpype_java/nbproject/build-impl.xml /project/jpype_java/nbproject/genfiles.properties /project/jpype_java/dist /project/jpype_java/build /project/jpype_cpython/Makefile /project/jpype_cpython/nbproject/Package-Release.bash /project/jpype_python/nbproject/private/ /project/coverage/*.jar *.swp *.stackdump *.zip tmp doc/html native/jars docker/*.tar.gz docker/wheelhouse /.eggs /tmp* a.out .idea/ _build/ jacoco/ wheelhouse/ vc*.pdb *.class jpype-1.6.0/.readthedocs.yml000066400000000000000000000011171501674766700157560ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/conf.py # Optionally build your docs in additional formats such as PDF and ePub # formats: all # Optionally set the version of Python and requirements required to build your docs python: install: - method: pip path: . extra_requirements: - docs build: tools: python: "3.8" os: ubuntu-22.04 apt_packages: - openjdk-11-jdk jpype-1.6.0/AUTHORS.rst000066400000000000000000000006421501674766700145510ustar00rootroot00000000000000Authors ------- The original author: Steve Menard Current Lead Developer: Karl Einar Nelson Current Maintainer: Martin K. Scherer Huge thanks to these CONTRIBUTORS: * lazerscience * Koblaid * Michael Willis (michaelwillis) * awesomescot * Joe Quant (joequant) * Mario Rodas * David Moss * Stepan Kolesnik * Philip Smith * Bastian Bowe * Kristi * Martin K. Scherer * Dongwon Shin * rbprogrammer * Karl Einar Nelson jpype-1.6.0/LICENSE000066400000000000000000000261351501674766700137040ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. jpype-1.6.0/MANIFEST.in000066400000000000000000000003051501674766700144240ustar00rootroot00000000000000recursive-include native *.h *.xml *.java *.jar *.class recursive-include examples * include *.rst doc/* LICENSE # include test/ graft test prune test/classes/* graft setupext global-exclude *.pyc jpype-1.6.0/NOTICE000066400000000000000000000036351501674766700136030ustar00rootroot00000000000000COPYRIGHT ========= Copyright (c) 2004-2008 Steve Menard Copyright (c) 2020 Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory LLNL-CODE- 812311 All rights reserved. LICENSE ======= Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. AUSPICES ======== This work was produced under the auspices of the U.S. Department of Energy by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. This work was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor Lawrence Livermore National Security, LLC, nor any of their employees makes any warranty, expressed or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or Lawrence Livermore National Security, LLC. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or Lawrence Livermore National Security, LLC, and shall not be used for advertising or product endorsement purposes. jpype-1.6.0/README.rst000066400000000000000000000070611501674766700143630ustar00rootroot00000000000000.. image:: doc/logo_small.png :alt: JPype logo :align: center JPype ===== |implementation| |pyversions| |javaversions| |jvm| |platform| |license| JPype is a Python module to provide full access to Java from within Python. It allows Python to make use of Java only libraries, exploring and visualization of Java structures, development and testing of Java libraries, scientific computing, and much more. By gaining the best of both worlds using Python for rapid prototyping and Java for strong typed production code, JPype provides a powerful environment for engineering and code development. This is achieved not through re-implementing Python, as Jython has done, but rather through interfacing at the native level in both virtual machines. This shared memory based approach achieves decent computing performance, while providing the access to the entirety of CPython and Java libraries. :Code: `GitHub `_ :Issue tracker: `GitHub Issues `_ :Discussions: `GitHub Discussions `_ :Documentation: `Python Docs`_ :License: `Apache 2 License`_ :Build status: |TestsCI|_ |Docs|_ :Quality status: |Codecov|_ |lgtm_python|_ |lgtm_java|_ |lgtm_cpp|_ :Version: |PypiVersion|_ |Conda|_ The work on this project began on `Sourceforge `__. LLNL-CODE- 812311 .. |alerts| image:: https://img.shields.io/lgtm/alerts/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18 .. _alerts: https://lgtm.com/projects/g/jpype-project/jpype/alerts/ .. |lgtm_python| image:: https://img.shields.io/lgtm/grade/python/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18&label=python .. _lgtm_python: https://lgtm.com/projects/g/jpype-project/jpype/context:python .. |lgtm_java| image:: https://img.shields.io/lgtm/grade/java/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18&label=java .. _lgtm_java: https://lgtm.com/projects/g/jpype-project/jpype/context:java .. |lgtm_cpp| image:: https://img.shields.io/lgtm/grade/cpp/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18&label=C++ .. _lgtm_cpp: https://lgtm.com/projects/g/jpype-project/jpype/context:cpp .. |PypiVersion| image:: https://img.shields.io/pypi/v/Jpype1.svg .. _PypiVersion: https://badge.fury.io/py/JPype1 .. |Conda| image:: https://img.shields.io/conda/v/conda-forge/jpype1.svg .. _Conda: https://anaconda.org/conda-forge/jpype1 .. |TestsCI| image:: https://dev.azure.com/jpype-project/jpype/_apis/build/status/jpype-project.jpype?branchName=master .. _TestsCI: https://dev.azure.com/jpype-project/jpype/_build/latest?definitionId=1&branchName=master .. |Docs| image:: https://img.shields.io/readthedocs/jpype.svg .. _Docs: http://jpype.readthedocs.org/en/latest/ .. |Codecov| image:: https://codecov.io/gh/jpype-project/jpype/branch/master/graph/badge.svg .. _Codecov: https://codecov.io/gh/jpype-project/jpype .. |implementation| image:: https://img.shields.io/pypi/implementation/jpype1.svg .. |pyversions| image:: https://img.shields.io/pypi/pyversions/jpype1.svg .. |javaversions| image:: https://img.shields.io/badge/java-8%20%7C%209%20%7C%2011-purple.svg .. |jvm| image:: https://img.shields.io/badge/jvm-Open%20%7C%20Oracle%20%7C%20Corretto-purple.svg .. |platform| image:: https://img.shields.io/conda/pn/conda-forge/jpype1.svg .. |license| image:: https://img.shields.io/github/license/jpype-project/jpype.svg .. _Apache 2 License: https://github.com/jpype-project/jpype/blob/master/LICENSE .. _Python Docs: http://jpype.readthedocs.org/en/latest/ SPDX-License-Identifier: Apache-2.0 jpype-1.6.0/codecov.yml000066400000000000000000000012041501674766700150320ustar00rootroot00000000000000codecov: require_ci_to_pass: yes coverage: precision: 2 round: down range: "70...100" status: project: default: false python: target: 85% threshold: 1% paths: - "jpype/" cpp: target: 80% threshold: 1% paths: - "native/common/" - "native/python/" java: target: 75% threshold: 2% paths: - "native/java/" parsers: gcov: branch_detection: conditional: yes loop: yes method: no macro: no comment: layout: "reach,diff,flags,tree" behavior: default require_changes: no jpype-1.6.0/coverage.sh000066400000000000000000000013301501674766700150140ustar00rootroot00000000000000#!/bin/sh # Script for testing coverage locally PYTHON=python PIP="python -m pip" ./resolve.sh $PYTHON setup.py test_java $PIP install gcovr pytest-cov jedi $PYTHON setup.py --enable-coverage --enable-build-jar build_ext --inplace $PYTHON -m pytest -rsx -v test/jpypetest \ --cov-report=xml:coverage_py.xml \ --cov-report=html:build/coverage/python \ --cov=jpype \ --classpath="build/classes" --jacoco --checkjni java -jar lib/org.jacoco.cli-0.8.5-nodeps.jar report build/coverage/jacoco.exec \ --classfiles build/classes/ \ --html build/coverage/java \ --sourcefiles native/java mkdir build/coverage/cpp gcovr -r . --html-details -o build/coverage/cpp/jpype.html --exclude-unreachable-branches --exclude-throw-branches jpype-1.6.0/dev-requirements.txt000066400000000000000000000000521501674766700167250ustar00rootroot00000000000000typing-extensions ; python_version< "3.8" jpype-1.6.0/doc/000077500000000000000000000000001501674766700134355ustar00rootroot00000000000000jpype-1.6.0/doc/CHANGELOG.rst000066400000000000000000001020761501674766700154640ustar00rootroot00000000000000Changelog ========= This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: - **1.6.0 - 2025-05-31** - **1.6.0 - 2025-01-20** - Java components have been converted to maven style module. - JArray is now registered as a Sequence. - Fixed conversion of float16 for subnormal numbers. - Fixed segmentation fault on null String. - Fixed bugs with java.util.List concat and repeat methods. - Enhancement to JProxy to handle wrapping an existing Python object with a Java interface. - Fixed bug in which using an interface the derived from Map with JProxy failed. - Fixed a bug in which JPype did not respect a JConversion between two Java classes. - Enhancement in convertToDirectBuffer to support wrapping bytes and readonly memoryviews as readonly java ByteBuffers. - **1.5.2 - 2025-01-20** - Roll back agent change due to misbehaving JVM installs. - Correct issues with non-ascii path for jdbc connections and forName. - **1.5.1 - 2024-11-09** - Future proofing for Python 3.14 - Support for Python 3.13 - Allow access to default methods implemented in interfaces when using ``@JImplements``. - Added support for typing ``JArray`` (Java type only), e.g. ``JArray[java.lang.Object]`` ``"JArray[java.lang.Object]"`` - Fixed uncaught exception while setting traceback causing issues in Python 3.11/3.12. - Use PEP-518 and PEP-660 configuration for the package, allowing editable and configurable builds using modern Python packaging tooling. Where before ``python setup.py --enable-tracing develop``, now can be done with ``pip install --editable ./ --config-setting="--install-option=--enable-tracing"``. The old setup.py usage remains, but is discouraged, and the arguments are now passed after the command (previously they were specified before). - Use PEP-518 configuration for the package, allowing configurable builds using more up-to-date Python packaging tooling. For editable installs, ``python setup.py --enable-tracing develop`` must now be done with ``python setup.py develop --enable-tracing``. - Update for tests for numpy 2.0. - Support of np.float16 conversion with arrays. - Fixed a problem that caused ``dir(jpype.JPackage("mypackage"))`` to fail if the class path contained non-ascii characters. See issue #1194. - Fixed ``BufferOverflowException`` in ``JUnpickler`` when decoding multiple Java objects. - **1.5.0 - 2023-04-03** - Support for Python 3.12 - Switched ``__eq__`` and ``__ne__`` operator to use ``equals`` rather than ``compareTo`` for comparable objects to avoid exception when comparing object of different types. - Fixed segmentation fault when comparing Java Comparable to primitives. - Java exceptions that occur in inequality comparisons now map to Python TypeError. - **1.4.2_dev0 - 2022-10-26** - Fixed crash when calling subscript on JArray. - Fixed direct byte buffers not reporting nbytes correctly when cast to memoryview. - Expand the defintion for Functional interface to include classes without FunctionInterface annotation. - Add additional matching level for derived types to resolve ambiguities when a derived type is used in place of base class when determining the method overload. This will resolve some previous ambiguities between methods. - **1.4.1 - 2022-10-26** - Fixed issue with startJVM changing locale settings. - Changes to support Python 3.11 - Fix truncation of strings on null when using convert strings. - Replaced distutil with packaging - **1.4.0 - 2022-05-14** - Support for all different buffer type conversions. - Improved buffer transfers to numpy as guaranteed to match Java types. However, exact dtype for conversions is os/numpy version dependent. - Support for byte order channels on buffer transfers. - Byte size for buffers now fixed to Java definitions. - When directly accessing Java arrays using memory view, Python requires a cast from buffers. Required because Python does not support memory view alterations on non-native sizes. - Fix crash when comparing JChar. - Order handling for numerical operations with JChar fixed. - Improved matching for Java functors based on parameter count. - Dropped support for Python 3.5 and 3.6 - dbapi2 handles drivers that don't support autocommit. - Fixed issue when Java classes with dunder methods such as ``__del__`` caused conflicts in Python type system. Java method which match dunder patterns are longer translated to Python. - Fix issue with numpy arrays with no dimensions resulting in crash. - Support for user defined conversions for java.lang.Class and array types. - Fixed issue with ssize_t on Windows for Python 3.10. - **1.3.0 - 2021-05-19** - Fixes for memory issues found when upgrading to Python 3.10 beta. - Add additional diagnositics for importing of non-public class. - Fixed issue with classes with unsatified dependencies leading to a crash on windows. - Fixed a bug with arrays created using the short cut. The wrong type was being returned. - **1.2.1 - 2021-01-02** - Missing stub files added. - Python 3.9 issues are resolved on Windows. - JPype scans jar files and rebuilding missing directories to allow imports from stripped and obfuscated jar files. - **1.2.0 - 2020-11-29** - Added builds for Python 3.9. Python 3.9 on Windows is currently failing due to issue in Python. - Fixed bug when importing from multi-release jars. The directory was being truncated to only those classes in the overlay. - addClassPath can add jar files after the JVM is started. The default loader for JPype class is ``org.jpype.classloader.DynamicClassLoader``. - Build support of z/OS added. - Bug causing ambiguity between primitives and variadic arguments in method resolution was corrected. - Boolean was inadvertently left out of method resolution. ``boolean`` now properly matched with boxed types. - Support for PyInstaller was added. - **1.1.2 - 2020-10-23** - Linux binaries are now stripped for size. - Add importlib.util to address instability in Python importlib boot process. Certain versions of Python such as 3.9 appear to not properly load this module resulting in unexpected errors during startJVM. - **1.1.1 - 2020-10-21** - Fixed packaging problem on linux. - **1.1.0 - 2020-10-13** - Correct bug resulting in reporting ambiguous overloads when resolving methods with variadic arguments. - Ctrl+C behavior is switchable with interrupt flag to startJVM. If True, process will halt on Ctrl-C. If False, the process will transfer control to Python rather than halting. If not specified JPype will assume false if Python is started as an interactive shell. - Fixed crash with Ctrl+C when multiple exceptions were generated. - Removed extraneous exception when calling Ctrl+C before Java code is executed for methods and fields. - Fixed memory leak with string cache. - Fixed crash when manually creating wrappers for anonymous classes. - Fixed reference count problem in stackframes used for exceptions. - Errors report `*static*` when the matching with a static method so that it is clear when a member method was called statically. - java.lang.String slices function like Python string slice. - Java packages now operate as normal Python modules. Removed restrictions regarding setattr. All package instances for the same package name are shared so that functionality added to one instance is shared wiht all instances. - **1.0.2 - 2020-07-27** - The wrapper for Throwable was getting the wrapper for Object rather than the expected wrapper resulting in odd conversions from Python classes. - Typos within the import system resulting in "jname" not found corrected. - ^C propogates to a KeyboardInterrupt properly. - Added cache to the method dispatch to bypass resolution of overloads. This reduces the cost of method resolution significantly especially if the same overload is hit repeatedly such as during loop operations. - Improved speed on transfer of lists, tuples, buffers to arrays of Java primitives by a factor of 4 to 100 depending on the data type. The conversion uses optimized path for memory buffers, rather than the Sequence API. When a Python buffer is encountered only the first element is checked for conversion as Python buffers are homogeneous. - Corrected symbol problem with Python 3.5.3. PySlice_Unpack was introduced in a later patch release and should not have been used. - **shutdown** The behavior log entry for changes on shutdown were lost in the 1.0 release. JPype now calls the JVM shutdown routine which tries to gracefully exit when shutdown is called. This results in several changes in behavior. Non daemon threads can now hold open the JVM until they have completed. Proxy calls will hold the shutdown until the call is completed but will receive an interrupt message. Files now close properly and will flush if the threads properly handle the exception. Resource clean up hooks and finalizers are executed. AtExit hooks in the JVM are called as spawned threads. Automatic attachment of threads by use of the JVM from Python are done as daemon but can be reattached as user threads on demand. Buggy code that fails to properly handle thread clean up will likely hang on shutdown. Additional documentation is located in the user guide. - A bug was reported with numpy.linalg.inv resulting in crashes. This was traced to an interaction with threading between the JVM and some compilations of numpy. The workaround appears to be calling numpy.linalg.inv prior to starting the JVM. - **1.0.1 - 2020-07-16** - Workarounds for Python 3.8.4 release. Python altered logic regarding the use of ``__setattr__`` for object and type, preventing it from being used to alter derived classes. Also the checking for errors was delegated from the ``__setattr__`` method so exception types on some sanity checks needed to be updated accordingly. - **1.0.0 - 2020-07-12** - ``JChar`` is supported as a return type, thus rather than returning a string where a ``JChar`` is expected. For compatiblity ``JChar`` is derived from ``str`` and implements implicit conversion to an ``int`` when used in numeric operations. Therefore, it passes the return, argument, and field contracts. But that means it is no longer considered a numerical type to Python and thus ``isinstance(c, int)`` is False. This is consistent with the Java type conversion rules. - Introduced Python operator for Java casting. In Java to cast to a type you would use ``(Type) obj``, but Python does not support anything similar. Therefore, we are enlisting the rarely used ``matmul`` operator as to allow an easy way to cast an object to a Java type. When a cast to a Java type is required, use ``Type@obj`` or ``(Type)@obj``. - Introduced array notation to create Java arrays. In earlier versions, JArray factory was required to make a new array type. But this is tedious to read. In Java the notation would be ``Type[]`` to declare a type or ``new Type[sz]`` to make a new array. Python does not directly support this notation, but it does allow for unspecifed array sizes using a slice. All Java class types support ``Type[sz]`` to create an array of a fixed size and ``Type[:]`` to create an array type which can be intiated later. This call be applied to multiple dimensions to create fixed sized arrays ``Type[s1][s2][s3]`` to declare multidimension array types ``Type[:][:][:]`` or to create a new multi dimensional array with unspecified dimensions ``Type[sz][:][:]``. Applying a slice with limits to a class is unsupported. - Java classes annotated with ``@FunctionalInterface`` can be converted from any Python object that implements ``__call__``. This allows functions, lambdas, and class constructors to be used whereever Java accepts a lambda. - Support for Protocol on type conversions. Attribute based conversions deprecated in favor of Protocol. Internal API for stubbing. - Deprecated class and functions were removed. ``JIterator``, use of ``JException`` as a factory, ``get_default_jvm_path``, ``jpype.reflect`` module. - Default for starting JVM is now to return Java strings rather than convert. - Python deprecated ``__int__`` so implicit conversions between float and integer types will produce a ``TypeError``. - Use of ``JException`` is discouraged. To catch all exceptions or test if an object is a Java exception type, use ``java.lang.Throwable``. - Chained Java exception causes are now reflected in the Python stackframes. - Use of ``JString`` is discouraged. To create a Java string or test if an object is a Java string type, use ``java.lang.String``. - Updated the repr methods on Java classes. - ``java.util.List`` completes the contract for ``collections.abc.Sequence`` and ``collections.abc.MutableSequence``. - ``java.util.Collection`` completes the contract for ``collections.abc.Collection``. - Java classes are closed and will raise ``TypeError`` if extended in Python. - Handles Control-C gracefully. Previous versions crash whenever Java handles the Control-C signal as they would shutdown Java during a call. Now JPype will produce a ``InterruptedException`` when returning from Java. Control-C will not break out of large Java procedures as currently implemented as Java does not have a specific provision for this. - **0.7.5 - 2020-05-10** - Updated docs. - Fix corrupt conda release. - **0.7.4 - 4-28-2020** - Corrected a resource leak in arrays that affects array initialization, and variable argument methods. - Upgraded diagnostic tracing and JNI checks to prevent future resource leaks. - **0.7.3 - 4-17-2020** - **Replaced type management system**, memory management for internal classes is now completely in Java to allow enhancements for buffer support and revised type conversion system. - Python module ``jpype.reflect`` will be removed in the next release. - ``jpype.startJVM`` option ``convertStrings`` default will become False in the next release. - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` is deprecated to support casting to Python wrapper types in Java in a future release. - Dropped support for Cygwin platform. - ``JFloat`` properly follows Java rules for conversion from ``JDouble``. Floats outside of range map to inf and -inf. - ``java.lang.Number`` converts automatically from Python and Java numbers. Java primitive types will cast to their proper box type when passed to methods and fields taking Number. - ``java.lang.Object`` and ``java.lang.Number`` box signed, sized numpy types (int8, int16, int32, int64, float32, float64) to the Java boxed type with the same size automatically. Architecture dependent numpy types map to Long or Double like other Python types. - Explicit casting using primitives such as JInt will not produce an ``OverflowError``. Implicit casting from Python types such as int or float will. - Returns for number type primitives will retain their return type information. These are derived from Python ``int`` and ``float`` types thus no change in behavior unless chaining from a Java methods which is not allowed in Java without a cast. ``JBoolean`` and ``JChar`` still produce Python types only. - Add support for direct conversion of multi-dimensional primitive arrays with ``JArray.of(array, [dtype=type])`` - ``java.nio.Buffer`` derived objects can convert to memoryview if they are direct. They can be converted to NumPy arrays with ``numpy.asarray(memoryview(obj))``. - Proxies created with ``@JImplements`` properly implement ``toString``, ``hashCode``, and ``equals``. - Proxies pass Python exceptions properly rather converting to ``java.lang.RuntimeException`` - ``JProxy.unwrap()`` will return the original instance object for proxies created with JProxy. Otherwise will return the proxy. - JProxy instances created with the ``convert=True`` argument will automatic unwrap when passed from Java to Python. - JProxy only creates one copy of the invocation handler per garbage collection rather than once per use. Thus proxy objects placed in memory containers will have the same object id so long as Java holds on to it. - jpype.imports and JPackage verify existance of packages and classes. Imports from Java packages support wildcards. - Bug with JPackage that imported private and protected classes inappropriately has been corrected. Protected classes can still be imported using JClass. - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` is deprecated to support casting to Python wrapper types in Java in a - ``@JImplements`` with keyword argument ``deferred`` can be started prior to starting the JVM. Methods are checked at first object creation. - Fix bug that was causing ``java.lang.Comparable``, ``byte[]``, and ``char[]`` to be unhashable. - Fix bug causing segfault when throwing Exceptions which lack a default constructor. - Fixed segfault when methods called by proxy have incorrect number of arguments. - Fixed stack overflow crash on iterating ImmutableList - ``java.util.Map`` conforms to Python ``collections.abc.Mapping`` API. - ``java.lang.ArrayIndexOutOfBoundsException`` can be caught with ``IndexError`` for consistency with Python exception usage. - ``java.lang.NullPointerException`` can be caught with ``ValueError`` for consistency with Python exception usage. - **Replaced type conversion system**, type conversions test conversion once per type improving speed and increasing flexiblity. - User defined implicit conversions can be created with ``@JConversion`` decorator on Python function taking Java class and Python object. Converter function must produce a Java class instance. - ``pathlib.Path`` can be implicitly converted into ``java.lang.File`` and ``java.lang.Path``. - ``datetime.datatime`` can implicitly convert to ``java.time.Instant``. - ``dict`` and ``collections.abc.Mapping`` can convert to ``java.util.Map`` if all element are convertable to Java. Otherwise, ``TypeError`` is raised. - ``list`` and ``collections.abc.Sequence`` can convert to ``java.util.Collection`` if all elements are convertable to Java. Otherwise, ``TypeError`` is raised. - **0.7.2 - 2-28-2020** - C++ and Java exceptions hold the traceback as a Python exception cause. It is no longer necessary to call stacktrace() to retrieve the traceback information. - Speed for call return path has been improved by a factor of 3. - Multidimensional array buffer transfers increase speed transfers to numpy substantially (orders of magnitude). Multidimension primitive transfers are read-only copies produced inside the JVM with C contiguous layout. - All exposed internals have been replaced with CPython implementations thus symbols `__javaclass__`, `__javavalue__`, and `__javaproxy__` have been removed. A dedicated Java slot has been added to all CPython types derived from `_jpype` class types. All private tables have been moved to CPython. Java types must derive from the metaclass `JClass` which enforces type slots. Mixins of Python base classes is not permitted. Objects, Proxies, Exceptions, Numbers, and Arrays derive directly from internal CPython implementations. See the :doc:`ChangeLog-0.7.2` for details of all changes. - Internal improvements to tracing and exception handling. - Memory leak in convertToDirectBuffer has been corrected. = Arrays slices are now a view which support writeback to the original like numpy array. Array slices are no longer covariant returns of list or numpy.array depending on the build procedure. - Array slices support steps for both set and get. - Arrays now implement `__reversed__` - Incorrect mapping of floats between 0 and 1 to False in setting Java boolean array members is corrected. - Java arrays now properly assert range checks when setting elements from sequences. - Java arrays support memoryview API and no longer required NumPy to transfer buffer contents. - Numpy is no longer an optional extra. Memory transfer to NumPy is available without compiling for numpy support. - JInterface is now a meta class. Use ``isinstance(cls, JInterface)`` to test for interfaces. - Fixed memory leak in Proxy invocation - Fixed bug with Proxy not converting when passed as an argument to Python functions during execution of proxies - Missing tlds "mil", "net", and "edu" added to default imports. - Enhanced error reporting for UnsupportedClassVersion during startup. - Corrections for collection methods to improve complience with Python containers. - java.util.Map gives KeyError if the item is not found. Values that are ``null`` still return ``None`` as expected. Use ``get()`` if empty keys are to be treated as ``None``. - java.util.Collection ``__delitem__`` was removed as it overloads oddly between ``remove(Object)`` and ``remove(int)`` on Lists. Use Java ``remove()`` method to access the original Java behavior, but a cast is strongly recommended to to handle the overload. - java.lang.IndexOutOfBoundsException can be caught with IndexError for complience when accessing ``java.util.List`` elements. - **0.7.1 - 12-16-2019** - Updated the keyword safe list for Python 3. - Automatic conversion of CharSequence from Python strings. - java.lang.AutoCloseable supports Python "with" statement. - Hash codes for boxed types work properly in Python 3 and can be used as dictionary keys again (same as JPype 0.6). Java arrays have working hash codes, but as they are mutable should not be used as dictionary keys. java.lang.Character, java.lang.Float, and java.lang.Double all work as dictionary keys, but due to differences in the hashing algorithm do not index to the same location as Python native types and thus may cause issues when used as dictionary keys. - Updated getJVMVersion to work with JDK 9+. - Added support for pickling of Java objects using optional module ``jpype.pickle`` - Fixed incorrect string conversion on exceptions. `str()` was incorrectly returning `getMessage` rather than `toString`. - Fixed an issue with JDK 12 regarding calling methods with reflection. - Removed limitations having to do with CallerSensitive methods. Methods affected are listed in :doc:`caller_sensitive`. Caller sensitive methods now receive an internal JPype class as the caller - Fixed segfault when converting null elements while accessing a slice from a Java object array. - PyJPMethod now supports the FunctionType API. - Tab completion with Jedi is supported. Jedi is the engine behind tab completion in many popular editors and shells such as IPython. Jedi version 0.14.1 is required for tab completion as earlier versions did not support annotations on compiled classes. Tab completion with older versions requires use of the IPython greedy method. - JProxy objects now are returned from Java as the Python objects that originate from. Older style proxy classes return the inst or dict. New style return the proxy class instance. Thus proxy classes can be stored on generic Java containers and retrieved as Python objects. - **0.7.0 - 2019** - Doc strings are generated for classes and methods. - Complete rewrite of the core module code to deal unattached threads, improved hardening, and member management. Massive number of internal bugs were identified during the rewrite and corrected. See the :doc:`ChangeLog-0.7` for details of all changes. - API breakage: - Java strings conversion behavior has changed. The previous behavior was switchable, but only the default convert to Python was working. Converting to automatically lead to problems in which is was impossible to work with classes like StringBuilder in Java. To convert a Java string use ``str()``. Therefore, string conversion is currently selected by a switch at the start of the JVM. The default shall be False starting in JPype 0.8. New code is encouraged to use the future default of False. For the transition period the default will be True with a warning if not policy was selected to encourage developers to pick the string conversion policy that best applies to their application. - Java exceptions are now derived from Python exception. The old wrapper types have been removed. Catch the exception with the actual Java exception type rather than ``JException``. - Undocumented exceptions issued from within JPype have been mapped to the corresponding Python exception types such as ``TypeError`` and ``ValueError`` appropriately. Code catching exceptions from previous versions should be checked to make sure all exception paths are being handled. - Undocumented property import of Java bean pattern get/set accessors was removed as the default. It is available with ``import jpype.beans``, but its use is discouraged. - API rework: - JPype factory methods now act as base classes for dynamic class trees. - Static fields and methods are now available in object instances. - Inner classes are now imported with the parent class. - ``jpype.imports`` works with Python 2.7. - Proxies and customizers now use decorators rather than exposing internal classes. Existing ``JProxy`` code still works. - Decorator style proxies use ``@JImplements`` and ``@JOverload`` to create proxies from regular classes. - Decorator style customizers use ``@JImplementionFor`` - Module ``jpype.types`` was introduced containing only the Java type wrappers. Use ``from jpype.types import *`` to pull in this subset of JPype. - ``synchronized`` using the Python ``with`` statement now works for locking of Java objects. - Previous bug in initialization of arrays from list has been corrected. - Added extra verbiage to the to the raised exception when an overloaded method could not be matched. It now prints a list of all possible method signatures. - The following is now DEPRECATED - ``jpype.reflect.*`` - All class information is available with ``.class_`` - Unncessary ``JException`` from string now issues a warning. - The followind is now REMOVED - Python thread option for ``JPypeReferenceQueue``. References are always handled with with the Java cleanup routine. The undocumented ``setUsePythonThreadForDaemon()`` has been removed. - Undocumented switch to change strings from automatic to manual conversion has been removed. - Artifical base classes ``JavaClass`` and ``JavaObject`` have been removed. - Undocumented old style customizers have been removed. - Many internal jpype symbols have been removed from the namespace to prevent leakage of symbols on imports. - promoted *`--install-option`* to a *`--global-option`* as it applies to the build as well as install. - Added *`--enable-tracing`* to setup.py to allow for compiling with tracing for debugging. - Ant is required to build jpype from source, use ``--ant=`` with setup.py to direct to a specific ant. - **0.6.3 - 2018-04-03** - Java reference counting has been converted to use JNI PushLocalFrame/PopLocalFrame. Several resource leaks were removed. - ``java.lang.Class<>.forName()`` will now return the java.lang.Class. Work arounds for requiring the class loader are no longer needed. Customizers now support customization of static members. - Support of ``java.lang.Class<>`` - ``java.lang.Object().getClass()`` on Java objects returns a java.lang.Class rather than the Python class - ``java.lang.Object().__class__`` on Java objects returns the python class as do all python objects - ``java.lang.Object.class_`` maps to the java statement ``java.lang.Object.class`` and returns the ``java.lang.Class`` - java.lang.Class supports reflection methods - private fields and methods can be accessed via reflection - annotations are avaiable via reflection - Java objects and arrays will not accept setattr unless the attribute corresponds to a java method or field whith the exception of private attributes that begin with underscore. - Added support for automatic conversion of boxed types. - Boxed types automatically convert to python primitives. - Boxed types automatically convert to java primitives when resolving functions. - Functions taking boxed or primitives still resolve based on closest match. - Python integer primitives will implicitly match java float and double as per Java specification. - Added support for try with resources for ``java.lang.Closeable``. Use python "with MyJavaResource() as resource:" statement to automatically close a resource at the end of a block. - **0.6.2 - 2017-01-13** - Fix JVM location for OSX. - Fix a method overload bug. - Add support for synthetic methods - **0.6.1 - 2015-08-05** - Fix proxy with arguments issue. - Fix Python 3 support for Windows failing to import winreg. - Fix non matching overloads on iterating java collections. - **0.6.0 - 2015-04-13** - Python3 support. - Fix OutOfMemoryError. - **0.5.7 - 2014-10-29** - No JDK/JRE is required to build anymore due to provided jni.h. To override this, one needs to set a JAVA_HOME pointing to a JDK during setup. - Better support for various platforms and compilers (MinGW, Cygwin, Windows) - **0.5.6 - 2014-09-27** - *Note*: In this release we returned to the three point number versioning scheme. - Fix #63: 'property' object has no attribute 'isBeanMutator' - Fix #70: python setup.py develop does now work as expected - Fix #79, Fix #85: missing declaration of 'uint' - Fix #80: opt out NumPy code dependency by '--disable-numpy' parameter to setup. To opt out with pip append --install-option="--disable-numpy". - Use JVMFinder method of @tcalmant to locate a Java runtime - **0.5.5.4 - 2014-08-12** - Fix: compile issue, if numpy is not available (NPY_BOOL n/a). Closes #77 - **0.5.5.3 - 2014-08-11** - Optional support for NumPy arrays in handling of Java arrays. Both set and get slice operators are supported. Speed improvement of factor 10 for setting and factor 6 for getting. The returned arrays are typed with the matching NumPy type. - Fix: add missing wrapper type 'JShort' - Fix: Conversion check for unsigned types did not work in array setters (tautological compare) - **0.5.5.2 - 2014-04-29** - Fix: array setter memory leak (ISSUE: #64) - **0.5.5.1 - 2014-04-11** - Fix: setup.py now runs under MacOSX with Python 2.6 (referred to missing subprocess function) - **0.5.5 - 2014-04-11** - *Note* that this release is *not* compatible with Python 2.5 anymore! - Added AHL changes * replaced Python set type usage with new 2.6.x and higher * fixed broken Python slicing semantics on JArray objects * fixed a memory leak in the JVM when passing Python lists to JArray constructors * prevent ctrl+c seg faulting * corrected new[]/delete pairs to stop valgrind complaining * ship basic PyMemoryView implementation (based on numpy's) for Python 2.6 compatibility - Fast sliced access for primitive datatype arrays (factor of 10) - Use setter for Java bean property assignment even if not having a getter by @baztian - Fix public methods not being accessible if a Java bean property with the same name exists by @baztian (*Warning*: In rare cases this change is incompatibile to previous releases. If you are accessing a bean property without using the get/set method and the bean has a public method with the property's name you have to change the code to use the get/set methods.) - Make jpype.JException catch exceptions from subclasses by @baztian - Make more complex overloaded Java methods accessible (fixes https://sourceforge.net/p/jpype/bugs/69/) by @baztian and anonymous - Some minor improvements inferring unnecessary copies in extension code - Some JNI cleanups related to memory - Fix memory leak in array setters - Fix memory leak in typemanager - Add userguide from sourceforge project by @baztian - **0.5.4.5 - 2013-08-25** - Added support for OSX 10.9 Mavericks by @rmangino (#16) - **0.5.4.4 - 2013-08-10** - Rewritten Java Home directory Search by @marsam (#13, #12 and #7) - Stylistic cleanups of setup.py - **0.5.4.3 - 2013-07-27** - Initial pypi release with most fixes for easier installation jpype-1.6.0/doc/ChangeLog-0.7.2.rst000066400000000000000000000571451501674766700164740ustar00rootroot00000000000000:orphan: Buffers and NumPy removal ========================= NumPy was used primarily for supplying Python buffers on slicing. Numpy has always been problematic for JPype. As an optional extra is may or may not be built into the distribution, but if it is compiled in it is required. Therefore, is itn't really on "extra". Therefore, removing it would make distribution for binary versions of JPype much easier. NumPy returns on slicing is but one part of the three uses of Python buffers in JPype. Thus to properly remove it we need to rework to remove it we need to review all three of the paths. These paths are - Conversion of Python buffers to Java arrays on setArrayRange. - Conversion of Java arrays to slices (and then from slices to buffers so that they can be transferred to NumPy.) - Connecting bytearray to Java direct byte buffers. We reviewed and revised each of the paths accordingly. On conversion of Python buffers, the implementation was dated from Python 2.6 era where there was no formal support for buffers. Thus the buffer implementation never consulted the buffer type to see what type of object was being transferred nor how the memory was oriented. This entire section had to be replaced and was given the same conversion rules as NumPy. Any conversion is possible (including lossy ones like float to bool). The rules to trigger the conversion is by slicing just as with NumPy. By replicating the rules of NumPy we hide the fact that NumPy is no longer used and increase typesafety. There was a number of cases where in the past it would reinterpret cast the memory that will now function properly. The old behavior was a useless side effect of the implementation and was unstable with machine architecture so not likely used be a user. The getArrayRange portion has to be split into two pieces. Under the previous implementation the type of the return changes from Python list to NumPy array depending on the compile option. Thus the test suite tested different behaviors for each. In removing NumPy we replace the Java array slice return with a Java array. Thus the type is always consistent. The Java array that is returned is still backed by the same array as before and has the start, end, and step set appropriately. This does create one change in behavior as the slice now has left assignment (just like NumPy) and before it was a copy. It is difficult exercise this but to do so we have to copy a slice to a new variable then use a second array dereference to assign an element. Because we converted to either list or NumPy, the second dereference could not affect the original. There is no way to avoid this behavior change without adding a large transaction cost. Which is why NumPy has the exact same behavior as our replacement implementation. We could in principle make the slice read only rather than allowing for double slicing, but that would also be an API change. There is one other consequence of producing a view of the original having to do with passing back to Java. As Java does not recognize the concept of an array view we must force it back to a Java type at the JNI level. We will force copy the array slice into a Java array at that time. Thus replicates the same functionality. This induces one special edge case as `java.lang.Object` and `java.lang.Serializable` which are both implemented by arrays must be aware of the slice copy. Before we trigger the conversion to NumPy or list by calling the slice without limits operator `[:]` on the array. Under the new implementation this is effectively a no-op. Thus we haven't broken or forced any changes in the API. The third case of direct byte buffers also revealed problems. Again the type of the buffer was not being check resulting in weird reinterpret cast like behaviors. Additionally, the memory buffer interface was making a very bad assumption about the referencing of the buffer by assuming that referencing the object that holds the buffer is the same as referencing the buffer itself. It was working only because the buffer was being leaked entirely and was likely possible to break under situations as the leaked buffer essentially locked the object forever. To implement all of this properly unfortunately requires making the Python wrapper of Java arrays a direct type. This is possible in JPype 0.8 series where we converted all classes to CPython roots, thus our only choice is to backport the JPype speed patch into JPype 0.7. API change summary ------------------ - The type of a slice is no longer polymorphic but is always a Java array now. - The unbounded slice is now a no op. - Buffer types now trigger conversion routines rather than reinterpret casting the memory. - Direct buffers now guard the memory layout such that they work only with mutable bytearray like types. - Assignment of elements though double slicing of an array now affects the original rather than just doing nothing effective like before. JPype Speed Patch ================= Speed has always been an issue for JPype. While the interface of JPype is great the underlying implementation is wanting. Part of this was choices made early in the development that favors ease of implementation over speed. This forced a very thin interface using Python capsules and then the majority of the code in a pure Python module. The speed issue stems from two main paths. The first being the method resolution phase in which we need to consider each method overload argument by argument which means many queries applied to each object. The second is the object construction penality when we return a Java object back to Python. The object bottleneck is both on the cost of the wrappers we produce, but additionaly all of the objects that are constructed to communicate with a Python function. Every int, list, string and tuple we use in the process is another object to construct. Thus returning one object triggers dozens of object to be constructed. We have addressed these problems in five ways - Improve resolution of methods by removing the two phase approach to resolving a type match cutting the resolution time in half. - Caching the types in C so that they don't have to go back to Python to execute a method to check the Python cache and construct if necessary. - Converting all of the base types to CPython so that they can directly access the C++ guts without going back through entry points. - Remove all Python new and init method from the Java class tree so that we don't leave C during the construction phase. Thus avoiding having to construct Python objects for each argument used during object construction. - Adding a Java slot to so that we can directly access Java resources both increasing the speed of the queries and saving us an additional object during construction. All of these are being implemented for JPype 0.8 series. For now we are backporting the last four to the JPype 0.7 series. The first is not possible to backport as it requires larger structural changes. Lets briefly discuss each of the items Method resolution ----------------- During method resolution the previous implementation had a two phase approach. First is tried to match the arguments to the Java types in canConvertToJava. In this each Java argument had to walk through each possible type conversion and decide if the conversion is possible. Once the we have the method overload resovled we then perform the conversion by calling convertToJava. That would then walk through each possible type conversion to find the right one and apply it. Thus we did tha work twice. To prevent this from happening we need to reserve memory for each argument we are trying to convert. When we walk through the list and find a conversion rather than just returning true, we place a pointer to the conversion routine. That way when we call convertToJava we don't have to walk the list a second time but instead go straight to the pointer to get the routine to execute. This change has two additional consequences. First the primary source of bugs in the type conversion was a mismatch between canConvertToJava and convertToJava thus we are removing that problem entirely. The second and more important to the user is that the type system is now open. By installing a routine we can now add a user rule. Therefore if we need `java.sql.TimeStamp` to accept a Python time object we just need to add this to the type conversion table at the Python level. This is implemented in the ClassHints patch. About half of our customizer code was to try to achieve this on a per method level. Thus this elimiates a lot of our current Python customizer code. The remaining customizer code is to rename Java methods to Python methods and that will remain. Caching of Python wrappers -------------------------- In the previous implementation there was a text keyed dictionary that was consulted to get type wrappers. To access it C++ called to a Python function that decided when to return a cached type and when to create a new one. This meant dozens of object constructed just to find the wrapper. To solve this we simply move the cache and add it to the JClass directly. We have to back reference the Python class so it can't go away while the JVM is running. There is one section of code that also uses the wrapper dict in the customizers which needs to decided does a wrapper already exist for the customizer. We have replaced these calls with methods on the module. Conversion of the Base classes ------------------------------ JPype has a number of base classes (object, primitive, exception, string, array) which hold the methods for the class. If they are implemented as pure Python than every access from C++ to these elements needs to create objects accordingly when then are passed back through the module entry points to get back to C++. We can avoid this by implementing each of these in CPython first at the module layer and then extending them in the exposed module so that they have the same outward appearance as before. We made one refinement during the conversion by implementing all of the CPython classes using the Heep type API which has the distinct advantage that unlike static types, it can be changed at runtime. Thus from Python we can add behavior to the heap types simply with by using `type.__setattr_`. This was a bit of a challenge as the documentation on heap types is much more sparse than for static types. However, after going through the process I would recommend that all new CPython modules should use heap types rather than static as API is much better and the result much more flexable and stable. The only downside being the memory footprint increases from 400 bytes to 900 bytes. There are a few rough spots in the heap type API in that certain actions like setting the Buffer have to be added to the type after creation, but otherwise it is a big improvement. Now if all of the documentation would just drop the old static API in favor of heap types it would be great. Constructor simplifications --------------------------- In order to benefit from moving all of the base classes to C, we have to make sure that derived classes do not transfer control back to Python. Currently this happens due to the factory nature of our classes. The entry point for JObject is shared between the construction of objects from Python and a return from Java. Thus we have to either separate the factory behavior by pushing those types out of the type tree or pushing the factory behavior into the C layer. We have chosen to split the factories and use overrides of the type system in the meta class to apply `isinstance` and `issubtype` behavior. We can further restrict the type system if we need to by adding verifications that the `__new__` and `__init__` methods must point the original base class implementations if need. Howver, we have not taken this step as of yet. The split approach effectively removes these heavy elements from type creation. The concequence of this is that means all of the rest of logic needs to be in CPython implementation. These can be rather cumbersome at times. It is always a slippery slope when pushing code from Python back to CPython. Some thing are needed as they are on the critical path while others are called only occasionally and thus represent no cost to leave in Python. On the other hand some things are easy to implement in CPython because the have direct access rather than having to go through a module entry point. We have gone with the approach that all critical path and all code the eliminates the need for an entry point should be pushed back to C. Java Slots ------------- In order to get any reasonable speed with Python, the majority of the code needs to be in C. But additionally there needs to be the use of slots which are hard coded locations to search for a particular piece of information. This presents a challenge when wrapping Java as we need a slot for the Java value structure which must appear on Python object, long, float, exception, and type. These different types each have their own memory layout and thus we can't just add the slot at the base as once something is added to the base object it can no longer be used in multiple inheritance. Thus we require a different approach. Looking through the CPython source, we find they have the same quandary with respect to `__dict__` and `__weakref__` slots. Those slots do not appear one the base object but get added along the way. The method they use is to add those slots to the back of the object be increasing the basesize of the object and then referencing them with two offset slots in the type object. If the type is variable length the slot offset are negative thus referencing from the end of the object, or positive if the object is a fixed layout. Thus we tried a few formulations to see what would work best. Broken tree approach ~~~~~~~~~~~~~~~~~~~~ The problem with just directly adding the slots in the tree is that the Java inheritance tree forces the order of the Python tree we have to apply. If we add a slot to `java.lang.Object` we have to keep the slot on `java.lang.Throwable` but that is not possible because Throwable requires it to be derived from Python `Exception`. Thus if we are going to add a slot to the base we would have to break the tree into pieces on the Python side. This is possible due to Python inheritance hacking with some effort. But this approach had significant down sides. When we go to access the slot we have to first figure out if the slot is present and if not then fall back to looking in the dictionary. But one of the most common cases is one in which the item has no slot at all. Thus if we have to both look for the slot and then hit the dictionary, this is worse than just going to the dictionary in the first place. Thus this defeats the point of a slot in many cases. Python dict approach ~~~~~~~~~~~~~~~~~~~~~~~~~ We attempted the same trick by increasing the basesize to account for our extra slot. This leaves to difficulties. First, the slot has no offset so we need to find it each time by implying its location. Second, the extra objects have to be "invisible" during the type construction phase, or Python will conclude the memory layout of the object is in conflict. We can fool the type system by subtracting the extra space from the type during the declaration phase and then adding it back after the base types are created. This approach failed because the "invisible" part is checked each and every time a new type is added to the system. Thus every dynamic type we add checks the base types for consistency and at some point the type system will find the inconsistency and cause a failure. Therefore, this system can never be robust. Dict and weakref appear to be very special cases within the Python system and as there is no general facility to replicate them working within the system does not appear to be viable. Memory hacking approach ~~~~~~~~~~~~~~~~~~~~~~~ The last system we attempted to mess with the memory layout of the object during the creation phase to append our memory after Pythons. To do this we need to override the memory allocator to allocate the requested memory plus our extra. We can then access this appended memory by computing the correct size of the object and thus our slot is on the end. We can test if the slot is present by looking to see if both `tp_alloc` and `tp_finalize` point to our Java slot handlers. This means we are still effectively a slot as we can test and access with O(1). The downside of this approach is there are certain cases in which the type of an object can be changed during the destruction phase which means that our slot can point to the wrong space if the basesize is changed out from under us. To guard against this we need to close our type system by imposing a ClassMeta which closes off mixin types that do not inherit from one of the special case base classes we have defined. The API implications should be small. There was never a functional case where extending a Java object within Python actually made sense as the Python portion is just lost when passed to Java and unlike Proxies there is no way to retrieve it. Further the extending a Java object within Python does not bind the lifespan of the objects so any code that used this is likely already buggy. We will properly support this option with `@JExtends` at a latter point. With this limitiation in mind, this appears to be the best implementation - It adds the slot to all of the required types. - The slot is immediately accessable using just two fields (basesize, itemsize) - The slot can be tested for easily (check tp_alloc, tp_finalize) - It closes the type system by forcing a meta class that guards against inappropraite class constuction. We could in principle add the slot to the "front" of the Python object but that could cause additional issues as we would require also require overriding the deallocation slot to disappear our memory from the free. The Python GC module has already reserved the memory in the front of the object so the back is the next best option. Speed patch implications ------------------------ Other than improving the speed, the speed patch has a lot of below the hood changes. So long as the user was not accessing private members there is no API change, but everything below that is gone. All private symbols like `__javaclass__` and `__javavalue__` as well as all exposed private members vanish from the interface. There is no longer a distinction between class wrappers and `java.lang.Class` instances for purposes of the casting system. The wrapper is an extension of a Python type and has the class methods and fields, and the instance is an extension of a Python object without these. Both hold Java slots to the same object. Therefore a lot of complexity of the private portions is effectively removed from the user view. Every path now has the same resolution, check the Java slot first and if not assume it is Python. Two private methods now appear on the wrapper (though I may be able to hide them from the user view.) These are the test entry points `_canConvertToJava` and `_convertToJava`. Thus the speed patch should be transparent all user code that does not access our private members. That said some code like the Database wrappers using JPype have roots in some code that did make access to the private tables. I have sent corrections when we upgraded to 0.7 series thus making them conforming enough not to touch the private members. But that does mean some modules may be making such accesses out in the wild. The good new is after the speed patch pretty much everything that is supposed to be under the hood is now out of the reach of the user. Thus the chance of future API breakage is much lower. Below the hood changes ====================== - We have tried to prevent backend changes from reaching the API, though this is not always entirely the case. The majority of the cases not already noted appear to be in the range of implementation side effects. The old implementation had bugs that create undefined behaviors like reinterpet casting buffers and the like. It is not possible to both fix the bugs in the backend and make preserve buggy behavours on the front end. We have limited our changes to only those for which we can see no desirable use existing. Calling a list slice assignment on a float from a numpy array of ints and getting a pile of gibberish was as far as we can tell not useful to the user. - setResource is dropped in favor of caching the module dictionary once at start of the JVM. We have a lot of resources we will need and the setResources method was cumbersome. - There is a lot of thrashing for the Python module style between C and C++ style. The determining blow was that C++ exception warning showed up when the proper linkage was given. Thus the perferred style flipped from C++ style to C. Thus the naming style change accordingly. - In addition to the style change there is also an attempt to isolate symbols between the different classes. The older style with a formal header that declares all the symbols at the top encouraged access to the functions and increased the complexity. Moving to a C style and making everything static forces the classes to be much more independent. - With the change to C style there is natural split in CPython class files between the structure declaration, static methods that implement Python API functions, the declaration of the type, and the exposed C++ style API used by the rest of the module. - There is some thrashing on how much of the C++ wrapper style Python API to keep. The rewrapping of the API was mainly so support differences between Python 2.7 and 3.x. So we dropped where we could. Only the JPPyObject which acts as memory handling device over pure Python style (because Python style is not exception safe) is strongly needed. - There is some spacing thrashing between different editors and the continuing debate of why C was written to have the pointer stick to to the variable rather than the type. When writing `Object* foo` it implies that the star is stuck to type rather than the variable where C reads it as the opposite. Hence there is the endless churn between what is correct `Object *foo` and what we would say in which `Object*` is actually a type. As Python favors the former and we currently have the latter that means at some point we should just have formatter force consistency. - We introduced Py_IsInstanceSingle. Is is a missing Python API function first for fast type check method of a single inherited type using the mro. Then something is singlely inherited we can bypass the whole pile of redirects and list searchs and just compare if the mro list matches at the same point counted from the end. As all of our base types are single inherited this saves time and dodges the more expensive calls that would trigger due to the PyJPClassMeta overrides. - We introduced Py_GetAttrDescriptor. This was previously implemented in Python and was very slow. Python has very good implementations for GetAttr but unfortunately it has the behavior that it always dereferences descriptors. Thus it is useless in the case that we need to get the descriptor from the type tree. We have reimplemented the Python type search without the descriptor dereferencing behavior. It is not nearly as complete as the Python method as we only need to consult the dictionary and not handle every edge case. That of course means that we are much faster. - As with every rewrite there is the need to cleanup. Anything that wasn't reached by the test bench or was identified as being called only from one location was removed or reencorperated back in into the function that call it. jpype-1.6.0/doc/ChangeLog-0.7.rst000066400000000000000000000546141501674766700163320ustar00rootroot00000000000000:orphan: JPype 0.7 Core ChangeLog ======================== Here is the "complete" log of the changes I think I made. Module changes -------------- * Moved Python module objects to namespace ``PyJP`` so that they are consistent with a Python module namespace. Renamed module classes presented to ``_jpype`` extension to ``PyJP*`` to match the internal classes. Though not exactly a standard convention, the types were internal anyway and having the names match the C structure makes it more clear what resource is being accessed. It also eliminates the confusion between ``jpype`` and ``_jpype`` resources. * Removed all usage of Capsule from the extension module. This was bridging between Python versions and had to be replicated on old platforms. As the capsules were functioning as crippled objects, they could not have methods of their own. Thus functionality that properly belonged to a specific class would get pushed to the base class. This affected former capsules of ``JPObject``, ``JPProxy``, and ``JPArray``. These are now formal classes in the module as ``PyJPValue``, ``PyJPProxy``, and ``PyJPArray``. * Moved the initialization of each class to the ``__init__`` function. Thus rather than creating the resource at the top level ``_jpype`` module (such as ``_jpype.findClass('cls'))``, the resource is created by allocating a new object (such as ``_jpype.PyJPClass('cls')``). * The presentation of ``JPArrayClass`` has been merged as a generic ``JPClass``. The only requirement for creation of an array instance is that the supplied ``PyJPClass`` satisfy ``isArray()``. * Removed direct dependencies that objects holding resource be exactly the type in ``jpype`` module. This reduces the restrictions in the underlying Python layer and allows for multiple classes such as ``JavaArray``, ``JWrapper``, and ``JavaClass`` to all be recognized as holding resources. This simplifies some paths in the ``jpype`` module where we needed to simply access a single method during bootstrapping and we were forced to construct complete classes necessitating the order of resource loading. * Remove ``JPObject`` concept and replaced it with ``JPValue``. ``JPValue`` holds the type of the object and a ``jvalue`` union. Both ``JavaClass`` and ``JWrapper`` now point to these classes as ``__javavalue__``. Anything with a ``__javavalue__`` with type ``_jpype.PyJPValue`` is now recognized as being a Java object. * Changed the recognization of a ``JavaClass`` to any object holding ``__javaclass__`` with type ``_jpype.PyJPClass``. This allows array classes, object classes, and wrappers classes to be used together. * Added hooks to direct convert ``PyJPClass`` to a ``PyJPValue`` with a type of ``java.lang.class`` and an object to the class. This replaces the need for calling ``forName`` to get to the existing class. * Changed ``PyJPField`` and ``PyJPMethod`` to descriptors so that we do not need to mess with ``__getattribute__`` and ``__setattr__`` in many places. * Eliminated the unnecessary class bound method. C++ Reorg ~~~~~~~~~ * Reorganized the type tree in the C++ layer to better match the Java structures. * Flattened out the redundant layers so that ``JPType`` is now ``JPClass`` corresponding to an instance of a ``jclass``. * ``JPClass`` is not the base class. Arrays are now objects and have base classes and methods. * Split ``JPClass`` into a separate type for each specialized object class for boxed, ``java.lang.Object``, and ``java.lang.Class`` which all required specialized conversion rules. * Boxed, string, base ``java.lang.Object`` and base ``java.lang.Class`` are now specialized with their required conversion rules. Path reduction ~~~~~~~~~~~~~~ * Removed ``HostRef`` and all of its usage. It was a halfway memory management method. To be passed around it was being held as a dynamically allocated pointer type with no static presence for cleanup. This defeats the point of having a smart point wrapper if the smart pointer is being used as a pointer itself. Thus it was only as safe as the user applied conventions rather than safe by design. * Replaced all the ``HostRef`` methods and ``JPy*`` Python object wrappers with a new smart pointer concept (namespace ``JPPy``). This removes the redundant host and ``JPy*`` wrapper layers. * Removed multiple optimization paths such as bypassing between ``jchar`` and ``unicode`` if the size matched. These paths were for speed reasons, but they could only be tested on particular machines. Thus it was difficult to tell if something was broken. It is better to have one tested code path that is slight slower, then a faster path that is busted. * Removed dead class ``JPCharString``. * **(bug)** Replaced all string handling with conversion through UTF8. Java and Python use different UTF8 encodings and thus those paths that were trying to short cut directly through from one system to another were badly flawed. By forcing a conversion to and from each time a Java string or Python string are passed eliminates conversion problems. This should resolve user issues having to do with truncating extended unicode characters. * Combined all code paths in ``canConvertToJava`` and ``convertToJava`` to use the ``JPValue`` * Combined code paths from ``check`` and ``get`` for ``JPValue``, ``JPClass`` and ``JPProxy`` ``get`` patterns when fetching from Python. Almost always we want to use the object immediately and just check if we can. * Removed the entirely redundant Primitive type ``setRange`` and ``getRange``. That code was entirely dead because it could not be reached. Renamed the direct methods as they now have the same function. * Removed ``JPTypeName``. This concept will be phased out to support lambdas. ``TypeManager`` now used ``getCanonicalName()``. Transferred responsibility for conversion to native names to Python module interface. * Introduced named classes for all specialized instances of classes to be held in ``TypeManager`` namespace. Thus converted most of the "is this type" to comparison of ``JPClass*`` pointers in place of string level comparisons. * Removed near duplicate methods. ``JProxy`` was requesting slightly altered copies of many conversions to support its usage. These operations could be supported by just splitting to two existing methods. Thus we could eliminate a lot of stray methods that served this specialized purpose. * ``JPArray`` is now a method holder rather than the primary object like ``JPBoundMethod``. All array objects in Python now hold both a ``__javaarray__`` and a ``__javavalue__``. This eliminates need for special paths for arrays. * ``_getClassFor`` is now overloaded to work with array classes. Thus asking for a ``JClass('[java.lang.Object;')`` will now correctly return a JavaArrayClass. * Constructing a string now shortcuts to avoid methodoverload resolution on new instance if given a Python string. * Reworked the GIL handling. The previous model was doing all the release locks on the JPJni calls automatically for almost all jni transactions. This would be fine, except that many utility functions were using those same calls regardless of whether is was a good time to release the lock. This ultra fine grain locking was effectively allowing any call to JPJni methods to become a break point, including those calls in critical sections such as ensureTypeCache and TypeManager::findClass. Any time it loaded a class or looked up a name it could be interrupted and thus end up in a corrupt state. Thus I moved all of the GIL calls to those places where we call user code on the type returns and the object constructors. Thus cuts the number of GIL transactions greatly and eliminates the need to deal with trampling global resources. The refactor exposed this a bit more because the removal of TypeName meant that we did a lot more transactions to get the class name. But that does not mean the flaw was not there before. If our tests cases had been any more aggressive about creating class instances during execution it would have overrun the TypeManager table and all would have failed. * Removed the previous default option to automatically convert ``java.lang.String`` to either a Python string or a unicode when returning from Java. This does mean some string operations now require calling the Java string method rather than the Python one. Having strings not convert but rather remain on the jvm until needed cuts the conversion costs when working with Java heavy code. I added a caching mech so that if we need to convert the string multiple times, we don't pay additional over the previous option. * A special ``toString`` method was added to ``PyJPValue`` to convert Java strings to Python strings. This can convert Java string resources to Python ones on request. Proxy changes ~~~~~~~~~~~~~ * Proxy as implemented previously held only a pointer to the proxy object and from this proxy object it lookup up the callable using either a dictionary or an instance. The majority of the resources were held by the ``jpype.Proxy``. This was replaced with a more general function in which the ``PyJPProxy`` proxy holds two resources. One is an object instance and the other is a lookup function that turns the name to a function definition. This supports the same use cases but eliminates the need for finding resources by convention. There is no need for the proxy in Python to have any specific layout other than holding a PyJPProxy as ``__javaproxy__``. Thus allowing alternive structures such as Proxy by inheritance to work. * Memory handling was changes slightly as a result so that the reference queue is now responsible for cleaning up the proxy. Proxy handle instances are generated whenever the proxy is passed to Java. Thus we form no counting loops as the proxy has no reference to the handles and the handles hold a reference to the proxy. Exception changes ~~~~~~~~~~~~~~~~~ * Changed all exception paths to use ``JPypeException`` exclusively. The prior system did way to much in the Exception constructors and would themselves crash if anything unusual happened making changing of the system nearly prohibitive to debug. Everything bubbles down to ``toJava`` and ``toPython`` where we perform all the logging and pass the exception off. This also centralizes all the handling to one place. * This pulls all the logic from ``JPProxy`` so that we can now reuse that when returning to any Java jni native implemented function. * Same thing for Python, but that was already centralized on ``rethrow``. * Reworked exception macros to include more info and introduced ``JPStackInfo``. It may be possible to connect all the stack info into the Python traceback (via a proxy class) to present a more unified error reporting. But this work is currently incomplete without a Python layer support class. * Integrated ``JPStackInfo`` into tracer to give more complete logs when debugging. Code quality ~~~~~~~~~~~~ * Applied a source formatter in netbeans. It is not perfect as it tends to add some extra spaces, but it does make faster work of the refactor. Custom spacing rules were applied to netbeans to try to minimize the total changes in the source. * Improved error handling where possible. * Rework ``JPTracer`` so that reporting from places that do not have a formal frame or could not properly throw (such as destructors) and still appear in the trace log. All ``TRACE`` macros were moved to ``JP_`` so that were less likely to hit conflicts. Removed guards that complete disabled Tracer from compiling when ``TRACE`` was not enabled so that unconditional logging for serious failure such as suppressed exceptions in destructors can report. * Defensively added ``TRACE`` statements whenever entering the module for a nontrivial action so that errors could be located more quickly. * Removed ``MTRACE`` layer as Java local frame handles all cleaning tasks for that now. * Replaced TRACE1, TRACE2, TRACE3 with a variodic argument macro ``JP_TRACE`` because I am too lazy to remember to count. * Renamed functions to best match the documented corresponding function in the language it was taken from. Thus making it easier to find the needed documentation. (Ie ``JPyString::isString()`` becomes ``JPPyString::check()`` if the corresponding language concept is ``PyString_Check()``). This does mean that naming is mixed for the Java/Python layers but it is better to be able to get the documentation than be a naming idealist. * Used javadoc comments on header of base clases. These strings are picked up by netbeans for document critical usage. * Moved method implementations and destructors out of headers except in the case of a truly trivial accessor. This has a small performance loss because of removal of inline option. This reduces the number of redundant implementation copies at link time and ensures the virtual destructor is fixed in a specific object. We can push those back to the header if there is a compelling need. ``jpype`` module changes --------------------------- Because these do affect the end user, we have marked them as enhance, change, remove, bug fix, or internal. General ~~~~~~~ * **(enhance)** ``__all__`` added to all modules so that we have a well defined export rather that leaking symbols everywhere. Eliminated stray imports in the jpype namespace. * **(enhance)** Add ``@deprecated`` to ``_core`` and marked all functions that are no longer used appropraitely. Use ``-Wd`` to see deprecated function warnings. * **(enhance)** Exposed ``JavaInterface``, ``JavaObject``, ``JavaClass`` so that they can be used in ``issubclass`` and ``isinstance`` statement. ``JavaClass.__new__`` method was pushed to factory to make it safe for external use. * **(enhance)** mro for Java Classes removes ``JavaInterface`` so that ``issubclass(cls, JavaInterface)`` is only true if the class not derived from ``JavaObject``. * **(enhance)** All classes derived from ``java.lang.Throwable`` are now usable as thrown exceptions. No requirement to access special inner classes with exception types. Exceptions can be raised directly from within a Python context to be passed to Java when in proxy. Throwables now use a standard customizer to set their base class to the Python Exception tree. Deprecated ``JException`` * **(enhance)** ``args`` is a property of ``java.lang.Throwable`` containing the message and the cause if specified. * **(enhance)** ``JChar`` array now converts to a string and compares with string properly. Conversion uses range so that it does not try to convert character by character. * **(remove)** ``JByte`` array is not a string type. It is not a string in Java and should not be treated as a string without explicit conversion. Conversion path was horribly inefficient converting each byte as a Python object. Test marked as skip. * **(change)** Array conversion errors produce ``TypeError`` rather than ``RunTimeError``. * **(enhance)** ``JArray`` now supports using raw Python types as the specifier for array types. It will convert to the most appropraite type or return an error. * **(remove)** property conversion customizer is deactivated by default. This one proved very problematic. It overrided certain customizers, hid intentionally exposed fields, bloated the dictionary tables, and interferred with the unwrapping of exception types. We can try to make it an optional system with ``import jpype.properties`` or some such but it will still have all those problems. Best to kill this misfeature now. * **(enhance)** ``JArray`` classes now have ``class_``. We can access the component type. This makes them more consistent with ``JClass``. (required for testing) * **(enhance)** Use of constructor call pattern eliminated the need for use of a separate factory and type. Thus we are back to the original design in which we only need to expose a small number of "types". This was applied to ``JArray``, ``JClass``, ``JException``, and ``JObject``. Use of ``isinstance()`` and ``issubclass`` now supported. The only challenge was keeping box types working. * **(remove)** Functions that return a string now return a ``java.lang.String`` rather than converting to Python. Thus when chaining elements together in Java will get the full benefit matching types. The previous auto convert has been removed. * **(enhance)** ``java.lang.String`` now has much more complete set of Python operations. String conversions are now cached, so the penalty of converting is kept to a minimum. Wrappers ~~~~~~~~~ * **(internal)** Rewrote the ``JWrapper`` module from scratch to reflect the use i of ``JPValue``. Renamed ``_jwrapper`` to ``_jtypes``. The concept of wrappers has now been lost internally. All objects and primitives are just values. * **(enhance)** Created import module containing all of the symbols needed for creating types in jpype so that we can support a limited import statement ``from jpype.types import *`` * **(enhance)** ``JString`` contructor now returns a ``java.lang.String`` object. Removed ``JStringWrapper`` as ``java.lang.String`` serves its purpose. * **(enhance)** ``JObject`` now returns an object with the Java type as a functional object rather than a dead end wrapper. This does allow some redundant things such as converting a Python class wrapper into a class ``JObject(java.lang.String) == java.lang.String.class_`` but otherwise seems good. * **(enhance)** 'JObject' and 'JString' accept 0 arguments to generate a generic object and empty string. * Tried to be more consistent about returning errors that are valid in Python. - Too many or two few arguments to a function will throw a ``TypeError`` - Value conversion out of range will throw ``OverFlowError`` - Value conversions that are the right type but invalid value will give ``ValueError`` (char from string too long) - Type conversions that cannot be completed should give ``TypeError``. - Errors setting attributes should give ``AttributeError`` such as trying to set a final field or trying to get an instance field from a static object. - Arrays access should produce ``IndexError`` on bad range. (it would be nice if these also mapped to Java errors and the corresponding errors in Java were derived from the Python error so that we can properly look for ArrayIndexOutOfBoundsException (derived from IndexException). But that is too heavy to attempt now.) * **(enhance)** ``JArray``, ``JException`` and ``JObject`` report as JavaClass when using issubclass. * **(enhance)** Short cut for just adding a base class as a customizer. Internal ~~~~~~~~~ * **(internal)** Changes corresponding to the ``__init__`` rework to match revised ``PyJP*`` classes. * **(internal)** Changes corresponding to the capsule removal. * **(internal)** Remove ``SPECIAL_CONSTRUCTOR_KEY`` as everything that uses it can recognize a PyJPValue as indicating they are receiving an existing Java resource as input. All special handling required to construct objects from within C++ layer were thus eliminated. * **(internal)** Removed almost all required resources from Python needing to be register in ``_jpype`` with the exception of getClassMethod. * **(internal)** Java class customizers did not need to be deferred until after the JVM is initialized. Pushing them into the dictionary immediately fixes issues in which a customizer was not applied to classes during early bootstrapping. This eliminates a large number of the need for calling initialize on each jpype module in ``_core``. * **(internal)** ``JArrayClass`` and ``JClass`` are the same for purposes of Customizers and class tree. * **(internal)** Customizer code and dictionary moved to ``_jcustomizer`` so that i it can be shared between Object and Array classes. * **(internal)** Converted ``JavaClass`` to more Python like "try first, eat an exception if it fails" philosophy to increase robustness to failure. This eliminates the problems when a new base class is introduced with a customizer without setting up a meta class. * **internal/enhance** Broke connections between boxed types and wrappers. User supplied wrappers can implements specified "Value" method. Wrapper types now have similar methods to boxed types with appropriate range checks. * **(internal)** All ``$Static`` meta classes have been eliminated. There is now only one tree of classes. A single meta class ``JClass`` serves as the type for all classes. Bugs ~~~~~~~ * **(bug fix)** Fixed bug in ``jpype.imports`` in which it would not install its hooks if loaded afer the jvm was started. * **(bug fix)** Fixed bug in JBoxed type wrappers in Python which would lead ``java.lang.Double`` and ``java.lang.Float`` to have an integer value when boxed was corrected. * **(bug fix)** Fixed bug in ``JObject`` that was preventing classes from being wrapped as objects. Verified a number of test cases in the test suite. * **(bug fix)** Reenabled the throw from Java test during proxy. The issue was that jpype was releasing resources before it could transfer control a ``PyErr_Clear`` removed the reference and thus our throwable was invalid. It was dastardly to find, but the fix was moving a statement one line up. Documentation changes ~~~~~~~~~~~~~~~~~~~~~~ * Documentation of major class methods have been added as well as marker whereever the underlying assumptions are not reasonably transparent. * Action items for further work have been marked as FIXME for now. Incomplete ---------- These tasks had to be pushed over post 0.7 release. * Finish specialization of ``JPArray`` classes for ``byte[]`` and ``char[]`` * Deal with fast array conversions misuse of types. ``int[]<=>float[]`` * Direct bridge methods for ``char[]`` are currently bypassing the unicode translation layer. It is unclear what Java does with extended unicode when dealing with ``char[]``. * Add a system to register a translation customizer so that we do not need to modify C++ code to add new simple translations like Python date to Java Instant. These would be installed into the PyJPClass during class wrapper customization. We will need to make sure each class has a Python type wrapper cached in ensureTypeCache so we are guaranteed to find the conversion. * Add tests for Exception.args jpype-1.6.0/doc/Makefile000066400000000000000000000151461501674766700151040ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JPype.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JPype.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/JPype" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JPype" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." jpype-1.6.0/doc/_static/000077500000000000000000000000001501674766700150635ustar00rootroot00000000000000jpype-1.6.0/doc/_static/theme_overrides.css000066400000000000000000000002411501674766700207560ustar00rootroot00000000000000/* override table width restrictions */ @media screen and (min-width: 767px) { .wy-table-responsive table td { white-space: normal !important; } } jpype-1.6.0/doc/add_labels.py000066400000000000000000000133771501674766700160740ustar00rootroot00000000000000import re def sanitize_label(text): """ Sanitize the label text by removing or replacing special characters. Only allow alphanumeric characters and underscores. """ # Replace spaces with underscores text = text.replace(" ", "_") # Remove any characters that are not alphanumeric or underscores text = re.sub(r"[^\w]", "", text) return text.lower() def sync_labels(input_file, output_file): """ Synchronize labels with headers in an RST file. Labels are generated based on the chapter, header, subheader, and sub-subheader structure. The script ensures anchors appear immediately before the correct header text, preserves all section content, and avoids duplicating labels. Header Hierarchy: - Chapters are identified by lines underlined with `*`. - Headers are identified by lines underlined with `=`. - Subheaders are identified by lines underlined with `-`. - Sub-subheaders are identified by lines underlined with `~`. Label Format: - Labels follow the format: `.. _chapter_header_subheader_subsubheader:` - Labels are generated dynamically based on the text of the header and its position in the hierarchy. """ # Regular expressions to identify underline patterns for different header levels underline_patterns = { "*": "chapter", "=": "header", "-": "subheader", "~": "subsubheader", } # Variables to track the current hierarchy of headers current_chapter = None current_header = None current_subheader = None # Read the input file with open(input_file, "r") as infile: lines = infile.readlines() # Initialize output lines output_lines = [] buffer = None # Holds the previous line to check for headers last_label = None line_count = 0 for i, line in enumerate(lines): # Debugging: Print the current line being processed print(f"Processing line {i}: {line.strip()}") if line.startswith(".."): last_label = line line_count = 0 # Check if the line is an underline pattern if re.match(r"^\*+$", line): # Process chapter current_chapter = sanitize_label(buffer.strip().lower().replace(" ", "_")) if buffer else None label = f".. _{current_chapter}:\n\n" if current_chapter else None if label and not line_count == 2: print(f"Detected chapter: {current_chapter}, adding label: {label.strip()}") output_lines.append(label) if buffer: print(f"Detected chapter (skip): {current_chapter}, adding label: {label.strip()}") output_lines.append(buffer) # Add the header text immediately after the label output_lines.append(line) # Add the underline itself buffer = None elif re.match(r"^=+$", line): # Process header current_header = sanitize_label(buffer.strip().lower().replace(" ", "_")) if buffer else None if not current_header: # Fallback for empty buffer current_header = "unknown_header" label = f".. _{current_chapter}_{current_header}:\n\n" if current_header else None if label and not line_count == 2: print(f"Detected header: {current_header}, adding label: {label.strip()}") output_lines.append(label) if buffer: print(f"Detected header (skip): {current_chapter}, adding label: {label.strip()}") output_lines.append(buffer) # Add the header text immediately after the label output_lines.append(line) # Add the underline itself buffer = None elif re.match(r"^-+$", line): # Process subheader current_subheader = sanitize_label(buffer.strip().lower().replace(" ", "_")) if buffer else None label = f".. _{current_chapter}_{current_subheader}:\n\n" if current_subheader else None if label and not line_count == 2: print(f"Detected subheader: {current_subheader}, adding label: {label.strip()}") output_lines.append(label) if buffer: output_lines.append(buffer) # Add the header text immediately after the label output_lines.append(line) # Add the underline itself buffer = None elif re.match(r"^~+$", line): # Process sub-subheader subsubheader_text = sanitize_label(buffer.strip().lower().replace(" ", "_")) if buffer else None label = f".. _{current_chapter}_{subsubheader_text}:\n\n" if subsubheader_text else None if label and not line_count == 2: print(f"Detected sub-subheader: {subsubheader_text}, adding label: {label.strip()}") output_lines.append(label) if buffer: output_lines.append(buffer) # Add the header text immediately after the label output_lines.append(line) # Add the underline itself buffer = None else: # If the line isn't an underline, store it in the buffer if buffer: output_lines.append(buffer) # Add the previous line to the output buffer = line # Store the current line for processing if not line.isspace(): line_count += 1 if buffer is not None: output_lines.append(buffer) # Write the output to the specified file with open(output_file, "w") as outfile: outfile.writelines(output_lines) print(f"Labels synchronized successfully! Output written to {output_file}") # Input and output file paths input_file = "userguide.rst" output_file = "userguide_with_synced_labels.rst" # Run the label synchronization sync_labels(input_file, output_file) jpype-1.6.0/doc/android.rst000066400000000000000000000064351501674766700156170ustar00rootroot00000000000000JPype for Android ================= We ported JPype to the Android system. There are a number of important differences between JPype on Android and on a JVM. The Andoid platform uses the Dalvik virtual machine(DVM) which supports only a portion of the Java specification. As a result some portions of JPype have been removed or function differently. Supported Functions ------------------- The following features operate the same on both the JVM and DVM version. - Java classes using ``jpype.JClass`` - Access to Java methods and fields - Java arrays using ``jpype.JArray`` including slicing and direct transfers - Java primitive types, string type, and boxed types - Java.nio byte buffer including memory mapped data - Python collections API for Java collection types - Class customizers and type conversions - Support of scientific codes such as numpy - Implementation of Java interfaces using JProxy Functional differences ---------------------- When using JPype on Android, the virtual machine is started prior to the start of Python. Thus, there is no need to lauch the machine. Instead to use jpype only the statement ``import jpype`` is required. In addtion, the following functions have been removed... - ``jpype.startJVM`` is removed as the virtual machine cannot be started more than once. - ``jpype.shutdownJVM`` is removed as DVM cannot to stopped during operation. - ``jpype.addClassPath`` is removed as DVM does not support class path based jar loading. Dex files can be loaded dynamically using the Android API. - ``jpype.getDefaultJVMPath`` is removed as there is no JVM on Android. - ``jpype.beans`` has been removed as adding addition properties for semantic sugar increase the memory profile unnecessarily. - attach and detaching of threads is not allowed and those entry points have been removed. Removed JPype Services ---------------------- Not all JPype services are provided on Android as some functions depend on the internals of the JVM. These include... - The dynamic class loader does not operate on JPype as DVM does not support jar files. - jpype.imports and jpype.JPackage use the jar file system to identify packages which is not available on DVM. Therefore, all classes must be loaded use ``jpype.JClass``. - jpype support for Javadoc was removed as DVM does not support Javadoc jars in the classpath. - jpype does not install interrupt handlers for ^C on Android. Unsupported Java libraries -------------------------- Some Java libraries are not supported on Android. - Bytecode manipulation libraries to not function on DVM. This means generation of dynamic classes, manipulation of loaded classes, and Java agent code will not function. - Some standard Java libraries are not implemented on Android such as AWT. Jar libraries that access these unimplemented libraries will not function. - Private JVM internal classes such as ``sun.*`` are not implemented and cannot be used. Behavior differences -------------------- Some changes in JPype behavior occur simply because the DVM operates differently. These changes include - java.lang.Class.forName will not load classes from classes.dex. Attempting to access classes with forName will get ClassNotFoundException. Applying forName to base classes that use the system classloader function as expected. jpype-1.6.0/doc/api.rst000066400000000000000000000061071501674766700147440ustar00rootroot00000000000000API Reference ============= JVM Functions ~~~~~~~~~~~~~ These functions control and start the JVM. .. autofunction:: jpype.startJVM .. autofunction:: jpype.shutdownJVM .. autofunction:: jpype.getDefaultJVMPath .. autofunction:: jpype.getClassPath .. autodecorator:: jpype.onJVMStart Class importing ~~~~~~~~~~~~~~~ JPype supports several styles of importing. The newer integrated style is provided by the imports_ module. The older ``JPackage`` method is available for accessing package trees with less error checking. Direct loading of Java classes can be made with JClass_. For convenience, the JPype module predefines the following ``JPackage`` instances for ``java`` and ``javax``. .. autoclass:: jpype.JPackage Class Factories ~~~~~~~~~~~~~~~ .. _JClass: .. autoclass:: jpype.JClass .. autoclass:: jpype.JArray .. autoclass:: jpype.JException Java Types ~~~~~~~~~~ JPype has types for each of the Java primitives: ``JBoolean``, ``JByte``, ``JShort``, ``JInt``, ``JLong``, ``JFloat`` and ``JDouble``. There is one class for working with Java objects, ``JObject``. This serves to cast to a specific object type. .. autoclass:: jpype.JObject .. _threading: Threading ~~~~~~~~~ .. autofunction:: jpype.synchronized .. autoclass:: java.lang.Thread :members: Decorators ~~~~~~~~~~~ JPype uses ordinary Python classes to implement functionality in Java. Adding these decorators to a Python class will mark them for use by JPype to interact with Java classes. .. autodecorator:: jpype.JConversion .. autodecorator:: jpype.JImplementationFor .. autodecorator:: jpype.JImplements .. autodecorator:: jpype.JOverride Proxies ~~~~~~~ JPype can implement Java interfaces either by using decorators or by manually creating a JProxy. Java only support proxying interfaces, thus we cannot extend an existing Java class. .. autoclass:: jpype.JProxy Customized Classes ~~~~~~~~~~~~~~~~~~ JPype provides standard customizers for Java interfaces so that Java objects have syntax matching the corresponding Python objects. The customizers are automatically bound to the class on creation without user intervention. We are documentating the functions that each customizer adds here. Information about Java methods can be found in the Javadoc. These internal classes can be used as example of how to implement your own customizers for Java classes. .. autoclass:: java.util.Iterable :members: :special-members: .. autoclass:: java.util.Collection :members: :special-members: .. autoclass:: java.util.List :members: :special-members: .. autoclass:: java.util.Map :members: :special-members: .. autoclass:: java.util.Set .. autoclass:: java.util.Iterator .. autoclass:: java.util.Enumeration .. autoclass:: java.lang.AutoCloseable Modules ~~~~~~~ Optional JPype behavior is stored in modules. These optional modules can be imported to add additional functionality. .. _imports: .. automodule:: jpype.imports .. automodule:: jpype.pickle .. autoclass:: jpype.pickle.JPickler .. autoclass:: jpype.pickle.JUnpickler .. automodule:: jpype.beans .. automodule:: jpype.types jpype-1.6.0/doc/attach_debugger.png000077500000000000000000000412531501674766700172630ustar00rootroot00000000000000‰PNG  IHDREC4ΟzAsRGBΞιgAMA± όa pHYsttήfxB@IDATx^νέ|eώΗρ_@J^•ήQ€ "‚4±+ž žXQ8EžzΨNΛِβέ)`χΠSDDQ€ˆ₯χ&EΪώηϋd&,!Ιn* ωΌ_―ΝΞΜ3»™έη7Οσ{fcBσψwvθΠ!7ΜObbbά­P‘BΙσξ^A‘‘˜ Ω‘ƒ‡\ΐρ¬PαBζ…GvΒ 'Έ)Ζ ˆBϋφο#’‚£Ψ"±VθΰΑƒD ΐR€x¨Π‘(ΨΕμΩ³'ͺŒκ}ϋχ[Β]–`‰‰‰nY\\œΕΗΗ[|Ι’[€ˆ[­Όώ| `‰*(ΪϋΗφΫΦ­.[ΙHΊ‰ζƒeεΚ—·bE‹Ίε‘δυη½Pβ.Ϋ΄ν+]Ή’ΙQ«‘„υ6ω»νΦͺ}c;±0#ed€±hιP Œ)^ΌΈΕΖΖ&eΣ΄–‰ΦΡΊ‘δυη1;Χόl ~žg+·'΅Πφžm3V%ψsήΨ‘]φΣ“mεώ’¬8d1–θύ€μ1(R—”Z[`θΪEαΧ1 ¦U¦u΄n$yύωD/ζP‚­χŸUk³ƒn* ή‰J}ΔΓNTBv‚vO₯‹‰@ΦEμ>[½z΅»―ΐ"2Β©EFεϋφνsεΥ«WχKR—ΧŸ@τl[nSζ$Ψ駝h?Ν_jg΄λdbΩ¦E³lώΊΓ­D§Άϊ“ύ>λ{[γ=VlhN?Ι οΫe«ωΩ~٘΄nω:§Yڕ-Ξο†Kά±ήώ°Π~σ¦C1ν”fυ­Z™’JXk_|»ΨͺΧn«—―vλVmΠάN©V&ς™€!βρCIΛΚΙ 64r^λ ΞιΙλΟ :1vΘΆ―_f‡*W±J«X­³5ΏνΆwX)[«±^=ήV?ΝZ΅>ΣΚ•8Ρͺ5­οW­qsk[·‚·ΦAΫτΣ·Άxwkqf[kσ§Σ-΄μ'[²y―[OΟt/ Š©ΣΔΞ<λLkV'dKfmλΒΞγV&ΔٟΞjkj\ΣΦ/™m«θPyœTȜ};lωΖ«W­Œ7g•λ•±­‹6[’[4ήJ-bρq%¬d| ‹-c%Κ”΅J^ΰ_ΚJυΦ*l•›w²N-k[ιβqV¬di;ΉrΘvύ‘”sτϋΖEv¨lC;­V+Q¬„•―yš΅nΪΔJ%£π’²²ΦΪ †J+j₯+Χ²S½ΝX·}_1(°vεγ¨* ι”σZGλF’ΧŸ@tφlΫdΚ[Άp‘-X°ΐ~ωe»ΕZf[χ$΅ΦΥf΄ή&_-d —Ω—_|a“½Ϋ_~eσΌ «ΈwTбύ–°5ΖΚT(ε…NΑΪ…,Ύ\‹=όέN™Lλί@fD Št)o’u΄n$yύωDcϋlλΚuvπ€Φ¬ώIV©R%«Ρτt«ζ}έVnϊέ­£ƒΛCa‘‘ΐπn… %…91μ—+­ΚigZΗN¬s§s­I…©­GIΤ%Κ‡lΫ–]GW‰»ΜoH€l9(*Yεγ(QY†’–ƒ`#˜V™ΦΡΊ‘δυηYhο6[’`ΦΈΖΙVl9«P‘‚•-WΡͺ7¬l{–―΅έ‡”qδ@ΛΧێ½ϋ’›Β±VΦ š6nώΝ lΌθ(ι|Εz',φ'ZΒζ•6oKŒ%]D#ΖJVh`…·ύlΏίn‰ϋχۍKmϊΜomγ‡Γ€π€ ²*bP€+AλΒ‡²gΟ`‰ΜšΦ2Ρ:Ρ\5:―?€ΘφόΆΙB…κXyυu…)^ΎͺΪ`[vο·βεͺYωΠZϋaΖWξΪD!σ‚’'ΩΞesmϊWkm_‘’V·QuΫΌθ{›6mΊΝX²zΥγ-h*\ͺΊΥ€Ά­Y8Ϋ¦O›fί-Xi5Nki5βu²SΘ{ΎβvBΔ#DŸωpLΕ„فPŒ.δ7₯ΰΚ…,¦pαΘgqQEΗ3NΌ<E‚"OΜΌyσΘ)^ΜΆmΫŠ@Gχ€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(ππƒ°Ή@;x_b’νή½ΫφμΩc‰ή4ΘΊΈΈ8+^ΌΈ•(QΒb½ιyfεA[·n΅˜˜;逓¬tι~ Ȋ;vΨΊuλ, Yωςε]”YtŸε΅) :υΤS ˆΘFͺWUΏͺžU}›EΉ@oR•ͺUύ9έTΟε{χξ΅²eΚψsΗΏΈfLr‹κYΥ·YQΈώόι\£JSΤΤUlήΌΩͺU«ζΟEoώόω6eΚkάΈ±Ώ$η}ψα‡6}ϊtϋρΗmαΒ…–ΰΨt‹ΦΉηžk«V­²φνΫϋKς§^xΑZ΅juΤϋμ£9sζΨΚ•+­hΡ’VΖz3Ί6nάhoΎω¦|ςΙο/5{ύυΧ­FξΉf“'OΆ[o½ΥͺV­j΅kΧφ—iυκΥV‘B.γ2άRτΙ'ŸΈΆnoΏύΆ}χέwφΫoΏω₯ΡΉόςΛν©§žςηŽ™m5ωμ³ΟμοχέηΟεŽAƒΩ?ωO›9s¦χώΎe7ί|³KW€PάύΙω_)ίν£^|Α&Mšdχάsϋr~πΑΙeΩZ~Λ-·ΨΛ/Ώμ/I²eKλΡ£Gς dS§N΅Αƒ»γ©ξ5Ÿš¬φRd8(zϊι§ν•W^±οΏήώϋίΪwάαœFŽ鯁ό[·nφ―ύΛ>ώx‚-Z΄ΘUςzŸίx㠍γΫϋοΏo_ύ΅]{ν΅ώ’£ίν|wR°|ωrλΠ‘ƒϋN’έ x^}υU«W―ž[?όΛ|Κ)§Ψi§fύϋχχ—@ΑτΝ7ίΈΞ‹.ΊΘώσŸΈ{ΝkyvΛTNQ§NάnΠRτόΓξσΞ€ί{ο= δνσ³Ο>Ϋ 9μάΉ³Νš5Λ-Ώι¦›μ™gžqӁΫoΏ=Ή’NλqΡκΣ§]|ρEφδ'ύ%f[Άl±ξέ»»ΦUΰκ^ § ώ»ξ²Š+ΊJ_έJ‹.ΊΠƎλΟ™½φΪkvι₯—ψsf›6mrσeΛ–΅¦M›Ϊc=fϊӟόΘειm[―^½\·o_WώϋοΏϋ%‡=χάsΦ»wo.²† Ί²΄€Άδγ?ΆC‡ΩΈqγlνΪ΅E*œZ†n{φξρ—@Ασ裏Ί“OΥ)’{₯i<:ΰQ7Ÿ²%Ρϊ†npgΗͺLΊ.ΟuΧυ²Z΅jYΛV-]žE8Uš>ψ «0›7oξΊεκ6xηwό9s9ωΛ_όΉ€JOσzξsڟcC‡΅Ž;ϊ₯‘ΛΣΫ6U– &ξΎϋnWΎkΧ.6lXž8cW³α‹/ΎhλΦ―³J•*%BmΫΆ=’{F―oΤ¨Qξ=‘΄—:œk ^θ*qιΩ³§ JΤ{ηwΊ–p£GΆκΥ«Ϋ_|aνΪ΅sŸ‘ΰ±+V¬tΧ•lίΎέV\εΟ™]uΥU^°²Λo ”»σλ―Ώϊ₯‘ΛΣΫ6-{όρΗ­f͚΅³dΙ’~I’] •¨N:ώ’τ-]ΊΤu%Ÿwήyώ’Τ₯ά’χθΖot]‹-ά> §mTΰΧΣΏφ—@ΑσωηŸΫ<ΰΟ%QŠΓηŸ}ξΟeŸl}¦ŠyΙ’%vπΰA7―3ν₯ΛΈ$Υ>·φq-Iαƌγ’KΥ‚pΦYgYΏ~ύ’«$ݝ;wΊiQͺΚ, –α«Β}οέχ짟ζ»nŒ@€ςτΆM€R‘*t%uι ™ͺΤƒγXR.V“&M,.6Ξνo΅ΙŸόgΧz2mΪ47―JZQ΄u%­ΗeD©R₯άϋ³ΐ~·/΅o.Έΰ·Ώ”ŒΌ`Α—4P‹Œ’yu)(UΰΝU‘Χρ·ΏύΝκΦ­λZšΒβHεΡl›Z‘δ* O™μΏΖœιΉΣ£ ]-DzŽ]»ΪΣΝπύ'ϊ<λD@9G’V!ε%))?pΒ 'Έΐ\ί(¨”•ƒ΅²-(:ρΔέA_y‘£Š«K—.ΆaΓΧ€άŠπΎΞφΥ*Σ¨Q#0`€«45b'Uz:“ΏνΆΫ\‚«²Μ―Ός*Ώ4ry4Ϋvα…Ί΅*TΘ%½ζ…‘Tϋί¬uλΦΦ¬Y3—Γδ hΤ’£ϋίn^eαέ?i=.#V¬XαZ.X•τ‘C\₯€ΆM›6.` „_Q΄\Ήrξ}PΛL$zODοKj"•G³mUͺTρ§Ž¦χ["%8λk__Ώήu— 9–šπύ'Κ!R τόσΟ»–,%fλϋ£€6œΆ£pαΒώ₯HεΡl[εΚ•ύ©ΌCέCjρQ׏φSΚξͺλ―Ώή΅^¨υA-k_|±[ιqΡΨΏΏkΩ»δ’€Ό΅’ΙΐmƌΙ7΅B₯FέyκT7^ h•ԍλZwzl RyF·-₯κ5’ΏlΩ2wŸΜΚΣJ/ΐ €άκBSΧΩuΧ]ηZ›tSšZœΒ»ΠφνΫη>― ¦  κ΅‹λΑ §y-ΟnΩι –W!_ΰw1©[LΤηχ駟&ίΤZ‘ ιΧMΧ„WšΫΆmσ§Μ%ΦͺRTλN ΌRTžΡm;Φ ϋΝƒAΕ―nu©k0ωdrˌ~θVω8"ϊ? TΡλ:>]ΌJ_”ό¬΄Δ}‰.ΙZj X±b^pΠΕξ½χ^—€¬;εC―'Ry4Ϋ–žβŊ»dτπ% ή‡ŒHkισ¦ίΓΣe)ΤZάΈͺ Z-’ΐLϋ-Ϊ€ŽG:ɝ8q’=ϋμ³n^χšΧςμ–₯ θ?ώ°yσζΩΥW_νζƒάεA(G΄Μ¨b…μί~ϋ­ΛRπ£³hόU±KύϊυlφμΩβP’υˆ#άrΡΕμ”/σΘ#Ψ»οΎλ*?uέ‘Κ£ΩΆ”Τυ‘Κ*·©«O­X’. ΅ώ( P7Ψ9ηœγ^S°οεϊ^½\δ©H4KI‰εκ«Υθ,XΊδΒόŸ~²R'žθ―anτ˜΅b(Ρύœ°–ΎςεΚΉmΧE+υ|z/‚.&ŒSζ‰%Ot9e)θFŽe§Ÿ~ΊKWΐ­ό‘π‹F*΄m‘όύοwVΠ…ώ>D#ώ9r„Kδ44(0 ΏtdD3dΘW?λ@©Q/…r•3+ΓAQF©RR%~Ωm!Κ)*βUš›6n΄J•+[ΚΞ ΅ ©RS2j$Ή£JQέKMZε©m[NPW^΄­Z%ΤέR»v-λΩσ—T€HΏ΄`P@’χK­&_~ω₯»θ‘Ξ ‚Λ*D*ΟzΤ’Ψ΄ιΗδ}ΠλQy˜ ˆLtTžffεxP”4bGyFϊ-*]cH£T1-M‘Κs›’Έ5<^ODdκZΣϋ¦ύ¦€w¬SX R9 ΰQώ°rIΥ;”Yω2(Rσ˜†0λΕ«N£z”cˆTžΫΤ]§V―΄†‘€¬Q.²xTοgVΎ Šς%Œ«UCΧ› ²NŒTΗ*νFulψ%2Š (hοNHpΧPR·žς˜rβ’S$<€ACH€A5%βγΚQΞ‚’\€}`ώ#~ dž.3sB‘"Y †Ežlϋ™€όŒ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πx2ό+ω›6mς§ςΆJ•*ωS‘e8(8Ρ}ΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E~%?ŸΡ›΅/1Ρvοήm{φμ±Do€‚*..Ί/n%J”°Xo:Ζ_žEωŒ‚ ­[·ZLLŒtIVΊtiΏ€‚gǎΆnέ: …BVΎ|y$eέgωŒZˆzκ©D€Ou‘κDՍͺ#³‚ (ŸΡ^₯jUˆκF‚’fοή½VΆLξΨ;pΰ€k²<ž―)·^C~ΨWΗγgTŽΧΧd–κFΥ‘YAP”Οόσ4|πύϊλ―φα‡ΊΟχ„Ώδ0kFνΟ₯νυΧ_wω @4&Ožl;wvχiΙlΘTP€s“Ο>ϋΜ†jχάs=όσφΡGΩ￞΄rLfΟ /½τw­^½Ί-\ΈΠ^zι%ΏδΨY»v­­Z΅ΚŸ;’Œ[nΉΕ^~ωeΙa©=.½ηΚ χίQ9^ων5ΰhΡΌ'ΉρΎεφgcΡ’EvΝ5ΧuΌων·ίμͺ«²ΨΨX4hϋ|Ώχξ»~i’={χΨ•W^i½{χφ—€­eΛ–Φ£Gw€GAϊΰΑƒέ#έ§΄g΅υ4ΓAΡΆmΫμ²K/΅Ώύνon$TΓ† mσζΝn#ηΝ›η―•7­_ΏήΦ¬YγΟK—.΅ >±ργΗΫέwίm―½φš½›β@v,όυ―}]@’―ΎϊͺΥ«WΟώυ―υ!Oνqi=Wnxύχνλ―ΏΆk―½Φ_’^RΝ{’ο[n6©…s€Iώ’$oΏύΆ΅nέΪjΤ¨αζΛ”)cγƍsӁρo… φηwΚ)§Ψi§fύϋχχ—Gϋζ›o\~ΡEΩώσw―y-ΟnŠόq[᝱L›6Ν~ψa»ι¦›μ‰'ž°™3gZΫ³ΟφΧΚ›Τͺ5bΔΰ(Z΄¨»W“vjtφΧ«W/«X±’U\ΩΏώzΫΎ}»_šTχYzΙ’%­iΣ¦~Ι‘vώώ»΅oίήήyη‰Ω–-[¬{χξEΐ)S¦Έε ’'OώΒυλΧw-?ώΨ:δΆ:CV«d ΅Η₯υ\j©9ΫϋLjˆ¦š\g͚ε–K4―IŸk΅¬uΦYG<6₯ηž{βΌώz_DšΞ]:»2=V'=ιQΐwΧ]wΉΟ‚@u«Θ 7ά`>ψ ›όύοw-Μ©Iοζ΅Οh ­u"moJι­i;2σλύ󟴾}ϋΊς”­όϊΌ]rΙ%φo/ §ξπΰ_-B_|ρ…;α 耫gϞώ\’τή[΅6 >ά΅0©yτΡG­C‡ξ8#Ί?χάsνяΊωl₯λE{[Ύ|ΉNwC^%jyp[Ίtiθͺ« •+W.T‘B…P=άcƒr•=ωδ“‘x T΅jΥПώτ§ΠδΙ“£.ε—_B]ta¨T©R‘Ίυκ†Όƒpr™ώχ_ώς—P₯J•BŊ vϊinωC=δζέcκΦ ½ρΖΙλGΪVο ςπV­rΫεU‚Ιλδζνλ―Ώφꠌ»ώϋέ{ηlCήΑ_šΔ«lC;v -\ΈΠέΌ_¨[·n~i(Τ©S§PνΪ΅C&L%&&†~ώωgΏ$:ηœsBήΦ-ΧγΌƒ¬_’D½ωζ›C;wξ ½όςˑƍ»ε»vνrWλ{ΘΠ‰Έεrώωη‡Ό3G7έ’E‹w`wΣ’Ϊγzχί?4wξ\7οωΊχ2ι5ισΣ―_ΏΠ‚ B—_~yθ²Λ.σKτ»χΏ΅_SΎ/yύ5€υΎH›6mάw\V¬Xαž;-ϊ?ϊŽ>σΜ3‘ωση‡nΌρΖP™2eB ygtξ;σΗI―e‡χΏτό駟ά|Jιύ_ν›Ότ €΅N€νM)½υΣϊYyυXσtœΥρΤ ΰύ’ΓΌΰ+ς‚37―υ4―χQt\3fL¨Z‘aΓ†ΉeλΦ­sοΏw澁τήΫύϋχ»u½ p€Τ>Ÿ’Ϊr‹S«;£½e((š4i’ϋπNŸ>=Υςΰ¦/t»vνBί~ϋ­»iZ_Π ΌmΫΆ.ρΞBή„ p.Όπ‚¨Λυ…φΞV\€’/cΓ† («Y³fθν·ίmάΈ14sζL·|͚5n»tΠΦ—{Γ† ny4Ϋͺƒ‡ͺί}hλΦ­‘‰'†ώϋί&―“›·ΜEςΑΈƒ¨*±ΰy‚@χϋŸ›­§eΪΏΛ–-sΣΟ>ϋ¬_z$νοGyΔ’\pΎ« ΑcǏοτ³gΟvσΪχ’χυoϋ››θj_ύΥ͏92TΈpαΠ¦M›άΌ€φΈΤ–…σΞDέηB’yM Ž£G|ςΙώά‘TωκΉτY δυΧι}Ρw kΧ‘•+WΊωτθάwί}ώ\Θ}?τ\ϊήξή³;7nœ+{α…\ε˜–΄ώo^ϋŒ¦”rHΫ›R4λ§ΆYyυX_Σ£`EΗa}ξδα‡ύωŠ+ά΄(({«“Δ&MšΈeC‡ υιΣ'4eΚχ‘>S΅jΥrί «²e¨ϋLWRυ#§Εϋ»fZο‹αšzuSσ¨²ΕՍπΎ$ζ₯Έfέͻ؏?ΞρK’€Uξ}©\Χ]—.]Μϋ‚[σζΝ]R w•\¦ οΜΚ%6hΠΐ=N—ΦU.Υ•\ρ2Ϊm½πΒ έ¨­:uκX‘B…\r šΰσ5‡k_5mz†kzLHHpϋLZ΄hαξ%˜V™φ‘xQwŸu ;ΦxΰA·Αc‡ bή«yg΄ζUŠ.Η)-ΚΏ)Uͺ”ΛŸΈγŽ;\·¬F¨Ω>£ΌJΖε?4kΦΜΌnr^O4―IŸ…€>+κ KMπzΓEσϊkˆτΎh;:θ>οΚυπΞκές΄„_=Φ;0―"5οŠ+ξrS΄?D η·ήz«›NMZ7―}F#‰΄½)etύp™}₯J•*ώTκN8α»ξΊλ’ίΏ1cΖΨ΅½zΉιp^°ιςI½ΰΛε{θ1)EϊLιϋm žΰΈ—RZΛ³"CA‘ϊ‚eυκΥξ>5AYxŸ~0ώΈΊuλϊSζ‚—”/.­ς QZύα TΌ³TkΥͺ•yg[Ιeκ[F΄Ϋͺ>ώγ…Ϊ—_nϋφνsΙΑΑQ΄ΐ‚ ά}ΥͺU“ίσοΎϋΞέ§ζΌσΞsοΕε—_nήͺΏτπηeΰ€6cƌδ[ΫΆmές”t@Χ_T•ΐ―›w6jήfTΓ{ΓiΘ°ΆG9p?ώψ£ NΡΌ¦hU―‘τ\ΑλΞ―!ϋ’$ΪIŸO²/Ώόε±dd»uβ€\εφˆ*d在RTήΙUW]ε–§&­›—>£Ρˆ΄½)etύhdΧλRn“\δ»vνrϋ1%}ΖuάΤ Žύϋχ» >₯τ>S:ιΨ]³fM p€.]»ΨΣO?νΟ%ΡΌ–g· E΅kΧv•κ[o½ε/9Z@,^ΌΨέ‹Z'$;‚‹“O>Ωέk΄‡ƒ›ΎˆAΩμΩ³έ}$9½­y…^“ZΠtΐUR:+ΣY¦Zš5ofώχΏέυFt͝κLU©Ξμ:tθ`ΒμΧ_~ωε¨ VΙΖj½S Ο#zlǎ]‹_pζͺ± kR·n=›3ηp ‘ήGA>υΤS.4Έ)(X²d‰ β$εγ$ε2UΜ’Χ νΡk‡νkІZCτšΐH~x ‘ή—ιΣ§»3w΅Φκ§ΥJPe«νzͺμtŒθ⁒ΟΡi§ŸζZoUΑ†·*₯”ΦΝKŸΡΤ€\'φ¦ΝϊΡlGΈH―+Zϊιm‡ZšzτθaEŠρKŽ€δλ‰'¦ΪJ$ι}¦€κ3“•@Η7χϊ|=ϋμ³n^χšΧςμ–‘ H΄‡zȝυ©)\]’Έš§υαΦ—±I“&φΖΈq{fηΝΩZg΅Ό *«jΥͺεΎXΪ)AːΎμϊ_*ΣKζΉsηΊ2mSπTP7ώ|7-™έΦ+VΈ .ΏPΡ­[7+[Ά¬Υ«_ύτPSΈφ‡~+ζ­7ίr―§ͺwΖͺ3SDί|σMΡζφ‰ΊEtpΤsθ`»o>Ώτ0ΘO=΅‘‹ήƒΚ[-V¬˜; ΤώΦύœte7ήx£­Y»ΖΥjθοΘ‘#܈%΅ †SK‹ή§ΰμ2εγR[¦‚‹/ΎΨuΏͺλI]+κ6Ήϊκ«έϊΡΎ¦hhDΥ‹/Ύθϊωε5€ϋΎάt£λbΦχ©B…ς΅'=ε½mPkm΅jΥlΨ°aξr%γγύR³ή7χv­ιuIZ7/}FS“rhΆ7\4λG³)₯χΊ2B[ΙrπͺλLι©Iο3υΜ3ΟΈV¦”ί  c‘z…4ςR'WΊW7¬–g»Τ"έ”0ιEφn$‚υ‡ͺT©β’θ”ˆ¬rέ{…K²Τhο@ϊα‡’ο.ndN0Κ+―ΈηˆΆ|ρβΕ‘N;ι4Π%žzΓΠΗμΚ.\θ―2|ρ0ΙIΥή­‘’eA’tF·U7%(*ρ1|Ynέ‚ιŒςC7nύϊυώ’£)I6i’šίΌŸ^yz4‚&<9œF¬hϋ2*΅Η₯\¦ΧΜοή½ΫέΒeε5…ΣwB#°2κXΏ†΄ή—ί½εΊE’‘M‰ώH"mwjžzκ)χ=ŠF€›—?£©­i{SŠ΄~4Ϋ‘Rz―+7₯φήj ŒFUΡπNά@(έ§%«‰Φ1ϊγ’£LPŽ€ZXκΥ―o1ώ²pήs»n%ζύπ›Z‰”Ψ™’w`qgΥ©ύo5λz α —9½­ΩE]a9#Λή{ο=»βŠ+ό9ˆZΟΤ: n%]αtΖ―V»ΈΨ΄»UŒΠ5±”η–YY Аϋ”P€E5Ey,(±φ β£Άnέ꺞•’YEωŒ.= Φ9]Š$Ρ@‹ψψxΧ”YJ΄Ζ±§ξ=nπγ`t•κBΥ‰ͺ³šCKQ>£7kwB‚ϋ±FεA)§*”‰‘δδw½YΌxq7κ΄R₯JV">>ΥηhεSzΣμίΔυ>(h4hκ„"E² Š<δxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E~ϋ,ΩΈq£?SΉre*zEωΘή½{­jΥͺώHΝϊυλ­X±bώ\τθ>πxŠŽ#ϋχοχ§ςŽX(D- οΛPNΡϋοΏo»wοφηŽvΕWXρβΕύ9d·τrŠτήάtΣMΆcΗIήΠΎ}{w0`€Ώ€œ•+9EσζΝ³~ψΑέ^yε{όρΗ“ηuΫ·oŸΏfώ₯ΉfΝ.Z»v­­Z΅ΚŸΛŸŽ‡ΧΘ?2=φΨcφόσΟ»ΫΉηžk΅jΥJžΧ­tιώšωΧ=χάc#FŒπης―Ώώ΅―{Oς³γα5ςlΟ)κΫ·―kEΊϋξ»]Π΄kΧ.9r€ΑωV§N»μ²ΛμΗτΧNZ_εΓ† ³Ζ[Χ](Ÿ5k–{ŒžKε[ΆlρK’«ζΑ΄zυκYσζΝν“O>ρKΝΆmΫζΦQYƒ μΆΫn;’{)εΆώγ°iΣ¦ΩΈqγ¬eΛ–6qβD·žΆ­ώn:―ΨΊu«]zι%VΆlYkΪ΄©}ϋν·~‰ΩΰΑƒmςδ/μΥW_΅ϊυλΫG}δ—€ν›oΎ±Ξ]:»ΐΆzυκΆyσfΏΔμ·ί~³^½zYŊέuΏώzΫΎ}»_šT»“N:ΙJ–,ιΆ'5;έu₯½σΞ;ώsοgχξέέ=ε”SlΚ”)nyf^Y‘νAΡΊuλμ駟vλδΙ“­D‰V₯J:d¨-\ΈΠ*V¨`/½τ’ΏφαυUΙΏχή{^₯[ɞ{ξYΏΤμα‡vς+\ΐSͺT)Ώ$ι±jI8ωδ“νΓ?΄³Ξ:Λϊυλgtε½{χΆ 6Ψ„ \₯ͺυoΉεW&)·υφΫo·Φ­[»JZ«C‡n½vνΪY·nέάt^qν΅ΧΪ$ڜ9sμγ?ΆΕ‹ω%fwάq‡΅mΫΦzφμi3fΜ°.]»ψ%i»χή{νΒ .tAγW_}uD«_=\·’F-«W―ΆkΉΖ/5»ϊκ«mκΤ©6jΤ( )¨LI]«—]z©5jΤΘΌςJ©ΉmT`§ηΌσΞ;έΆKf^Y‘#£Ο.ΌπBWΑ©e¨P‘BvΡEΉVžΈΈ8k}ζ™φCXK(θxςΙ']KAηΞ]μΗηψ%fEбI“&ΩΪ5k\πλ—$Ήκͺ«\‹*[%σͺC-MΚEQή§OΧ ›"?ΚU „o«Z9΄E‹΅ςεΛ»iQ«‘Z8ς ε<©KC5\ ΝυΧίΰ—šΕΗΗ{―!Ξ%™Uπ‚ΠΈΨ€Χ‘žN8Α>ύτS·ίjΦ¬™ΌŸWxΑ¨φΏφQΓ† έMΣZWΜςεΛέ>U0zώωη»Ηι½§ΡgjI*^Ό˜½ψβ‹ώRK~μ\ΰΤV­ZΩ‚ ά•»3σȊ ŠR^Z[h§Ξ\ΛΛλ―Ώnvθ_’€nέΊώ”ΉJ5|χπαΓ½ΥY³fΝ\Π“˜˜θ—$ Q‹CΉrε\P€ [Β»r‚ι L2sπcMΑ„€ΥM•z_:θ‚CuϋyεΚ•ξΎE‹ξ^‚i•‰ΠηœsŽ»OΝgŸ}fcǎ΅xΠɁΰ±C†±›oΎΩVmΪ΄±₯K—Ίεδ¦ ŠΒ-[ΆΜε£<τΰCεF]ZQ­Z5?~ΌλS·Μ˜1cό’£©λF7 [‚Ε‹»{Y΄(©‹)?Bα”%Aΐ’Τβ4ισIφε—_Ί<žΡ£G»εκϊu}Ԛ#ΪΟj½“οΎϋΞέ§ζΌσΞs­K—_~Ήϋ<‚Η0Πu‘7u›Ϋr<(R"JdVςςU~ύw·,3gΞt<³M«Q³†k5 §γ;wΊ<"LeΚ”q-Rjρh€‰½αR n΅V¨u₯VνΪώ£VΫ+›?Ύ?—D]HK–,ρηŽ=u͚7s­hκ.Τεty„puλΦsωFњ>}ΊΫΟκΚTΛ]°Ÿ€ιύϋίΆ]ή~TΞΡΏώυ/ΧZ€υ΄Ÿ΅Ώ•'6{φlχ˜_~ωε¨ 6>χάsΦ₯KλάΉsςΫκ±;vt]§A«Ρž={ά‘ŒΎ²"Ηƒ"εγtλvžΛQ‹’‘Υ…’“h¨KE£ΗΞπ‚™ ε+ΈV§pκnSE{ϊι§»ŠYΆςQτ?ΤΪ±tΩ2—γ’›r‰” γ?65J VR±r ”<,α6xπ 7W<ώΨγM]†7άpƒλ~ wγ7ښ΅IyXΚ ŠδΖ›ntyT ^+T(Ÿό|111φΦ›oΉ °j•*uHΜ›oΎιΚEΑ¦Ί-(i{θμΫτ5«τޜzj#—4ŒΤc•7€<&IΊŸγηœeτ51Ήυ+ω―M VΤ Ρ^ύZ-=’`'άΕ_사Ώχοo›6n΄J•+§πθ.\ψˆ‘k‘¨5CCΠΓs`Ž΅”W΄VˎZβ*Uͺδ/9š»ηž{lĈώ²Γ_Ϊמώy€”cAΡΆmΫ¬oίΎV―^=kΠ έvΫmΆcΗΏΤlΦ¬YvΩe—Y­Z΅¬qγΖΆeΛ·ό™gž±iΣ¦ΩΈqγ¬eΛ–6qβD·|Ψ°aΦΏ7 ³^½zΩ /Ό`=τU­Z՚·hnΏώϊ«λΖ¬WΏž5jΤΘ&L˜ΰΦeB7D“'OvΉ-R€H›4i’­]³ΖͺW―n±±±ny‰%\E_΄hQ+_ΎΌ›΅΅oίήM#‰G—5kΦtAc||Όk)RΛάyηgσζΝsλiyΡ’qV¬X1«P‘‚ΕΕΖΉeΰ°LEj½QΒuψ-œZ1€iΣ¦ξ^‚ι lψπαvθΠ!kΦ¬™ 0ΐέrDON P‘BV\9‹‰‰qσΑ=ˆNŽδU\Ωέ/^ΌΨέΛ’E‹ά}PV­Z5?~ΌΙ¦ό‘1cΖΈεΗBŽEuκΤ±&MšΨ^°“`;wξtέ:j-ͺεΙΜ™3νΐvf›6V£f ΧjΠο•€μ’Σ5–,YβΟ!£κΦ­—m—Lΰx”#A‘ΊrFmK—-s£ tS.Ρ¨Q£,θΤιΧ―Ÿ™v†(U(_Α¦ \sΝ5ξZE•6uκT·LCτ䦑q7ήx£­Y›”Ώ₯\.p€•| Ν/\Έpςθ²pjE’΄’~7nάh+VtA²ηWςlV©R…œ#ΐq+ΟώJ~Ω²eS ˆDΑPz£ ”D@”½tM#"ŽFΔΰ!(πδxN²ΟάΉsύ)ž3Ξ8ߊA€‡ξ3A€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐΓoŸε#ό ,ΡαasΛ—/·ζΝ›ϋs 5³gΟΆΪ΅kϋsΡ£ϋ ΐCPΰ!(πd8§θ“O>±­[·ZLLŒ•.]ΪN?ύt«Q£†_Šœ”2§hώύ6kΦ,›:uͺυλΧΟN<ρDΏΔlΛ–-6}ϊtϋζ›oμ”SN±Λ.»ΜΚ•+η—&Y΄h‘½ρΖξy.Ήδ;σΜ3ύ’$Y-ΰXΘ΅œ’§Ÿ~ΪFŽiS¦L±GyĚ6mj&LπK3nύϊυΆfΝΡΪ»w―UͺTΙnΏύv{葇ά|Έ;Ό ιυΧ_³zυκΩ΄iΣάΊλΦ­σKΝf̘agŸ}Ά%&&ΊΗvμΨΡή}χ]Ώ4λεδ7n)RεΧ];{τΡGέό₯—^j… ΆχίίΝgTϞ=]4χΔOψK–”-E H–-[f§žzͺmάΈΡ>P(δZσš¨χjΐ€n^M›6mlȐ!n^Αξ?_xΑVz£P‘BY.ΰX9f£ΟΤ±dΙ7νXΦ·o_·¬AƒvΫm·ΩŽ;\™¨μ•W^±»οΎΫjΥͺeψΗ?\+ΖΈqγ¬eΛ–6qβD·ή°aΓ¬ώni‹‹‹σ§ŽIΩ²e]—Z@˜πξσΞ;ΟΦ¬ZesζΜqσY- ΏΙRP€–‹·ί~Ϋ:uκδζ{χξm6lpέi}τ‘λΉε–[\™h^- Υ«W·Ι“'»ŸΦ­[[χξέ]R‡άzj‰κΦ­››FΦ)PQwΧΥW_ν/1«X±’ΛG ( •Υ«W»ϋ¬–ίd*(ϊΰƒ¬U«VΦ’E ;χάλkΥͺU.Ο¨OŸ>VΏ~}wS@€ΰgνΪ΅ώ#Ν.ΌπB»σΞ;­N:V²dIΧΪQ΄hQ+_Ύ|rΛ‡ZΪ·o撚5 \•=jΤ(ΧεPKœrΓ^|ρEΨ8Π-Z˜²Z@~“© ¨I“ΣνΉηžs£ώσŸΧ,>>>Ή…@‰Χ`:Όυ rεΚώrΪ/Ώόβ‘'Ÿ|zυκε/M’ΜαΓ‡ΫΧ_mί~ϋ­έzλ­ny͚5έ}VΛΘo2ΥͺUΫε“„'φΑΞβΕ‹έ½(h‘ά§nLuG4Θ%³§ζΟώ³½υΦ[φμ³ΟΊ„m½Ÿ 5τK³^@~’εDλ€ΊΓš4iboŒg ΆsηN;v¬k-ͺ•NΈ²ΓηϟοΟ%Y±bErς62ηΞ;ξ°rεΛΉhLΣ-<ι=q_’?eΆmϋv3fŒέwί}›Τ…™Υrς›l Š4 {τθΡΆtΩ2kΤ¨‘»)—HΉ,ιe™\sΝ5ξZE7v!”#FΨΰΑƒά4R·gοwρLεvIέΊu]ςsΰΛ)Sμ§ω?Y•*U’oJ„ zb[œsΞ±F Z=μώοό¬—ίδΘ―δk$’]TͺT)IdjΙP%Λ5n–έΏ’―.6 ΣΧ―cccύ₯‡e΅€c!³Χ)Κ‘ 9#»ƒ"ŽGΗμβΗ‚"έgωΘάΉsύ)ύζgFxθ>πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<όφY>ΒΒ~φ8·|ωrkήΌΉ?R3{φl«]»Ά?=ΊΟ<E‚’8`ϋχοwχΩιΰΑƒξyuyO†rŠή}Ϋ½{·?w΄+ΈΒΎώϊk«T©’5iΔ_šΏ\|ρΕ/²}ϋφ6nά8iΦ 6Μ^zι%KHH°-[ΆψK3†œ""Λ•œ’yσζΩ?όΰn―ΌςŠ=ώψγΙσΊνΫ·Οήyηϋζ›oόGδ-λΧ―·5kΦψsi{ώωη³5 ’{ξΉΗΎϊκ+δ5™}φΠCΩ¬Y³μσΟ?χ—δ}={φt‘γO<α/9šZŠ»ξ:λή½»Ώ$ϋ( kΦ¬-Eδ <3ϊ¬oίΎ6jΤ(.i~δΘ‘6hΠ kΨ°‘uθΠΑ–-[ζZ”Z΄ha­[·>"°ΪΊu«”τ²Z΅jYΛV-mϊτι~‰™ΐΉη«W―ž5hΠΐn»ν6Ϋ±c‡_jΦ£GχΌ7ί|Σώς—ΏΈιgžyΖ¦M›ζZ€ZΆli'NtΛ#Ιθφ+PΌμ²Λάφ7nά8έH]jύϋχχηΐ±”νAΡΊuλ\πΠόΣO?m±±±φΩgŸYΡ’EνΚ+―΄Ι“'Ϋ[o½eνΪ΅³!C†ψk›υξέΫJ—.cσηΟ·>·φ±ϋξ»Ο/I*Ϋ°aƒM˜0Α>ϊθ#χά·άr‹_jΆjΥ*ΫΉs§?g.`Z½z΅›ΎυΦ[]£ O>ωΔ7ΡΘθφ?όπΓΦ΅kW[Ύb…ϋ?₯J•ςKަΗvλΦ͟ΗRŒ>S—Τ½χήkΥ«W·3Ο<ΣβKΖ»œ€ΊuλZǎνηŸvλ­\Ή΅ζtιΕ?κ*Z΄h‘mήΌΩ}ϊXύϊυέM‘‚“΅kΧΊΗ§§D‰η‚šςεΛ»ιhE»ύR€H›4i’­]³Ζ­―`*-j±RB78φr%(R(T¨•)]ΖέKLLŒ»— ϊŸό§έy睕¨U«V.—&hρiΪ΄©»—`:(Λ)ΡnΏ >ά:δr‡ `‰‰‰~ ΘΛr%(ŠΦΙ'ŸμξοΏ~ϋτΣO“oκφͺ\Ή²+[Όx±»΅"IP&ΊP Ό/·T«VΝƏo~ψ‘Λ_3fŒ_ς²<)9Yy6Ο>ϋlr«Ρž={ά΅}κΤ©γ}τ†hh^ΉCcǎu­E΅ό σϊυ빌s΅Ξ(ΙzĈny@™θΚUΚI3gΞt~<³M«Q³†k5Jˊ+lΙ’%ώ8–ςTP$ιU΄XQ© Jχ dΤ]5zτh[Ίl™5jΤΘέ”K€‘nAΦΝ7χv¬Q£†}ϊι'vΧ]wω%IΉζw­" ›:uͺΏ4{υλΧύŽ;Γ Φ*”―`½zυςKަ mπΰAώ8–2}’œ¦+g«•¨B… ώ’ΓΤ-VΈpαTGv©ϋL-IιϊΪΈq£U¬X19/(œ’ͺ5ΊμςΛ/·βΕ‹ϋK3F_βγγέ½¨υJΉQj γ:EδœγξWς5Z,΅€HΚ–-›fΠ“V°N9H©D’9tθP»νΆΏϊK2NΑPx@$­vΥUWΉi οΙ³-E8-EDvά΅δ&ZŠς‘ΉsηϊS =gœq†?=‚"έg‚"A€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(πxŠ<E‚"OΜΆmΫBώ4δ{: νKL΄έ»w۞={,Ρ›†Y\\œ/^άJ”(a±ήtŒΏ<+ΨΧ©c_ηžμήΧEŽ+ͺ,Άnέj111vI'Yι₯ύ’‚mǎΆnέ: …BVΎ|yW™dϋ:uμλά“έϋšξ3ǝI«β8υΤS©8Βh_hŸhίheφuκΨΧΉ'»χ5A€γŠŒUͺVυ琒φMvVΤμλ΄±―sOvνk‚"Η•½{χZΩ2eόΉΌιΐΉXΠΎΡ>Κωa_KμλθdΗχ!»φ5A€γΚΑƒύ©œρα‡Ϊwί}ηΟφκ«―Ϊ²eΛόΉτuκΤΙθΟεΎμΪG9½―Σ3~όx{ΰμή{ο΅7ί|Σφοίο—$Ρϋ4ώ|.ΙoΌa ,πηrG^άΧό±=φΨcφΧΏώΥ†jύοmηοΏϋ₯Ή/»ΎΩ±ŠWrΊfΠ Aφή{οωs‡έ|σΝφύχίϋsy[vν£cΡΪ΅}ϋvλάΉ³υοίߊ+fεΚ•s{›6mlνΪ΅ώZIοΣΔ‰ύ9³!C†ΨΰΑƒ­f͚ώ’ά‘—φυoΏύζ[o½ΥώψγkάΈ±mάΈΡ~ψaϋqφl­ό+;φA ίPώλ―ΏΪχ?όΰ¦ο»ο>ϋφΫo]YΏ~ύά}J&L°§žzΚώχΏY||ΌΏ΄ΰQ˚Z3ηΝλD΅=σΜ3φσΟ?[‡ό΅ 6‚"Θ[Άl±ξέ»»Ρ1§œrŠM™2Ε/9Z―^½μω矷»ξΊΛ*V¬hυκΥsέ?8’†_ΏςΚ+vρΕ[©Oτ—&]«ζŠ+°>ψΐ–,Yβ/M²xρb»φΪkνwή±Ϊ΅kϋK ΅°9F*Tπ—ν…^°³Ο>Ϋ oW‹ά¬Y³ό’€Ο©ΚŸxβ «^½ΊuΦYG”«%ꦛnr— (Y²€5mΪΤ/ΙΨχαX"(€ š;o«`Βo)υμΩΣΚ–-k«W―Ά;οΌΣξΈγΏδhZGέ;ͺhΎψβ kםέpΓ vθΠ! ˆεtιΕ_rXΧ]έ½Z= .Ίψ"—―rξΉηϊK ¦_~ωΕέ·jΥΚέ§EΝ‹/ΎhλΦ―³J•*Ή–€€>§κ–άΌy³}ϊι§VΉre{ςΙ'ύR³«―ΎΪ¦Nj£FrΈqγό’Œ}Ž%‚"Θ -›·Ψœ9sŽΈ…[Ύ|ΉMž<Ω.Έΰwa9UDJπUώFZt–’ΣN;ΝεΘ¨BO-‘» Skƒ¨%-%εΗH°ŽΜœ9Σ–ώΊ4Υυ š`Ώ(0IΟε—_nMš4±ΈΨ8kΫΆνQŸA—jΥΤ΅Ξ?όδ<Ίΰ3―.L-΅FQ–‘οΓ±BP€n…αΓ‡q ·jΥ*w?dθ—€­ŠB‰ΐK—.uΛS~%^%«‹#Ώ$nη– ϋλ«―Ύrχα‚ 6Ό‹L•³Z2TΡψγώ‚)H0_Ήr₯»O‹ςZ·nm͚5s- )“—λΧ―οO%}fƒΦΜΰ3Ξ9ηΈϋp™ω>+EΝΤ & ΄3f$ίtζ ύœƒΞμΥ•ΓΤβS₯J›τωηώ’Γ”ƒ₯ΡhͺΜΓi”šrŠΞ;ο<[±b…Ώ΄ΰ©WΏž•)SΖ^ύuΙΡ”ΐςρΗwAdFΊΈ‚Ο|j­›Yύ>δ&‚"ΘfuκΤ±Ž;ΊVŠΰ,Y?βΉ+!ΑM§F•„‰uζ=zτhWuρσd€H‘".χκγ μ³Ο>σ—&Uď>ϊ¨¦VΆ”^zι%kήΌΉΛ;RKA€ξ08SΎς„‚΅ιΊEΚ9 ΊΨκΦ­λZή”ί¦Οd4τ™Χ6νλΩώπ~=§ž?3ί‡c… rΐΨ±c]Λ…Ί-T)θ~N:]8qq±.Χ’Z΅j6lΨ0{χέw­d>ž΅ϊ¨[§GV«V-Χz€kο<ςΘ#vύχϋkι„N°wΌύ©_SΏπ’ ³ν*ΣωMŸ>}άEF5zLŸΝ ΨΙ'Ÿl#FΌβΚΥ₯₯‘}¦7u…*TΘ%PGCŸy₯-Z΄pΉK „φνί—\–‘οΓ±Γ―δ8ž,\ΈΠ Ξ+Ό³aύ&“Fς€₯}ϋφξ6`ΐ[Ώ~½UΝαίΈR«T›Ηz_λš;AK„~4/Κ«ϋZΊά΅k—5lΨΠ_r˜ZΣΤhŸͺEGPFkΫφνVΘ{lj?\Νχ!³²c_ΣRΰΈ’Χ*G],0#@ND’]ϋθXοkCκκΙ«‘δΥ}­’Τ"QkOπ e$ ύYZΏδŸΡοCFdΗ>"(p\ш%*η'Χ]w]ͺ£vr‚φMψH·¬Θϋ:7±―sOvνk‚"ǝ‰ɜω….Ԙ[?³ }“]?u‘χunb_ηžμΪΧEŽ+₯J•²ΔΔD7")όB~φ…φ‰φφQv`_§Ž}{²{_“h ΰΈ’Ϊξ„Ϋ΄i“yΗ7—(šΏžŸ)ΧBy!JžU>G οŒ:;2TΨΧGc_瞜ΨΧEŽK:°ΨΏŸίσihυ EŠdKϋϊHμλά“έϋš ΐCN€‡ ΐCPΰ!(πxŠ<E‚"A€‡ ΐCPΰ!(π˜Ω*ΌZœώΈ²:IENDB`‚jpype-1.6.0/doc/boilerplate.txt000066400000000000000000000037661501674766700165140ustar00rootroot00000000000000Today, we are working on modifying the JPype documentation. The document I have provided is in reStructuredText (RST) format. All responses must adhere to the following guidelines: 1. **Line Length**: - All responses must maintain an 80-character line length limit. 2. **Formatting**: - Match the existing format in terms of anchors, linkage, and overall structure. - Header levels must follow the established hierarchy: - ****** Chapter Level - ====== Header Level - ------ Subheader Level - ~~~~~~ Subheader Level 3. **Content Style**: - When revisions are presented, they must match the header levels and formatting of the existing text. - Use descriptive text instead of bullet lists to ensure the document reads as a cohesive narrative rather than an outline. 4. **Sphinx Compatibility**: - The resulting RST file must conform to Sphinx's interpretation of RST. - Ensure proper use of directives, roles, and syntax supported by Sphinx to avoid processing errors. - Test the RST file with Sphinx to verify compatibility and correctness before finalizing revisions. 5. **Avoid Speculative Material**: - Ensure all revisions are based on verified information, authoritative sources, or established best practices. - Avoid unverified claims, ambiguous statements, or unsupported examples. - Do not include predictions or assumptions about future behavior unless explicitly supported by authoritative sources or documentation. - Provide sufficient context and explanation for all claims, examples, and workflows to ensure clarity and reliability. By following these guidelines, all revisions will align with the existing style and structure of the JPype documentation. Typical prompts: Please review the document for consistency of style between the different sections. Identify sections that no longer fit the description of a narrative (excluding sections in which a narrative would not be expected such as glossary) jpype-1.6.0/doc/caller_sensitive.rst000066400000000000000000000101331501674766700175200ustar00rootroot00000000000000:orphan: Caller Sensitive Methods ======================== The following methods use the caller sensitive (as of JDK 12): - ``java.io.ObjectStreamClass.forClass`` - ``java.io.ObjectStreamField.getType`` - ``java.lang.Class.forName`` - ``java.lang.Class.newInstance`` - ``java.lang.Class.getClassLoader`` - ``java.lang.Class.getEnclosingMethod`` - ``java.lang.Class.getEnclosingConstructor`` - ``java.lang.Class.getDeclaringClass`` - ``java.lang.Class.getEnclosingClass`` - ``java.lang.Class.getClasses`` - ``java.lang.Class.getFields`` - ``java.lang.Class.getMethods`` - ``java.lang.Class.getConstructor`` - ``java.lang.Class.getConstructors`` - ``java.lang.Class.getField`` - ``java.lang.Class.getMethod`` - ``java.lang.Class.getDeclaredClasses`` - ``java.lang.Class.getDeclaredField`` - ``java.lang.Class.getDeclaredFields`` - ``java.lang.Class.getDeclaredMethod`` - ``java.lang.Class.getDeclaredMethods`` - ``java.lang.Class.getDeclaredConstructor`` - ``java.lang.Class.getDeclaredConstructors`` - ``java.lang.Class.getResource`` - ``java.lang.Class.getResourceAsStream`` - ``java.lang.Class.getNestHost`` - ``java.lang.Class.getNestMembers`` - ``java.lang.ClassLoader.getParent`` - ``java.lang.ClassLoader.getPlatformClassLoader`` - ``java.lang.invoke,MethodHandleProxies.asInterfaceInstance`` - ``java.lang.invoke.MethodHandles.lookup`` - ``java.lang.Module.addReads`` - ``java.lang.Module.addExports`` - ``java.lang.Module.addOpens`` - ``java.lang.Module.addUses`` - ``java.lang.Module.getResourceAsStream`` - ``java.lang.Package.getPackage`` - ``java.lang.Package.getPackages`` - ``java.lang.reflect.AccessibleObject.setAccessible`` - ``java.lang.reflect.AccessibleObject.setAccessible`` - ``java.lang.reflect.AccessibleObject.trySetAccessible`` - ``java.lang.reflect.AccessibleObject.canAccess`` - ``java.lang.reflect.Constructor.setAccessible`` - ``java.lang.reflect.Constructor.newInstance`` - ``java.lang.reflect.Field.setAccessible`` - ``java.lang.reflect.Field.get`` - ``java.lang.reflect.Field.getBoolean`` - ``java.lang.reflect.Field.getByte`` - ``java.lang.reflect.Field.getChar`` - ``java.lang.reflect.Field.getShort`` - ``java.lang.reflect.Field.getInt`` - ``java.lang.reflect.Field.getLong`` - ``java.lang.reflect.Field.getFloat`` - ``java.lang.reflect.Field.getDouble`` - ``java.lang.reflect.Field.set`` - ``java.lang.reflect.Field.setBoolean`` - ``java.lang.reflect.Field.setByte`` - ``java.lang.reflect.Field.setChar`` - ``java.lang.reflect.Field.setShort`` - ``java.lang.reflect.Field.setInt`` - ``java.lang.reflect.Field.setLong`` - ``java.lang.reflect.Field.setFloat`` - ``java.lang.reflect.Field.setDouble`` - ``java.lang.reflect.Method.setAccessible`` - ``java.lang.reflect.Method.invoke`` - ``java.lang.reflect.Proxy.getProxyClass`` - ``java.lang.reflect.Proxy.newProxyInstance`` - ``java.lang.reflect.Proxy.getInvocationHandler`` - ``java.lang.Runtime.load`` - ``java.lang.Runtime.loadLibrary`` - ``java.lang.StackWalker.walk`` - ``java.lang.StackWalker.forEach`` - ``java.lang.StackWalker.getCallerClass`` - ``java.lang.System.getLogger`` - ``java.lang.System.getLogger`` - ``java.lang.System.load`` - ``java.lang.System.loadLibrary`` - ``java.lang.Thread.getContextClassLoader`` - ``java.security.AccessController.doPrivileged`` - ``java.security.AccessController.doPrivilegedWithCombiner`` - ``java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater`` - ``java.util.concurrent.atomic.AtomicLongFieldUpdater.newUpdater`` - ``java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater`` - ``java.util.ResourceBundle.getBundle`` - ``java.util.ResourceBundle.clearCache`` - ``java.util.ServiceLoader.load`` - ``java.util.ServiceLoader.loadInstalled`` - ``java.util.logging.Logger.getLogger`` - ``java.util.logging.Logger.getLogger`` - ``java.util.logging.Logger.getAnonymousLogger`` - ``java.sql.DriverManager.getConnection`` - ``java.sql.DriverManager.getDriver`` - ``java.sql.DriverManager.deregisterDriver`` - ``java.sql.DriverManager.getDrivers`` jpype-1.6.0/doc/conf.py000066400000000000000000000257771501674766700147560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # JPype documentation build configuration file, created by # sphinx-quickstart on Wed Feb 26 20:16:40 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from unittest import mock import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.napoleon', 'sphinx.ext.autodoc', 'sphinx.ext.autosectionlabel', 'readthedocs_ext.readthedocs', ] autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'JPype' copyright = u'2014-18, Steve Menard, Luis Nell and others' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. class ObjectMock(object): def __init__(self, *args, **kwargs): self._kwargs = kwargs self._to = mock.Mock object.__init__(self) def __getattr__(self, key): kwargs = self._kwargs m = self._to(**kwargs) object.__setattr__(self, key, m) return m class TypeMock(type): def __new__(cls, name, bases=None, members={}, to=mock.Mock, **kwargs): if not bases: bases = tuple([]) members['__init__'] = ObjectMock.__init__ members['__getattr__'] = ObjectMock.__getattr__ members['_kwargs'] = kwargs members['_to'] = to members['__slots__'] = [] return type.__new__(cls, name, bases, members) def __init__(self, *args, **kwargs): return type.__init__(self, *args) def __getattr__(self, key): kwargs = self._kwargs m = self._to(**kwargs) type.__setattr__(self, key, m) return m class _JClass(type): pass class _JClassHints(object): def __init__(self): self.bases = [] def _addClassBases(self, *args): pass def _addTypeConversion(self, *args): pass def _addAttributeConversion(self, *args): pass def _excludeConversion(self, *args): pass class _JPackage: def __init__(self, *args, **kwargs): pass # Monkey patch (this is needed to catch errors for missing TypeMock) m = mock.Mock.__getattr__ def patch(self, key): if key == "__name__": return "FIXME TypeMock is missing" return m(self, key) mock.Mock.__getattr__ = patch mockModule = mock.MagicMock() mockModule.isStarted = mock.Mock(return_value=False) mockModule._JArray = TypeMock("_JArray") mockModule._JClass = _JClass mockModule._JField = TypeMock("_JField") mockModule._JMethod = TypeMock("_JMethod") mockModule._JObject = TypeMock("_JObject") mockModule._JProxy = TypeMock("_JProxy") mockModule._JException = TypeMock("_JException") mockModule._JPackage = _JPackage mockModule._JClassHints = _JClassHints mockModule._hasClass = lambda x: False sys.modules['_jpype'] = mockModule import jpype import jpype.imports # For some reason jpype.imports does not work if called in sphinx. Importing # it here solved the problem. version = jpype.__version__ # The full version, including alpha/beta/rc tags. release = jpype.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' # otherwise, readthedocs.org uses their theme by default, so no need to specify it # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] html_css_files = ['theme_overrides.css'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'JPypedoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'JPype.tex', u'JPype Documentation', u'Steve Menard, Luis Nell and others', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'jpype', u'JPype Documentation', [u'Steve Menard, Luis Nell and others'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'JPype', u'JPype Documentation', u'Steve Menard, Luis Nell and others', 'JPype', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False napoleon_custom_sections = ["Static Methods", "Virtual Methods", "Constructors"] jpype-1.6.0/doc/dbapi2.rst000066400000000000000000000470131501674766700153350ustar00rootroot00000000000000################## JPype DBAPI2 Guide ################## `Introduction` ============== One common use of JPype is to provide access to databases using JDBC. The JDBC API is well established, very capable, and supports most databases. JPype can be used to access JDBC both directly or through the use of the Python DBAPI2 as layed (see PEP-0249_). Unfortunately, the Python API leaves a lot of behaviors undefined. The JPype dbapi2 module provides our implementation of this Python API. Normally the Python API has to deal with two different type systems, Python and SQL. When using JDBC, we have the added complexity that Java types are used to communicate with the driver. We have introduced concepts appropriate to handle this additional complexity. `Module Interface` ================== `Constructors` -------------- Access to the database is made available through connection objects. The module provides the following constructor for connections: .. _connect: .. autofunction:: jpype.dbapi2.connect Globals ------- JPype dbapi2 defines several globals that define the module behavior. These values are constants. .. _apilevel: `apilevel`_ The apilevel for the module is "``2.0``". .. _threadsafety: `threadsafety`_ The threadsafety level is 2 meaning "Threads may share the module and connections". But the actual threading level depends on the driver implementation that JDBC is connected to. Connections for many databases are synchronized so they can be shared, but threads must execute statements in series. Connections in the module are implemented in Python and have per object resources that cannot be shared. Attempting to use a connection with a thread other than the thread that created it will raise an ``Error``. Sharing in the above context means that two threads may use a resource without wrapping it using a mutex semaphore to implement resource locking. Note that you cannot always make external resources thread safe by managing access using a mutex: the resource may rely on global variables or other external sources that are beyond your control. .. _paramstyle: `paramstyle`_ The parameter style for JPype dbapi2 module is ``qmark`` ============ ============================================================== paramstyle Meaning ============ ============================================================== ``qmark`` Question mark style, e.g. ``...WHERE name=?`` ============ ============================================================== Exceptions ---------- The dbapi2 module exposes error information using the following exceptions: .. autoclass:: jpype.dbapi2.Warning .. autoclass:: jpype.dbapi2.Error .. autoclass:: jpype.dbapi2.InterfaceError .. autoclass:: jpype.dbapi2.DatabaseError .. autoclass:: jpype.dbapi2.DataError .. autoclass:: jpype.dbapi2.OperationalError .. autoclass:: jpype.dbapi2.IntegrityError .. autoclass:: jpype.dbapi2.InternalError .. autoclass:: jpype.dbapi2.ProgrammingError .. autoclass:: jpype.dbapi2.NotSupportedError Python exceptions are more fine grain than JDBC exceptions. Wherever possible we have redirected the Java exception to the nearest Python exception. However, there are cases in which the Java exception may appear. Those exceptions inherit from `:py:class:jpype.dbapi2.Error`. This is the exception inheritance layout:: Exception |__Warning |__Error |__InterfaceError |__java.sql.SQLError | |__java.sql.BatchUpdateException | |__java.sql.RowSetWarning | |__java.sql.SerialException | |__java.sql.SQLClientInfoException | |__java.sql.SQLNonTransientException | |__java.sql.SQLRecoverableException | |__java.sql.SQLTransientException | |__java.sql.SQLWarning | |__java.sql.SyncFactoryException | |__java.sql.SyncProviderException | |__DatabaseError |__DataError |__OperationalError |__IntegrityError |__InternalError |__ProgrammingError |__NotSupportedError Type Access =========== JPype dbapi2 provides two different maps which serve to convert data between Python and SQL types. When setting parameters and fetching results, Java types are used. The connection provides two maps for converting the types of parameters. An `adapter `_ is used to translate from a Python type into a Java type when setting a parameter. Once a result is produced, a `converter `_ can be used to translate the Java type back into a Python type. There are two lookup functions that select the behavior to decide how a column or parameter should be treated. These are `getters`_ and `setters`_. .. _adapters: adapters_ --------- Whenever a Python type is passed to a statement, it must first be converted to the appropriate Java type. This can be accomplished in a few ways. The user can manually convert to the correct type by constructing a Java object or applying the JPype casting operator. Some Java types have built-in implicit conversions from the corresponding type. For all other conversions, an adapter. An adapter is defined as a type to convert from and a conversion function which takes a single argument that returns a Java object. The adapter maps are stored in the connection. The adapter map can be supplied when calling `connect`_, or added to the map later through the `adapters `_ property. .. _setters: setters_ -------- A setter transfers the Java type into a SQL parameter. There are multiple types that an individual parameter may accept. The type of setter is determined by the JDBC type. Each individual JDBC type can have its own setter. Not every database supports the same setter. There is a default setter that should work for most purposes. Setters can also be set individually using the ``types`` argument to the ``.execute*()`` methods. The setter is a function which processes the database metadata into a type. Setters can be supplied as a map to `connect`_ or by accessing the `setter `_ property on a Connection. .. autofunction:: jpype.dbapi2.SETTERS_BY_META .. autofunction:: jpype.dbapi2.SETTERS_BY_TYPE .. _converters: converters_ ----------- When a result is fetched from the database it is returned as a Java type. This Java type then has a converter applied. Converters are stored in a map holding the type as key and a converter function that takes one argument and returns the desired type. The default converter map will convert all types to Python. This can be disabled by setting the converters to ``None``. The converter map can be passed in to the `connect`_ function, or set on the Connection using the `converters `_ property. It can be supplied as a list or a map to the ``.fetch*()`` methods. .. _getters: getters_ -------- JDBA provides more than one way to access data returned from a result. In the native JDBC, each executed statement returns a result set which acts as a cursor for the statement. It is possible to access each column using a different get method. The default map will attempt to fetch according to the most general type. The getter is a configurable function that uses the metadata to find the most appropriate type. .. autofunction:: jpype.dbapi2.GETTERS_BY_TYPE .. autofunction:: jpype.dbapi2.GETTERS_BY_NAME .. _Connection: `Connection Objects`_ ===================== A Connection object can be created by using the `connect`_ function. Once a connection is established the resulting Connection contains the following. .. autoclass:: jpype.dbapi2.Connection :members: .. _Cursor: `Cursor Objects`_ ================= These objects represent a database cursor, which is used to manage the context of a fetch operation. Cursors created from the same connection are not isolated, *i.e.*, any changes done to the database by a cursor are immediately visible by the other cursors. Cursors created from different connections may or may not be isolated, depending on how the transaction support is implemented (see also the connection's `rollback `_ and `commit `_ methods). .. autoclass:: jpype.dbapi2.Cursor :members: Cursors can act as an iterator. So to get the contents of a table one could use code like: .. code-block:: python with connection.cursor() as cur: cur.execute("select * from table") for row in cur: print(row) `SQL Type Constructors` ======================= Many databases need to have the input in a particular format for binding to an operation's input parameters. For example, if an input is destined for a ``DATE`` column, then it must be bound to the database in a particular string format. Similar problems exist for "Row ID" columns or large binary items (e.g. blobs or ``RAW`` columns). This presents problems for Python since the parameters to the `.execute*()` method are untyped. When the database module sees a Python string object, it doesn't know if it should be bound as a simple `CHAR` column, as a raw `BINARY` item, or as a `DATE`. This is less of a problem in JPype dbapi2 than in a typical dbapi driver as we have strong typing backing the connection, but we are still required to supply methods to construct individual SQL types. These functions are: .. autofunction:: jpype.dbapi2.Date .. autofunction:: jpype.dbapi2.Time .. autofunction:: jpype.dbapi2.Timestamp .. autofunction:: jpype.dbapi2.DateFromTicks .. autofunction:: jpype.dbapi2.TimeFromTicks .. autofunction:: jpype.dbapi2.TimestampFromTicks .. autofunction:: jpype.dbapi2.Binary For the most part these constructors are largely redundant because adapters can provide the same functionality and Java types can be used directly to communicate type information. .. `JDBC Types` `JDBC Types`_ ============= In the Python DBAPI2, the SQL type system is normally reduced to a subset of the SQL types by mapping multiple types together. For example, ``STRING`` covers types `STRING`, `CHAR`, `NCHAR` , `NVARCHAR` , `VARCHAR`, and `OTHER`. JPype dbapi2 supports both the recommended Python types and the fine grain JDBC types. Each type is represented by an object of type JBDCType. .. autoclass:: jpype.dbapi2.JDBCType :members: The following types are defined with the correspond Python grouping, the default setter, getter, and Python type. For types that support more than one type of getter, the special getter can be applied as the converter for the type. For example, the default configuration has ``getter[BLOB] = BINARY.get``, to get the Blob type use ``getter[BLOB] = BLOB.get`` or specify it when calling `use `_. .. The link to cursor.use above appears to be broken. Does cursor.use exist? ======== ======================== =================== ============== ================= =============== Group JDBC Type Default Getter Default Setter PyTypes Special Getter ======== ======================== =================== ============== ================= =============== DATE DATE getDate setDate datetime.datetime DATETIME TIMESTAMP getTimestamp setTimestamp datetime.datetime TIME TIME getTime setTime datetime.datetime -------- ------------------------ ------------------- -------------- ----------------- --------------- DECIMAL DECIMAL getBigDecimal setBigDecimal decimal.Decimal DECIMAL NUMERIC getBigDecimal setBigDecimal decimal.Decimal -------- ------------------------ ------------------- -------------- ----------------- --------------- FLOAT FLOAT getDouble setDouble float FLOAT DOUBLE getDouble getDouble float FLOAT REAL getFloat setFloat float -------- ------------------------ ------------------- -------------- ----------------- --------------- NUMBER BOOLEAN getBoolean setBoolean bool NUMBER BIT getBoolean setBoolean bool NUMBER TINYINT (0..255) getShort setShort int NUMBER SMALLINT (-2^15..2^15) getShort getShort int NUMBER INTEGER (-2^31..2^31) getInt getInt int NUMBER BIGINT (-2^63..2^63) getLong getLong int -------- ------------------------ ------------------- -------------- ----------------- --------------- BINARY BINARY getBytes setBytes bytes BINARY BLOB getBytes setBytes bytes getBlob BINARY LONGVARBINARY getBytes setBytes bytes BINARY VARBINARY getBytes setBytes bytes -------- ------------------------ ------------------- -------------- ----------------- --------------- TEXT CLOB getString setString str getClob TEXT LONGNVARCHAR getString setString str TEXT LONGVARCHAR getString setString str TEXT NCLOB getString setString str getNClob TEXT SQLXML getString setString str getSQLXML -------- ------------------------ ------------------- -------------- ----------------- --------------- STRING NVARCHAR getString setString str STRING CHAR getString setString str STRING NCHAR getString setString str STRING VARCHAR getString setString str -------- ------------------------ ------------------- -------------- ----------------- --------------- ARRAY getObject getArray OBJECT getObject getObject NULL getObject getObject REF getObject getRef ROWID getObject getRowId RESULTSET getObject getObject TIME_WITH_TIMEZONE getObject getTime TIMESTAMP_WITH_TIMEZONE getObject getTimeStamp -------- ------------------------ ------------------- -------------- ----------------- --------------- * ASCII_STREAM getAsciiStream * BINARY_STREAM getBinaryStream * CHARACTER_STREAM getCharacterStream * ASCII_STREAM getAsciiStream * BINARY_STREAM getBinaryStream * CHARACTER_STREAM getCharacterStream * NCHARACTER_STREAM getNCharacterStream * URL getURL ======== ======================== =================== ============== ================= =============== Some of these types never correspond to a SQL type but are used only to specify getters and setters for a particular parameter or column. Other ----- The default getter will attempt to look for the column type by name if the type is OTHER. This allows for user defined types to be added if supported by the database. User defined types ------------------ A user can declare a new type using ``JDBCType``. The arguments are the name of new type which must match a SQL typename. Use ``typeinfo`` on the connection to get the list of available types. It may be necessary to define a custom getter function when defining a new type so that the custom return type accurately reflects the column type. .. code-block:: python class JSONType(dbapi2.JDBCType): def get(self, *args): rc = JDBCType.get(self, *args) # Custom return converter here return rc JSON = JSONType("JSON") Interactions with prepared statements ------------------------------------- Certain calls can be problematic for dbapi2 depending on the driver. In particular, SQL calls which invalidate the state of the connection will issue an exception when the connection is used. For example, when using HSQLDB, the statement ``cur.execute('shutdown')`` will invalidate and close the connection, causing an exception to be raised. This exception is due to a conflict between dbapi2, Java, and HSQLDB specifications. Dbapi2 requires that statements be executed as prepared statements, Java requires that closing a statement yields no action if the connection is already closed, and HSQLBD sets the ``isValid`` to false but not ``isClosed``. Thus executing a shutdown through dbapi2 would be expected to close the prepared statement on an invalid connection resulting in an error. We can address these sorts of driver specific behaviors by applying a customizer to the Java class to add additional behaviors. .. code-block:: python @jpype.JImplementationFor("java.sql.PreparedStatement") class MyStatement(object): @jpype.JOverride(sticky=True, rename='_close') def close(self): if not self.getConnection().isValid(100): return return self._close() Alternatively we can access the ``java.sql.Connection`` directly and call the shutdown routine using an unprepared statement. Though that would require accessing private fields. Conclusion ========== This wraps up the JPype dbapi2 module. Because JDBC supports many different database drivers, not every behavior is defined on every driver. Consult the driver specific information to determine what is available. The dbapi2 does not fully cover all of the capabilities of the JDBC driver. To access functions that are not defined in DBAPI2, the JDBC native objects can be accessed on both the connection and the cursor objects. .. _PEP-0249: https://www.python.org/dev/peps/pep-0249/ .. _connection.rollback: #jpype.dbapi2.Connection.rollback .. _connection.commit: #jpype.dbapi2.Connection.commit .. _connection.adapters: #jpype.dbapi2.JDBCType.adapters .. _connection.setters: #jpype.dbapi2.JDBCType.setters .. _connection.converters: #jpype.dbapi2.JDBCType.converters .. _cursor.use: #jpype.dbapi2.Cursor.use .. _cursor.description: #jpype.dbapi2.Cursor.description .. _jdbctype.adapters: #jpype.dbapi2.Connection.adapters jpype-1.6.0/doc/develguide.rst000066400000000000000000001464171501674766700163210ustar00rootroot00000000000000Developer Guide =============== Overview -------- This document describes the guts of jpype. It is intended lay out the architecture of the jpype code to aid intrepid lurkers to develop and debug the jpype code once I am run over by a bus. For most of this document I will use the royal we, except where I am giving personal opinions expressed only by yours truly, the author Thrameos. History ~~~~~~~ When I started work on this project it had already existed for over 10 years. The original developer had intended a much larger design with modules to support multiple languages such as Ruby. As such it was constructed with three layers of abstraction. It has a wrapper layer over Java in C++, a wrapper layer for the Python api in C++, and an abstraction layer intended to bridge Python and other interpreted languages. This multilayer abstraction ment that every debugging call had to drop through all of those layers. Memory management was split into multiple pieces with Java controlling a portion of it, C++ holding a bunch of resources, Python holding additional resources, and HostRef controlling the lifetime of objects shared between the layers. It also had its own reference counting system for handing Java references on a local scale. This level of complexity was just about enough to scare off all but the most hardened programmer. Thus I set out to eliminate as much of this as I could. Java already has its own local referencing system to deal in the form of LocalFrames. It was simply a matter of setting up a C++ object to hold the scope of the frames to eliminate that layer. The Java abstraction was laid out in a fashion somewhat orthagonally to the Java inheritance diagram. Thus that was reworked to something more in line which could be safely completed without disturbing other layers. The multilanguage abstraction layer was already pierced in multiple ways for speed. However, as the abastraction interwove throughout all the library it was a terrible lift to remove and thus required gutting the Python layer as well to support the operations that were being performed by the HostRef. The remaining codebase is fairly slim and reasonably streamlined. This rework cut out about 30% of the existing code and sped up the internal operations. The Java C++ interface matches the Java class hierachy. Architecture ~~~~~~~~~~~~ JPype is split into several distinct pieces. ``jpype`` Python module The majority of the front end logic for the toolkit is in Python jpype module. This module deals with the construction of class wrappers and control functions. The classes in the layer are all prefixed by ``J``. ``_jpype`` CPython module The native module is supported by a CPython module called ``_jpype``. The ``_jpype`` module is located in ``native/python`` and has C style classes with a prefix ``PyJP``. This CPython layer acts as a front end for passing to the C++ layer. It performs some error checking. In addition to the module functions in ``_JModule``, the module has multiple Python classes to support the native jpype code such as ``_JClass``, ``_JArray``, ``_JValue``, ``_JValue``, etc. CPython API wrapper In addition to the exposed Python module layer, there is also a C++ wrapper for the Python API. This is located in ``native/python`` and has the prefix ``JPPy`` for all classes. ``jp_pythontypes`` wraps the required parts of the CPython API in C++ for use in the C++ layer. C++ JNI layer The guts that drive Java are in the C++ layer located in ``native/common``. This layer has the namespace ``JP``. The code is divided into wrappers for each Java type, a typemanager for mapping from Java names to class instances, support classes for proxies, and a thin JNI layer used to help ensure rigerous use of the same design patterns in the code. The primary responsibility of this layer is type conversion and matching of method overloads. Java layer In addition to the C++ layer, jpype has a native Java layer. This code is compiled as a "thunk" which is loaded into the JVM in the form of a a binary stored as a string. Code for Java is found in ``native/java``. The Java layer is divided into two parts, a bootstrap loader and a jar containing the support classes. The Java layer is responsible managing the lifetime of shared Python, Java, and C++ objects. ``jpype`` module ----------------- The ``jpype`` module itself is made of a series of support classes which act as factories for the individual wrappers that are created to mirror each Java class. Because it is not possible to wrap all Java classes with staticly created wrappers, instead jpype dynamically creates Python wrappers as requested by the user. The wrapping process is triggered in two ways. The user can manually request creating a class by importing a class wrapper with jpype.imports or ``JPackage`` or by manually invoking it with ``JClass``. Or the class wrapper can be created automatically as a result of a return type or exception thrown to the user. Because the classes are created dynamically, the class structure uses a lot of Python meta programming. Each class wrapper derives from the class wrappers of each of the wrappers corresponding to the Java classes that each class extends and implements. The key to this is to hacked ``mro``. The ``mro`` orders each of the classes in the tree such that the most drived class methods are exposed, followed by each parent class. This must be ordered to break ties resulting from multiple inheritance of interfaces. The factory classes are grafted into the type system using ``__instancecheck__`` and ``__subtypecheck__``. resource types ~~~~~~~~~~~~~~ JPype largely maps to the same concepts as Python with a few special elements. The key concept is that of a Factory which serves to create Java resources dynamically as requested. For example there is no Python notation to create a ``int[][]`` as the concept of dimensions are fluid in Python. Thus a factory type creates the actual object instance type with ``JArray(JInt,2)`` Like Python objects, Java objects derives from a type object which is called ``JClass`` that serves as a meta type for all Java derived resources. Additional type like object ``JArray`` and ``JInterface`` serve to probe the relationships between types. Java object instances are created by calling the Java class wrapper just like a normal Python class. A number of pseudo classes serve as placeholders for Java types so that it is not necessary to create the type instance when using. These aliased classes are ``JObject``, ``JString``, and ``JException``. Underlying all Java instances is the concept of a ``jvalue``. ``jvalue`` ++++++++++ In the earlier design, wrappers, primitives and objects were all seperate concepts. At the JNI layer these are unified by a common element called jvalue. A ``jvalue`` is a union of all primitives with the jobject. The jobject can represent anything derived from Java object including the pseudo class jstring. This has been replaced with a Java slot concept which holds an instance of ``JPValue`` which holds a pointer to the C++ Java type wrapper and a Java jvalue union. We will discuss this object further in the CPython section. .. _bootstrapping: Bootstrapping ~~~~~~~~~~~~~ The most challenging part in working with the jpype module other than the need to support both major Python versions with the same codebase is the bootstrapping of resources. In order to get the system working, we must pass the Python resources so the ``_jpype`` CPython module can acquire resources and then construct the wrappers for ``java.lang.Object`` and ``java.lang.Class``. The key difficulty is that we need reflection to get methods from Java and those are part of ``java.lang.Class``, but class inherits from ``java.lang.Object``. Thus Object and the interfaces that Class inherits must all be created blindly. The order of bootstrapping is controlled by specific sequence of boot actions after the JVM is started in ``startJVM``. The class instance ``class_`` may not be accessed until after all of the basic class, object, and exception types have been loaded. Factories ~~~~~~~~~ The key objects exposed to the user (``JClass``, ``JObject``, and ``JArray``) are each factory meta classes. These classes serve as the gate keepers to creating the meta classes or object instances. These factories inherit from the Java class meta and have a ``class_`` instance inserted after the the JVM is started. They do not have exposed methods as they are shadows for action for actual Java types. The user calls with the specified arguments to create a resource. The factory calls the ``__new__`` method when creating an instance of the derived object. And the C++ wrapper calls the method with internally construct resource such as ``_JClass`` or ``_JValue``. Most of the internal calls currently create the resource directly without calling the factories. The gateway for this is ``PyJPValue_create`` which delegates the process to the corresponding specialized type. Style ~~~~~ One of the aspects of the jpype design is elegance of the factory patterns. Rather than expose the user a large number of distinct concepts with different names, the factories provide powerfull functionality with the same syntax for related things. Boxing a primitive, casting to a specific type, and creating a new object are all tied together in one factory, ``JObject``. By also making that factory an effective base class, we allow it to be used for ``issubtype`` and ``isinstance``. This philosophy is further enhanced by silent customizers which integrate Python functionality into the wrappers such that Java classes can be used effectively with Python syntax. Consistent use and misuse of Python concepts such as ``with`` for defining blocks such as try with resources and synchronized hide the underlying complexity and give the feeling to the user that the module is integrated completely as a solution such as jython. When adding a new feature to the Python layer, consider carefully if the feature needs to be exposed a new function or if it can be hidden in the normal Python syntax. JPype does somewhat break the Python naming conventions. Because Java and Python have very different naming schemes, at least part of the kit would have a different convention. To avoid having one portion break Python conventions and another part conform, we choose to use Java notation consistently throughout. Package names should be lower with underscores, classes should camel case starting upper, functions and method should be camel case starting lower. All private methods and classes start with a leading underscore and are not exported. Customizers ~~~~~~~~~~~ There was a major change in the way the customizers work between versions. The previous system was undocumented and has now been removed, but as someone may have used of it previously, we will contrast it with the revised system so that the customizers can be converted. In the previous system, a global list stored all customizers. When a class was created, it went though the list and asked the class if it matched that class name. If it matched, it altered the dict of members to be created so when the dynamic class was finished it had the custome behavior. This system wasn't very scalable as each customizer added more work to the class construction process. The revised system works by storing a dictionary keyed to the class name. Thus the customizer only applies to the specific class targeted to the customizer. The customizer is specified using annotation of a prototype class making methods automatically copy onto the class. However, sometimes a customizer needs to be applied to an entire tree of classes such as all classes that implement ``java.util.List``. To handle this case, the class creation system looks for a special method ``__java_init__`` in the tree of base classes and calls it on the newly created class. Most of the time the customization was the same simple pattern so we added a ``sticky`` flag to build the initialization method directly. This method can alter the class to make it add the new behavior. Note the word alter. Where before we changed the member prior to creating the class, here we are altering the class. Thus the customizer is expected to monkey patch the existing class. There is only one pattern of monkey patching that works on both Python 2 and Python 3 so be sure to use the ``type.__setattr__`` method of altering the class dictionary. It is possible to apply customizers after the class has already been created because we operate by monkey patching. But there is a limitation that there can only be one ``__java_init__`` method and thus two customizers specifying a global behavior on the same class wrapper will lead to unexpected behavior. ``_jpype`` CPython module -------------------------- Diving deeper into the onion, we have the Python front end. This is divided into a number of distinct pieces. Each piece is found under ``native/python`` and is named according to the piece it provides. For example, ``PyJPModule`` is found in the file ``native/python/pyjp_module.cpp`` Earlier versions of the module had all of the functionality in the modules global space. This functionality is now split into a number of classes. These classes each have a constructor that is used to create an instance which will correspond to a Java resource such as class, array, method, or value. Jpype objects work with the inner layers by inheriting from a set of special ``_jpype`` classes. This class hiarachy is mantained by the meta class ``_jpype._JClass``. The meta class does type hacking of the Python API to insert a reserved memory slot for the ``JPValue`` structure. The meta class is used to define the Java base classes: * ``_JClass`` - Meta class for all Java types which maps to a java.lang.Class extending Python type. * ``_JArray`` - Base class for all Java array instances. * ``_JObject`` - Base type of all Java object instances extending Python object. * ``_JNumberLong`` - Base type for integer style types extending Python int. * ``_JNumberFloat`` - Base type for float style types extending Python float. * ``_JNumberChar`` - Special wrapper type for JChar and java.lang.Character types extending Python float. * ``_JException`` - Base type for exceptions extending Python Exception. * ``_JValue`` - Generic capsule representing any Java type or instance. These types are exposed to Python to implement Python functionality specific to the behavior expected by the Python type. Under the hood these types are largely ignored. Instead the internal calls for the Java slot to determine how to handle the type. Therefore, internally often Python methods will be applied to the "wrong" type as the requirement for the method can be satisfied by any object with a Java slot rather than a specific type. See the section regarding Java slots for details. ``PyJPModule`` module ~~~~~~~~~~~~~~~~~~~~~~ This is the front end for all the global functions required to support the Python native portion. Most of the functions provided in the module are for control and auditing. Resources are created by setting attributes on the ``_jpype`` module prior to calling ``startJVM``. When the JVM is started each of th required resources are copied from the module attribute lists to the module internals. Setting the attributes after the JVM is started has no effect. Resources are verified to exist when the JVM is started and any missing resource are reported as an error. ``_JClass`` class ~~~~~~~~~~~~~~~~~~~ The class wrappers have a metaclass ``_jpyep._JClass`` which serves as the guardian to ensure the slot is attached, provide for the inheritance checks, and control access to static fields and methods. The slot holds a java.lang.Class instance but it does not have any of the methods normally associate with a Java class instance exposed. A java.lang.Class instance can be converted to a Jave class wrapper using ``JClass``. ``_JMethod`` class ~~~~~~~~~~~~~~~~~~~~ This class acts as descriptor with a call method. As a descriptor accessing its methods through the class will trigger its ``__get__`` function, thus getting ahold of it within Python is a bit tricky. The ``__get__`` mathod is used to bind the static unbound method to a particular object instance so that we can call with the first argument as the ``this`` pointer. It has some reflection and diagnostics methods that can be useful it tracing down errors. The beans methods are there just to support the old properties API. The naming on this class is a bit deceptive. It does not correspond to a single method but rather all the overloads with the same name. When called it passes to with the arguments to the C++ layer where it must be resolved to a specific overload. This class is stored directly in the class wrappers. ``_JField`` class ~~~~~~~~~~~~~~~~~~~ This class is a descriptor with ``__get__`` and ``__set__`` methods. When called at the static class layer it operates on static fields. When called on a Python object, it binds to the object making a ``this`` pointer. If the field is static, it will continue to access the static field, otherwise, it will provide access to the member field. This trickery allows both static and member fields to wrap as one type. This class is stored directly in the class wrappers. ``_JArray`` class ~~~~~~~~~~~~~~~~~~~ Java arrays are extensions of the Java object type. It has both methods associated with java.lang.Object and Python array functionality. Primitives have specialized implementations to allow for the Python buffer API. ``_JMonitor`` class ~~~~~~~~~~~~~~~~~~~~~ This class provides ``synchronized`` to JPype. Instances of this class are created and held using ``with``. It has two methods ``__enter__`` and ``__exit__`` which hook into the Python RAII system. ``_JValue`` class ~~~~~~~~~~~~~~~~~~~ Java primitive and object instance derive from special Python derived types. These each have the Python functionality to be exposed and a Java slot. The most generic of these is ``_JValue`` which is simply a capsule holding the Java C++ type wrapper and a Java jvalue union. CPython methods for the ``PyJPValue`` apply to all CPython objects that hold a Java slot. Specific implementation exist for object, numbers, characters, and exceptions. But fundimentally all are treated the same internally and thus the CPython type is effectively erased outside of Python. Unlike ``jvalue`` we hold the object type in the C++ ``JPValue`` object. The class reference is used to determine how to match the arguments to methods. The class may not correspond to the actual class of the object. Using a class other than the actual class serves to allow an object to be cast and thus treated like another type for the purposes of overloading. This mechanism is what allows the ``JObject`` factory to perform a typecast to make an object instance act like one of its base classes.. .. _javaslots: Java Slots ------------------ THe key to achieving reasonable speed within CPython is the use of slots. A slot is a dedicated memory location that can be accessed without consulting the dictionary or bases of an object. CPython achieve this by reserving space within the type structure and by using a set of bit flags so that it can avoid costly. The reserved space in order by number and thus avoids the need to access the dictionary while the bit flags serve to determine the type without traversing the ``__mro__`` structure. We had to implement the same effect which deriving from a wide variety for Python types including type, object, int, long, and Exception. Adding the slot directly to the type and objects base memory does not work because these types all have different memory layouts. We could have a table look up based on the type but because we must obey both the CPython and the Java object hierarchy at the same time it cannot be done within the memory layout of Python objects. Instead we have to think outside the box, or rather outside the memory footprint of Python objects. CPython faces the same conflict internally as inheritance often forces adding a dictionary or weak reference list onto a variably size type sych as long. For those cases it adds extract space to the basesize of the object and then ignores that space for the purposes of checking inheritance. It pairs this with an offset slot that allows for location of the dynamic placed slots. We cannot replicate this in the same way because the CPython interals are all specialize static members and there is no provision for introducting user defined dynamic slots. Therefore, instead we will add extra memory outside the view of Python objects though the use of a custom allocator. We intercept the call to create an object allocation and then call the regular Python allocators with the extra memory added to the request. As our extrs slot has resource in the form of Java global references associated with it, we must deallocate those resource regardless of the type that has been extended. We perform this task by creating a custom finalize method to serve as the destructor. Thus a Java slot requires overriding each of ``tp_alloc``, ``tp_free`` and ``tp_finalize``. The class meta gatekeeper creates each type and verifies that the required hooks are all in place. If the user tries to bypass this it should produce an error. In place of Python bit flags to check for the presence of a Java slot we instead test the slot table to see if our hooks are in place. We can test if the slot is present by looking to see if both `tp_alloc` and `tp_finalize` point to our Java slot handlers. This means we are still effectively a slot as we can test and access with O(1). Accessing the slot requires testing if the slot exists for the object, then computing the sice of the object using the basesize and itemsize associate with the type and then offsetting the Python object pointer appropriately. The overall cost is O(1), though is slightly more heavy that directly accesssing an offset. CPython API layer ------------------ To make creation of the C++ layer easier a thin wrapper over the CPython API was developed. This layer provided for handling the CPython referencing using a smart pointer, defines the exception handling for Python, and provides resource hooks for duck typing of the ``_jpype`` classes. This layer is located with the rest of the Python codes in ``native/python``, but has the prefix ``JPPy`` for its classes. As the bridge between Python and C++, these support classes appear in both the ``_jpype`` CPython module and the C++ JNI layer. Exception handling ~~~~~~~~~~~~~~~~~~ A key piece of the jpype interaction is the transfer of exceptions from Java to Python. To accomplish this Python method that can result in a call to Java must have a ``try`` block around the contents of the function. We use a routine pattern of code to interact with Java to achieve this: .. code-block:: cpp PyObject* dosomething(PyObject* self, PyObject* args) { // Tell the logger where we are JP_PY_TRY("dosomething"); // Make sure there is a jvm to receive the call. ASSERT_JVM_RUNNING("dosomething"); // Make a resource to capture any Java local references JPJavaFrame frame; // Call our Java methods ... // Return control to Python return obj.keep(); // Use the standard catch to transfer any exceptions back // to Python JP_PY_CATCH(NULL); } All entry points from Python into ``_jpype`` should be guarded with this pattern. There are exceptions to this pattern such as removing the logging, operating on a call that does not need the JVM running, or operating where the frame is already supported by the method being called. Python referencing ~~~~~~~~~~~~~~~~~~ One of the most miserable aspects of programming with CPython is the relative inconsistancy of referencing. Each method in Python may use a Python object or steal it, or it may return a borrowed reference or give a fresh reference. Similar command such as getting an element from a list and getting an element from a tuple can have different rules. This was a constant source of bugs requiring consultation of the Python manual for every line of code. Thus we wrapped all of the Python calls we were required to work with in ``jp_pythontypes``. Included in this wrapper is a Python reference counter called ``JPPyObject``. Whenever an object is returned from Python it is immediately placed in smart pointer ``JPPyObject`` with the policy that it was created with such as ``use_``, ``borrowed_``, ``claim_`` or ``call_``. ``use_`` This policy means that the reference counter needs to be incremented and the start and the end. We must reference it because if we don't and some Python call destroys the refernce out from under us, the system may crash and burn. ``borrowed_`` This policy means we were to be give a borrowed reference that we are expected to reference and unreference when complete, but the command that returned it can fail. Thus before reference it, the system must check if an error has occurred. If there is an error, it is promoted to an exception. ``claim_`` This policy is used when we are given a new object with is already referenced for us. Thus we are to steal the reference for the duration of our use and then dereference when we are done to keep it from leaking. ``call_`` This policy both steals the reference and verifies there were no errors prior to continuing. Errors are promoted to exceptions when this reference is created. If we need to pass an object which is held in a smart pointer to Python which requires a reference, we call keep on the reference which transfers control to a ``PyObject*`` and prevents the pointer from removing the reference. As the object handle is leaving our control keep should only be called the return statement. The smart pointer is not used on method passing in which the parent explicitly holds a reference to the Python object. As all tuples passed as arguments operate like this, that means much of the API accepts bare ``PyObject*`` as arguments. It is the job of the caller to hold the reference for its scope. On CPython extensions ~~~~~~~~~~~~~~~~~~~~~ CPython is somewhat of a nightmare to program in. It is not that they did not try to document the API, but it is darn complex. The problems extend well beyond the reference counting system that we have worked around. In particular, the object model though well developed is very complex, often to get it to work you must follow letter for letter the example on the CPython user guide, and even then it may all go into the ditch. The key problem is that there are a lot of very bad examples of how to write CPython extension modules out there. Often the these examples bypass the appropriate macro and just call the field, or skip the virtual table and try to call the Python method directly. It is true that these things do not break there example, but they are conditioned on these methods they are calling directly to be the right one for the job, but depends a lot on what the behavior of the object is supposed to be. Get it wrong and you get really nasty segfault. CPython itself may be partly responsible for some of these problems. They generally seem to trust the user and thus don't verify if the call makes sense. It is true that it will cost a little speed to be aggressive about checking the type flags and the allocator match, but not checking when the error happens, means that it fails far from the original problem source. I would hope that we have moved beyond the philosophy that the user should just to whatever they want so it runs as fast as possible, but that never appears to be the case. Of course, I am just opining from the outside of the tent and I am sure the issues are much more complicated it appears superficially. Then again if I can manage to provide a safe workspace while juggling the issues of multiple virtual machines, I am free to have opinions on the value of trading performance and safety. In short when working on the extension code, make sure you do everything by the book, and check that book twice. Always go through the types virtual table and use the propery macros to access the resources. Miss one line in some complex pattern even once and you are in for a world of hurt. There are very few guard rails in the CPython code. C++ JNI layer ------------- The C++ layer has a number of tasks. It is used to load thunks, call JNI methods, provide reflection of classes, determine if a conversion is possible, perform conversion, match arguments to overloads, and convert return values back to Java. Memory management ~~~~~~~~~~~~~~~~~ Java provides built in memory management for controlling the lifespan of Java objects that are passed through JNI. When a Java object is created or returned from the JVM it returns a handle to object with a reference counter. To manage the lifespan of this reference counter a local frame is created. For the duration of this frame all local references will continue to exist. To extend the lifespan either a new global reference to the object needs to be created, or the object needs to be kept. When the local frame is destroyed all local references are destroyed with the exception of an optional specified local return reference. We have wrapped the Java reference system with the wrapper ``JPLocalFrame``. This wrapper has three functions. It acts as a RAII (Resource acquisition is initialization) for the local frame. Further, as creating a local frame requires creating a Java env reference and all JNI calls require access to the env, the local frame acts as the front end to call all JNI calls. Finally as getting ahold of the env requires that the thread be attached to Java, it also serves to automatically attach threads to the JVM. As accessing an unbound thread will cause a segmentation fault in JNI, we are now safe from any threads created from within Python even those created outside our knowledge. (I am looking at you spyder) Using this pattern makes the JPype core safe by design. Forcing JNI calles to be called using the frame ensures: - Every local reference is destroyed. - Every thread is properly attached before JNI is used. - The pattern of keep only one local reference is obeyed. To use a local frame, use the pattern shown in this example. .. code-block:: cpp jobject doSomeThing(std::string args) { // Create a frame at the top of the scope JPLocalFrame frame; // Do the required work jobject obj =frame.CallObjectMethodA(globalObj, methodRef, params); // Tell the frame to return the reference to the outer scope. // once keep is called the frame is destroyed and any // call will fail. return frame.keep(obj); } Note that the value of the object returned and the object in the function will not be the same. The returned reference is owned by the enclosing local frame and points to the same object. But as its lifespan belongs to the outer frame, its location in memory is different. You are allowed to ``keep`` a reference that was global or was passed in, in either of those case, the outer scope will get a new local reference that points to the same object. Thus you don't need to track the origin of the object. The changing of the value while pointing is another common problem. A routine error is to get a local reference, call ``NewGlobalRef`` and then keeping the local reference rather than the shiny new global reference it made. This is not like the Python reference system where you have the object that you can ref and unref. Thus make sure you always store only the global reference. .. code-block:: cpp jobject global; // we are getting a reference, may be local, may be global. // either way it is borrowed and it doesn't belong to us. void elseWhere(jvalue value) { JPLocalFrame frame; // Bunch of code leading us to decide we need to // hold the resource longer. if (cond) { // okay we need to keep this reference, so make a // new global reference to it. global = frame.NewGlobalRef(value.l); } } But don't mistake this as an invitation to make global references everywhere. Global reference are global, thus will hold the member until the reference is destroyed. C++ exceptions can lead to missing the unreference, thus global references should only happen when you are placing the Java object into a class member variable or a global variable. To help manage global references, we have ``JPRef<>`` which holds a global reference for the duration of the C++ lifespace. This is the base class for each of the global reference types we use. .. code-block:: cpp typedef JPRef JPClassRef; typedef JPRef JPObjectRef; typedef JPRef JPArrayRef; typedef JPRef JPThrowableRef; For functions that expect the outer scope to already have created a frame for this context, we use the pattern of extending the outer scope rather than creating a new one. .. code-block:: cpp jobject doSomeThing(JPLocalFrame& frame, std::string args) { // Do the required work jobject obj = frame.CallObjectMethodA(globalObj, methodRef, params); // We must not call keep here or we will terminate // a frame we do not own. return obj; } Although the system we have set up is "safe by design", there are things that can go wrong is misused. If the caller fails to create a frame prior to calling a function that returns a local reference, the reference will go into the program scoped local references and thus leak. Thus, it is usually best to force the user to make a scope with the frame extension pattern. Second, if any JNI references that are not kept or converted to global, it becomes invalid. Further, since JNI recycles the reference pointer fairly quickly, it most likely will be pointed to another object whose type may not be expected. Thus, best case is using the stale reference will crash and burn. Worse case, the reference will be a live reference to another object and it will produce an error which seems completely irrelevant to anything that was being called. Horrible case, the live object does not object to bad call and it all silently proceeds down the road another two miles before coming to flaming death. Moral of the story, always create a local frame even if you are handling a global reference. If passed or returned a reference of any kind, it is a borrowed reference belonging to the caller or being held by the current local frame. Thus it must be treated accordingly. If you have to hold a global use the appropraite ``JPRef`` class to ensure it is exception and dtor safe. For further information read ``native/common/jp_javaframe.h``. Type wrappers ~~~~~~~~~~~~~ Each Java type has a C++ wrapper class. These classes provide a number of methods. Primitives each have their own unit type wrapper. Object, arrays, and class instances share a C++ wrapper type. Special instances are used for ``java.lang.Object`` and ``java.lang.Class``. The type wrapper are named for the class they wrap such as ``JPIntType``. Type conversion ++++++++++++++++ For type conversion, a C++ class wrapper provides four methods. ``canConvertToJava`` This method must consult the supplied Python object to determine the type and then make a determination of whether a conversion is possible. It reports ``none_`` if there is no possible conversion, ``explicit_`` if the conversion is only acceptable if forced such as returning from a proxy, ``implicit_`` if the conversion is possible and acceptable as part of an method call, or ``exact_`` if this type converts without ambiguity. It is excepted to check for something that is already a Java resource of the correct type such as ``JPValue``, or something this is implementing the behavior as an interface in the form of a ``JPProxy``. ``convertToJava`` This method consults the type and produces a conversion. The order of the match should be identical to the ``canConvertToJava``. It should also handle values and proxies. ``convertToPythonObject`` This method takes a jvalue union and converts it to the corresponding Python wrapper instance. ``getValueFromObject`` This converts a Java object into a ``JPValue`` corresponding. This unboxes primitives. Array conversion ++++++++++++++++++ In addition to converting single objects, the type rewrappers also serve as the gateway to working with arrays of the specified type. Five methods are used to work with arrays: ``newArrayInstance``, ``getArrayRange``, ``setArrayRange``, ``getArrayItem``, and ``setArrayItem``. Invocation and Fields ++++++++++++++++++++++ To convert a return type produced from a Java call, each type needs to be able to invoke a method with that return type. This corresponses the underlying JNI design. The methods invoke and invokeStatic are used for this purpose. Similarly accessing fields requires type conversion using the methods ``getField`` and ``setField``. Instance versus Type wrappers +++++++++++++++++++++++++++++++ Instances of individual Java classes are made from ``JPClass``. However, two special sets of conversion rules are required. These are in the form of specializations ``JPObjectBaseClass`` and ``JPClassBaseClass`` corresponding to ``java.lang.Object`` and ``java.lang.Class``. Support classes ~~~~~~~~~~~~~~~ In addition to the type wrappers, there are several support classes. These are: ``JPTypeManager`` The typemanager serves as a dict for all type wrappers created during the operation. ``JPReferenceQueue`` Lifetime manager for Java and Python objects. ``JPProxy`` Proxies implement a Java interface in Python. ``JPClassLoader`` Loader for Java thunks. ``JPEncoding`` Decodes and encodes Java UTF strings. ``JPTypeManager`` ++++++++++++++++++ C++ typewrappers are created as needed. Instance of each of the primitives along with ``java.lang.Object`` and ``java.lang.Class`` are preloaded. Additional instances are created as requested for individual Java classes. Currently this is backed by a C++ map of string to class wrappers. The typemanager provides a number lookup methods. .. code-block:: cpp // Call from within Python JPClass* JPTypeManager::findClass(const string& name) // Call from a defined Java class JPClass* JPTypeManager::findClass(jclass cls) // Call used when returning an object from Java JPClass* JPTypeManager::findClassForObject(jobject obj) ``JPReferenceQueue`` ++++++++++++++++++++ When a Python object is presented to Java as opposed to a Java object, the lifespan of the Python object must be extended to match the Java wrapper. The reference queue adds a reference to the Python object that will be removed by the Java layer when the garbage collection deletes the wrapper. This code is almost entirely in the Java library, thus only the portion to support Java native methods appears in the C++ layer. Once started the reference queue is mostly transparent. registerRef is used to bind a Python object live span to a Java object. .. code-block:: cpp void JPReferenceQueue::registerRef(jobject obj, PyObject* hostRef) ``JPProxy`` ++++++++++++ In order to call Python functions from within Java, a Java proxy is used. The majority of the code is in Java. The C++ code holds the Java native portion. The native implement of the proxy call is the only place in with the pattern for reflecting Python exceptions back into Java appears. As all proxies are ties to Python references, this code is strongly tied to the reference queue. ``JPClassLoader`` ++++++++++++++++++ This code is responsible for loading the Java class thunks. As it is difficult to ensure we can access a Java jar from within Python, all Java native code is stored in a binary thunk compiled into the C++ layer as a header. The class loader provides a way to load this embedded jar first by bootstrapping a custom Java classloader and then using that classloader to load the internal jar. The classloader is mostly transparent. It provides one method called findClass which loads a class from the internal jar. .. code-block:: cpp jclass JPClassLoader::findClass(string name) ``JPEncoding`` +++++++++++++++ Java concept of UTF is pretty much out of sync with the rest of the world. Java used 16 bits for its native characters. But this was inadequate for all of the unicode characters, thus longer unicode character had to be encoded in the 16 bit space. Rather the directly providing methods to convert to a standard encoding such as UTF8, Java used UTF16 encoded in 8 bits which they dub Modified-UTF8. ``JPEncoding`` deals with converting this unusual encoding into something that Python can understand. The key method in this module is transcribe with signature .. code-block:: cpp std::string transcribe(const char* in, size_t len, const JPEncoding& sourceEncoding, const JPEncoding& targetEncoding) There are two encodings provided, ``JPEncodingUTF8`` and ``JPEncodingJavaUTF8``. By selecting the source and traget encoding transcribe can convert to or from Java to Python encoding. Incidentally that same modified UTF coding is used in storing symbols in the class files. It seems like a really poor design choice given they have to document this modified UTF in multiple places. As far as I can tell the internal converter only appears on ``java.io.DataInput`` and ``java.io.DataOutput``. Java native code ---------------- At the lowest level of the onion is the native Java layer. Although this layer is most remote from Python, ironically it is the easiest layer to communicate with. As the point of jpype is to communicate with Java, it is possible to directly communicate with the jpype Java internals. These can be imported from the package ``org.jpype``. The code for the Java layer is located in ``native/java``. It is compiled into a jar in the build directory and then converted to a C++ header to be compiled into the ``_jpype`` module. The Java layer currently houses the reference queue, a classloader which can load a Java class from a bytestream source, the proxy code for implementing Java interfaces, and a memory compiler module which allows Python to directly create a class from a string. Tracing --------- Because the relations between the layers can be daunting especially when things go wrong. The CPython and C++ layer have a built in logger. This logger must be enabled with a compiler switch to activate. To active the logger, touch one of the cpp files in the native directory to mark the build as dirty, then compile the ``jpype`` module with: :: python setup.py develop --enable-tracing Once built run a short test program that demonstrates the problem and capture the output of the terminal to a file. This should allow the developer to isolate the fault to specific location where it failed. To use the logger in a function start the ``JP_TRACE_IN(function_name)`` which will open a ``try catch`` block. The JPype tracer can be augmented with the Python tracing module to give a very good picture of both JPype and Python states at the time of the crash. To use the Python tracing, start Python with... :: python -m trace --trace myscript.py Coverage -------- Some of the tests require additional instrumentation to run, this can be enabled with the ``enable-coverage`` option:: python setup.py develop --enable-coverage Debugging issues ---------------- If the tracing function proves inadequate to identify a problem, we often need to turn to a general purpose tool like gdb or valgrind. The JPype core is not easy to debug. Python can be difficult to properly monitor especially with tools like valgrind due to its memory handling. Java is also challenging to debug. Put them together and you have the mother of all debugging issues. There are a number of complicating factors. Let us start with how to debug with gdb. Gdb runs into two major issues, both tied to the signal handler. First, Java installs its own signal handlers that take over the entire process when a segfault occurs. This tends to cause very poor segfault stacktraces when examining a core file, which often is corrupt after the first user frame. Second, Java installs its signal handlers in such as way that attempting to run under a debugger like gdb will often immediately crash preventing one from catching the segfault before Java catches it. This makes for a catch 22, you can't capture a meaningful non-interactively produced core file, and you can't get an interactive session to work. Fortunately there are solutions to the interactive session issue. By disabling the SIGSEGV handler, we can get past the initial failure and also we can catch the stack before it is altered by the JVM. :: gdb -ex 'handle SIGSEGV nostop noprint pass' python Thus far I have not found any good solutions to prevent the JVM from altering the stack frames when dumping the core. Thus interactive debugging appears to be the best option. There are additional issues that one should be aware of. Open-JDK 1.8 has had a number of problems with the debugger. Starting JPype under gdb may trigger, may trigger the following error. :: gdb.error: No type named nmethod. There are supposed to be fixes for this problem, but none worked for me. Upgrading to Open-JDK 9 appears to fix the problem. Another complexity with debugging memory problems is that Python tends to hide the problem with its allocation pools. Rather than allocating memory when a new object is request, it will often recycle and existing object which was collect earlier. The result is that an object which turns out is still live becomes recycled as a new object with a new type. Thus suddenly a method which was expected to produce some result instead vectors into the new type table, which may or may not send us into segfault land depending on whether the old and new objects have similar memory layouts. This can be partially overcome by forcing Python to use a different memory allocation scheme. This can avoid the recycling which means we are more likely to catch the error, but at the same time means we will be excuting different code paths so we may not reach a similar state. If the core dump is vectoring off into code that just does not make sense it is likely caused by the memory pools. Starting Python 3, it is possible to select the memory allocation policy through an enviroment variable. See the ``PYTHONMALLOC`` setting for details. Deliberate Crash for Debugging ------------------------------ JPype includes deliberate crashes in its exception handling for scenarios where multiple failures occur, making it impossible to deliver errors to either Python or Java. These crashes are designed to aid debugging in catastrophic situations and offer significant advantages over simple program termination (`terminate`). When debugging JPype, deliberate crashes (segmentation faults) provide the following benefits: 1. **Stack Trace Availability**: - Deliberate crashes generate a meaningful stack trace for tools like `gdb`. - Termination does not produce a stack trace, making it harder to identify the root cause of the problem. 2. **Bypassing Signal Handlers**: - Deliberate crashes bypass Java's signal handlers, ensuring the stack trace remains intact. - Termination may still be affected by Java's signal handling, corrupting the debugging process. 3. **Memory State Preservation**: - Deliberate crashes halt execution immediately, preventing Python's memory recycling from altering the program state. - Termination allows Python to continue recycling memory, which can obscure the root cause of memory-related bugs. 4. **Interactive Debugging**: - Deliberate crashes enable interactive debugging with `gdb`, allowing developers to inspect the program state before corruption occurs. - Termination does not provide this opportunity. For these reasons, deliberate crashes are preferred in catastrophic scenarios where debugging is required. ### Implementation and Use Case In rare and catastrophic situations where all exception handling mechanisms failβ€”such as during startup or when critical resources are unavailableβ€”JPype uses a deliberate crash mechanism to produce a meaningful stack trace for debugging. This situation most often occurs when JVM resources are not found during initialization, resulting in errors that cannot be recovered. Reordering the resource loading sequence in `jp_context.cpp` is the most likely source of such failures. The deliberate crash is implemented as follows: .. code-block:: cpp int *i = nullptr; *i = 0; // Trigger deliberate crash for gdb backtrace This crash bypasses Java's signal handlers and Python's memory management, which can obscure debugging efforts. By triggering a segmentation fault, `gdb` can capture the stack trace at the point of failure, providing valuable insight into the issue. ### Debugging with `gdb` To debug using `gdb`, follow these steps: 1. Start Python with `gdb` and disable the SIGSEGV handler: .. code-block:: bash gdb -ex 'handle SIGSEGV nostop noprint pass' python 2. Run the program until the deliberate crash occurs. 3. Use the `bt` command in `gdb` to view the backtrace and identify the source of the problem. ### Important Note This mechanism is intended exclusively for debugging and should never be triggered during normal operation. If you encounter this crash, it indicates a critical failure that requires opening an issue on GitHub. Future directions ----------------- Although the majority of the code has been reworked for JPype 0.7, there is still further work to be done. Almost all Java constructs can be exercised from within Python, but Java and Python are not static. Thus, we are working on further improvements to the jpype core focusing on making the package faster, more efficient, and easier to maintain. This section will discuss a few of these options. Java based code is much easier to debug as it is possible to swap the thunk code with an external jar. Further, Java has much easier management of resources. Thus pushing a portion of the C++ layer into the Java layer could further reduce the size of the code base. In particular, deciding the order of search for method overloads in C++ attempts to reconstruct the Java overload rules. But these same rules are already available in Java. Further, the C++ layer is designed to make many frequent small calls to Java methods. This is not the preferred method to operate in JNI. It is better to have specialized code in Java which preforms large tasks such as collecting all of the fields needed for a type wrapper and passing it back in a single call, rather than call twenty different general purpose methods. This would also vastly reduce the number of ``jmethods`` that need to be bound in the C++ layer. The world of JVMs is currently in flux. Jpype needs to be able to support other JVMs. In theory, so long a JVM provides a working JNI layer, there is no reason the jpype can't support it. But we need loading routines for these JVMs to be developed if there are differences in getting the JVM launched. There is a project page on github shows what is being developed for the next release. Series 0.6 was usable, but early versions had notable issues with threading and internal memory management concepts had to be redone for stability. Series 0.7 is the first verion after rewrite for simplication and hardening. I consider 0.7 to be at the level of production quality code suitable for most usage though still missing some needed features. Series 0.8 will deal with higher levels of Python/Java integration such as Java class extension and pickle support. Series 0.9 will be dedicated to any additional hardening and edge cases in the core code as we should have complete integration. Assuming everything is completed, we will one day become a real boy and have a 1.0 release. jpype-1.6.0/doc/imports.rst000066400000000000000000000044741501674766700156750ustar00rootroot00000000000000JImport ======= Module for dynamically loading Java Classes using the import system. This is a replacement for the jpype.JPackage("com").fuzzy.Main type syntax. It features better safety as the objects produced are checked for class existence. To use Java imports, import the domains package prior to importing a Java class. This module supports three different styles of importing java classes. 1) Import of the package path ----------------------------- **import ** Importing a series of package creates a path to all classes contained in that package. The root package is added to the global scope. Imported packages are added to the directory of the base module. .. code-block:: python import java mystr = java.lang.String('hello') mylist = java.util.LinkedList() path = java.nio.files.Paths.get() 2) Import of the package path as a module ----------------------------------------- **import as ** A package can be imported as a local variable. This provides access to all Java classes in that package including contained packages. Example: .. code-block:: python import java.nio as nio bb = nio.ByteBuffer() path = nio.file.Path() 3) Import a class from an object -------------------------------- **from import [,\*] [as ]** An individual class can be imported from a java package. This supports inner classes as well. Example: .. code-block:: python # Import one class from java.lang import String mystr = String('hello') # Import multiple classes from java.lang import Number,Integer,Double # Import java inner class java.lang.ProcessBuilder.Redirect from java.lang.ProcessBuilder import Redirect This method can also be used to import a static variable or method from a class. Wildcards import all packages and public classes into the global scope. Import caveats -------------- Keyword naming ~~~~~~~~~~~~~~ Occasionally a java class may contain a python keyword. Python keywords as automatically remapped using trailing underscore. Example:: from org.raise_ import Object => imports "org.raise.Object" Limitations ~~~~~~~~~~~ * Non-static members can be imported but can not be called without an instance. JPype does not provide an easy way to determine which functions objects can be called without an object. jpype-1.6.0/doc/index.rst000066400000000000000000000020151501674766700152740ustar00rootroot00000000000000JPype documentation =================== JPype is a Python module to provide full access to Java from within Python. It allows Python to make use of Java specific libraries, explore and visualize Java structures, develop and test Java libraries, make use of scientific computing, and much more. By enabling the use of Python for rapid prototyping and Java for strong typed production code, JPype provides a powerful environment for engineering and code development. Unlike Jython, JPype does not achieve this by re-implementing Python, but instead by interfacing both virtual machines at the native level. This shared memory based approach achieves good computing performance, while providing the access to the entirety of CPython and Java libraries. Parts of the documentation ========================== .. toctree:: :maxdepth: 2 install userguide quickguide api dbapi2 imports android CHANGELOG develguide Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` jpype-1.6.0/doc/install.rst000066400000000000000000000136261501674766700156450ustar00rootroot00000000000000Installation ============ JPype is available either as a pre-compiled binary for Anaconda, or may be built from source though various methods. Binary Install -------------- JPype can be installed as pre-compiled binary if you are using the `Anaconda `_ Python stack. Binaries are available for Linux, OSX, and Windows on conda-forge. 1. Ensure you have installed Anaconda/Miniconda. Instructions can be found `here `__. 2. Install from the conda-forge software channel:: conda install -c conda-forge jpype1 Source Install -------------- Installing from source requires: Python JPype works CPython 3.5 or later. Both the runtime and the development package are required. Java Either the Sun/Oracle JDK/JRE Variant or OpenJDK. JPype source distribution includes a copy of the Java JNI header and precompiled Java code, thus the Java Development Kit (JDK) is not required. JPype has been tested with Java versions from Java 1.8 to Java 13. C++ A C++ compiler which matches the ABI used to build CPython. JDK *(Optional)* JPype contains sections of Java code. These sections are precompiled in the source distribution, but must be built when installing directly from the git repository. Once these requirements have been met, one can use pip to build from either the source distribution or directly from the repository. Specific requirements from different achitectures are listed below_. Build using pip ~~~~~~~~~~~~~~~ JPype may be built and installed with one step using pip. To install the latest JPype, use: :: pip install JPype1 This will install JPype either from source or binary distribution, depending on your operating system and pip version. To install from the current github master use: :: pip install git+https://github.com/jpype-project/jpype.git More details on installing from git can be found at `Pip install `__. The git version does not include a prebuilt jar the JDK is required. Build and install manually ~~~~~~~~~~~~~~~~~~~~~~~~~~ JPype can be built entirely from source. **1. Get the JPype source** The JPype source may be acquired from either `github `__ or from `PyPi `__. **2. Build the source with desired options** Compile JPype using the `build ` module (this will produce a wheel): :: python -m build /path/to/source A number of additional argument may be provided. --enable-build-jar Force setup to recreate the jar from scratch. --enable-tracing Build a verison of JPype with full logging to the console. This can be used to diagnose tricky JNI issues. For example:: python -m build /path/to/source -C--global-option=build_ext -C--global-option="--enable-tracing" After building, JPype can be tested using the test bench. The test bench requires JDK to build. **3. Install the built wheel with:** :: pip install /path/to/wheel **4. Test JPype with (optional):** :: python -m pytest If it fails... ~~~~~~~~~~~~~~ Most failures happen when setup.py is unable to find the JDK home directory which shouble be set in the enviroment variable ``JAVA_HOME``. If this happens, preform the following steps: 1. Identify the location of your systems JDK installation and explicitly passing it to setup.py. :: JAVA_HOME=/usr/lib/java/jdk1.8.0/ python -m build . 2. If that setup.py still fails please create an Issue `on github `__ and post the relevant logs. .. _below: Platform Specific requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JPype is known to work on Linux, OSX, and Windows. To make it easier to those who have not built CPython modules before here are some helpful tips for different machines. Debian/Ubuntu ::::::::::::: Debian/Ubuntu users will have to install ``g++`` and ``python-dev``. Use: sudo apt-get install g++ python3-dev Windows ::::::: CPython modules must be built with the same C++ compiler used to build Python. The tools listed below work for Python 3.5 to 3.8. Check with `Python dev guide `_ for the latest instructions. 1. Install your desired version of Python (3.5 or higher), e.g., `Miniconda `_ is a good choice for users not yet familiar with the language 2. For Python 3 series, Install either 2017 or 2019 Visual Studio. `Microsoft Visual Studio 2019 Community Edition `_ is known to work. From the Python developer page: When installing Visual Studio 2019, select the Python development workload and the optional Python native development tools component to obtain all of the necessary build tools. If you do not already have git installed, you can find git for Windows on the Individual components tab of the installer. When building for windows you must use the Visual Studio developer command prompt. Path requirements ----------------- On certain systems such as Windows 2016 Server, the JDK will not load properly despite JPype properly locating the JVM library. The work around for this issue is add the JRE bin directory to the system PATH. Apparently, the shared library requires dependencies which are located in the bin directory. If a JPype fails to load despite having the correct JAVA_HOME and system architecture, it may be this issue. Known Bugs/Limitations ---------------------- - Java classes outside of a package (in the ````) cannot be imported. - Because of lack of JVM support, you cannot shutdown the JVM and then restart it. Nor can you start more than one copy of the JVM. - Mixing 64 bit Python with 32 bit Java and vice versa crashes on import of the jpype module. jpype-1.6.0/doc/java/000077500000000000000000000000001501674766700143565ustar00rootroot00000000000000jpype-1.6.0/doc/java/__init__.py000066400000000000000000000000461501674766700164670ustar00rootroot00000000000000from . import lang from . import util jpype-1.6.0/doc/java/lang.py000066400000000000000000000016511501674766700156540ustar00rootroot00000000000000import jpype class Thread: """ Thread support for Python JPype adds methods to ``java.lang.Thread`` to interact with Python threads. These methods are all classmethods that act on the current Python thread. """ isAttached = jpype._jthread._JThread.isAttached attach = jpype._jthread._JThread.attach attachAsDaemon = jpype._jthread._JThread.attachAsDaemon detach = jpype._jthread._JThread.detach class AutoCloseable: """ Customizer for ``java.lang.AutoCloseable`` and ``java.io.Closeable`` This customizer adds support of the ``with`` operator to all Java classes that implement the Java ``AutoCloseable`` interface. Example: .. code-block:: python from java.nio.file import Files, Paths with Files.newInputStream(Paths.get("foo")) as fd: # operate on the input stream # Input stream closes at the end of the block. """ ... jpype-1.6.0/doc/java/util.py000066400000000000000000000173661501674766700157220ustar00rootroot00000000000000import jpype from typing import Union class Iterable: """ Customized interface for a container which can be iterated. JPype wraps ``java.lang.Iterable`` as the Python iterator interface. """ def __iter__(self): """ Iterate over the members on this collect. """ ... class Collection: """ Customized interface representing a collection of items. JPype wraps ``java.util.Collection`` as a Python collection. """ def __len__(self) -> int: """ Get the length of this collection. Use ``len(collection)`` to find the number of items in this collection. """ ... def __delitem__(self, item): """ Collections do not support remove by index. """ ... def __contains__(self, item) -> bool: """ Check if this collection contains this item. Use ``item in collection`` to check if the item is present. Args: item: is the item to check for. This must be a Java object or an object which can be automatically converted such as a string. Returns: bool: True if the item is in the collection. """ ... class List(Collection): """ Customized container holding items in a specified order. JPype customizes ``java.lang.List`` to be equivalent to a Python list. Java list fulfill the contract for ``collection.abc.MutableSequence``. """ def __getitem__(self, ndx): """ Access an item or set of items. Use ``list[idx]`` to access a single item or ``list[i0:i1]`` to obtain a slice of the list. Slices are views thus changing the view will alter the original list. Slice stepping is not supported for Java lists. """ ... def __setitem__(self, index: Union[int, slice], value): """ Set an item on the list. Use ``list[idx]=value`` to set a value on the list or ``list[i0:i1] = values`` to replace a section of a list with another list of values. """ ... def __delitem__(self, idx: Union[int, slice]): """ Delete an item by index. Use ``del list[idx]`` to remove ont itme from the list or ``del list[i0:i1]`` to remove a section of the list. """ ... def __reversed__(self): """ Obtain an iterator that walks the list in reverse order. Use ``reversed(list)`` to traverse a list backwards. """ ... def index(self, obj) -> int: """ Find the index that an item appears. Args: obj: A Java object or Python object which automatically converts to Java. Returns: int: The index where the item first appears in the list. Raises: ValueError: If the item is not on the list. """ ... def count(self, obj): """ Count the number of times an object appears in a list. Args: obj: A Java object or Python object which automatically converts to Java. Returns: int: The number of times this object appears. """ ... def insert(self, idx: int, obj): """ Insert an object at a specific position. Args: idx: The index to insert the item in front of. obj: The object to insert. Raises: TypeError: If the object cannot be converted to Java. """ ... def append(self, obj): """ Append an object to the list. Args: obj: The object to insert. Raises: TypeError: If the object cannot be converted to Java. """ ... def reverse(self): """ Reverse the order of the list in place. This is equivalent to ``java.util.Collections.reverse(list)``. """ def extend(self, lst): """ Extends a list by adding a set of elements to the end. Args: lst: A Sequence holding items to be appended. Raises: TypeError: If the list to be added cannot be converted to Java. """ ... def pop(self, idx=-1): """ Remove an item from the list. Args: idx (int, optional): Position to remove the item from. If not specified the item is removed from the end of the list. Returns: The item or raises if index is outside of the list. """ ... def __iadd__(self, obj): """ Add an items to the end of the list. Use ``list += obj`` to append one item. This is simply an alias for add. """ ... def __add__(self, obj): """ Combine two lists. Use ``list + seq`` to create a new list with additional members. This is only supported if the list can be cloned. """ ... def remove(self, obj): """ Remove an item from the list by finding the first instance that matches. This overrides the Java method to provide the Python remove. Use ``lst.remove_`` to obtain the Java remove method. Args: obj: Must be a Java object or Python object that can convert to Java automatically. Raises: ValueError: If the item is not present on the list. """ ... class Map: """ Customized container holding pairs of items like a dictionary. JPype customizes ``java.lang.List`` to be equivalent to a Python list. Java maps fulfill the contract for ``collection.abc.Mapping``. """ def __len__(self): """ Get the number of items in this map. Use ``len(map)`` to get the number of items in the map. """ ... def __iter__(self): """ Iterate the keys of the map. """ ... def __delitem__(self, i): """ Remove an item by its key. Raises: TypeError: If the key cannot be converted to Java. """ ... def __getitem__(self, ndx): """ Get a value by its key. Use ``map[key]`` to get the value associate with a key. Raises: KeyError: If the key is not found in the map or the key cannot be converted to Java. """ ... def __setitem__(self, key, value): """ Set a value associated with a key.. Use ``map[key]=value`` to set the value associate with a key. Raises: TypeError: If the key or value cannot be converted to Java. """ ... def items(self): """ Get a list of entries in the map. The map entries are customized to appear as tuples with two items. Maps can traversed as key value pairs using ``map.items()`` """ ... def keys(self) -> list: """ Get a list of keys for this map. Use ``map.keySet()`` to obtain the keys as Java views them. Returns: list: A Python list holding all of the items. """ ... def __contains__(self, item): """ Check if a key is in the map. Use ``item in map`` to verify if the map contains the item. This will return true whether on not the associated value is an object or None. Returns: True is the key is found. """ ... class Set(object): """ Customized Java Sets. Java sets only provide the ability to delete items. """ ... class Iterator: """ Customized Java Iterator. Java iterators act just like Python iterators for the purposed of list comprehensions and foreach loops. """ ... class Enumeration: """ Customized Java enumeration. Enumerations are used rarely in Java, but can be iterated like a Java iterable using Python. """ ... jpype-1.6.0/doc/logo.png000066400000000000000000005326301501674766700151140ustar00rootroot00000000000000‰PNG  IHDRτxΤϊsRGBΞιgAMA± όa pHYsttήfxΑiTXtXML:com.adobe.xmp 72 2 72 <vύΨIDATx^μύ΄nIvί‡ν/ί|οΛ^Ηι0 ƒΑΜ€€œ!4 R Q”l²₯%YΆ’΅Έd{q-jI²EY²([”,/R²HJM‘@ BΐƒΑ 09tN/τΛ7ίϋ₯λοΏλ|χΎΧέ;ΌžWο;§]αTΥήU§Ξ9­!*******ξ(΄‹YQQQQQQq‘*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQQQQq’*w ͺPQQq{㠏ιTΗHΦ½βWQQρ]‘u {EEΕmˆ‘ήΕυI\ΩΫ‰έi Η1™Δ\―§–qοΚ|¬-βο•^‚ώκΖ4~ϋι«ριη―Δ—^ ›ϋ1‘ΤŸτbyЍγ*χj{!N/ΟΕkσqοκBά½Φ‰ϋW—bq±π©¨¨ψ†¨ @EΕmfΉ_ΈΈΏφμεψGO_Žσϋq}g?6‡ ~Ο~[Ρn΅’Σ‰Π?֝xότR<,MΰΡ“ΛρΨρ΅ΈχX?ξ^Yˆ…Ϋ] N€ΰάΗŸ»ŸΥρ«OœKΩ‘ΌRhΊύN΄;νhIᙆŽV;FΤAΊψ΄bΎΫŽ3R ξ“BπΑ{NΔέ,~C'baND-“VTTAU**nΌpeύχžΏχ• ρτΥm&ΒΡm·’­£#a―Mr¬ιΈRbrΣ©ζd²·d.u;ˆsq«X=ο{οιxηγ'γΎc½hχJδ·74ΓτΧ―Ζ§~ϋ₯ψΝΛ—γά΅ΈΆ;RY€Πτ:ΡΣΡξΛΡkǁΚίBΛιΆlŸt€(μ–‘Λ·δ‡’4Vω;νvά΅?όΨ½ρΏϋύΔ™γ0©¨¨hP€ŠŠ·“ύˆμSΟΕϊ›OΕ•νa 4γEπ[ΨλTl―υήƒΆζΕ’œ¦2$ Η O%e^Ϊ‹ƒΝQ΄φ§±°Φ»;8{:~£§γCχ―Ε½g’Σ/όή((KO<·ΏωΕ ρΛ_1Ύφz\ϋς΅˜jΦg•>ε•½%SrK˜Jf·$ψ₯Yψ³ά1‘`₯ ιhΥ7+εoe`<‰‡N,Δ_ϊ‰ο‹Ώη„I+**Τ]ͺPQρΦas+β_ϋ…ΟΗΟιBτ$τΊ:˜κ•kίΊ–fόD“³%αΟ‚@k:‰ι(­Ρ4ϊΟlΗξCKΡ{a+¦»“ΨΎ{.:ς_Ύ°<΄ϊΰύρ‘χέ|μT¬šσλ‚―<»Ώφ/Ε?ψΪsρδ λ±~u/ΪΗ1u_ylΕδξyΝμE¨τς*κ€U°ΐ=Ωε>Π’“[SΕ₯Μ30Šqυe`8™ͺ~Ϋρ—?ώžψ'θήZQqg£*ov·#ώΒίωLόΧ/Η\―#Ω₯Ÿά·”q’ά!—‘.έb―€ŽΦD"PGοΙΝέ·h{彈ύiŒOτ’{~/FΛέ8ΈΈνSƒΈηδbόwί?φβ;'N2‡ω·ŽkΧΗρϋ|όάο<_όμΕΨ@ψͺ|] ςe¦~ 4[;“μG…έ˜¬tc|ΟΌ'ρνVΈη©, H ˜JC°’p«pλjΐ-`˜©>Xpψ/φCροͺ+U¨¨x+ υ―ύ½/ΕύιcaP„?μΫΆ3Π‹5υχj€~mVPΈ ‘7ψόzμ?Έ˜~γƒθl£«Y8w ¦ό­νq΄6F1^ιΕh{lX^κΗCχ¬Ε£ο8ίw±xŸ‚΅•~ΜΝwcՍώœ&ιƒL{΄ρΥs›ρίώ/Ζ/|α\\ΫڏΑ•QtORθ+-Ε‹ύIŒO+=₯ΟΜΎ-s²Ϊ‹–œΓ»δΟ>χϊ%ψ­0ϋΗΞ-• α?ΆΏ₯Ω­`¨ާqί±ωψΕζΗβψκwZΩί¨ @EΕ[€O|εZόΉΏυιθI°qΫLΒν›’τΦ[ε7²ƒnv;@fιΪύξ“›19ՏΦπ _ߊΡέ’ά ξμMc:Χρ“±‰–°–НŽbΊ‰ΙυaLX»£Yz VNΞΗΚ zJ¨+ρB'v·†qṍΨWϊέs.SϋκΠ›CJΐΑΩηΫ1FΨ+σ€e οžS^E+dtFvζΜ_iZψ·cJύ0ϋoI PžΩοpSα•ΔMξ[‘Υ`n lK ω?cοŠρ#–€ŠŠ;ίʐSQQρzBι?ωΤfΘΆo’GΠη=Lάι‡»έš Mx€ έGοΉ-¦ΜσhKxO—{1]P·—ΰξί‹‰„ν„6Ό­ˆ^Ό$Έ»ϊζ Όsύnt{ݘHYΈ~a'ž»°O~νšfύλρΔ 7β₯+;Vζ—ϋΡ₯\£©yΔjίylχ%Θ•6BΈ³>ŽƒΉvŒξρυ=ε]•αε~e―%ΑϬᏠΩoώ.οk€0θι0U/=)ϋ‹/Εt(wEΕŒͺTTΌΙψτ³ριηoΔ€]οL‡Ώ  ‹ Οcƒ±dΪXX[B;ε›„.3bIτn-hfέ׌ΏσςžφϊΘKκ£³ ±χθ’Wz;Θ Μ&Rb­ςomβ€Y»ψyζ<·—z1؞Δ@B½·ΠΎ„s_ξŽβ ΐ’½1ŠΙZίiYΕ‚Ϋ›#ΝφqΠSΌs;ΡSΎ¦σͺ „³ό/‘ο[b6‘„Φt( ŽGό­hόΑ_€•Ν€O^َ/\”rTQq£*o2~ξ‹b\φ7B¬˜πOΏ© ό|o]3|‹YxrHΛδ»ρέσ1||9φ>p,ΖwΝEk8±‚@Η?˜οzKυνλC ί–fζ‘Ω»%―ΏΙ»Αϋw‘d؏η)έvΤΙ%γ`Q3yžέgζcSΉ½“_hwTfσφɊψ’<\ΨUΉ+RX΅πγ(δEnό²μ*c[4.AcΏΥ―Ιk& ΰ½7žΖW/l§GEΕŠͺTTΌΙψέ‹W’#!‡$²@;*ΐΘΑGΈW½±ΛΫ›ϋ0Ωά'Yή/Ώ±@.Έ ΪΞXώš­·ΈίνΔδžωkζΟγvάτο0ΫF γΆ@7:Wχc2PΚƒόQ&Xͺo³"@zšε;Ο»)β‘t[»E› '_ή;0ΧQzς”=Κκ~ΞΏβNΧzΡ;ΏγγΟς'KRDX΄Bαgύ9`VΠU™y²Α«₯N8Ή~šC~φ'^s|ε§―W βΞύ ’’βMΒΦNΔ³W·B“ε”SE€ΕΐldY[Σu–ϊΫγ‰fΧ<Ϋ―fΛξΨ%-°Α-τ’° >XRb£±…ρό―_ρl;φ$ΜΦ–ΰηm{#”€εžWάLΖ}‚iβ„&ΛψΪEp˜•Vx³ίπ@ΚH‰Ηκ€ν]ޏΡιΉcζ―‘E) ΩΥK^ςW>κJ&ν((!¬H!ΰP`ήΙ<ν› καϊ~ρ¨¨Έ3Q€ŠŠ77wc{,‘ŽKB‹£\ΕΟTn‹Νi.Ω{vΛ3{άcG 7’ήΒY‡˜ ‰Jo (’ςAΘ#7;τοXˆ½ο_ž}χλ1έ‡Ώh€‘°›r<ογΝLAΏ,€½§c œαOψœόh4AIπΚϋX `„ΑŸςŒΕ‡"ά7o₯ςME?Οκi‰[Κo3t™U>ŠM][2σGκMς•‰"ОŽ”e&3― ²n“LUTάΑ {VTTΌIZhI`I€±|ΠΜb=ι &n –ρE{ΐ½ϋ}KF/χϋ fΒDω±ΟbΑΙaٟ³ln;4θΖπ]+±bt/οωYό)BZ4Vx?« Œ€Γ=yn hί’Βΰ%~ς~EΨ™ΔοB + :Η„]Jp*ΎπηΒξcKβΥMΕRNςe?δΟδΞ3ό)+Ei–ψ­¨9¬d蘒TΰΒ 偺tβ…τΥΠ(F'Ω‹PQqƒnXQQρ&aΔύΈH¨"Ȑυ)ΈdWˆ_η‹πg&Ž€“@εΎϋσ鏐φΜή΄:‰§gΚhψ›.ΓεŒ™hβ¬ή>=ή7ORκlkΖΜ=KρšΉO$¨ΩΔ79>ΠL_Š ‚\&ϊPTΌw%L ήΐνΟΖ%Œ€ς€²2^ν;«Ρ—T'έ%ήτ7η½ωf?Ρ‰–΄yΟΏŸυGσQ$+0E軜{μπCωaohˆœ˜cεEˆ[ιύ Њ㬌TTάΑ¨ @EΕ›n3ϋχΜZ‚ΐΟι ξŒ|ΨμJΐ³›_Ώ¦Ζ‚ˆΠX˜!Τ γ`ωE‚x’A"+…­ό}―<₯«i<Γ†―θ¦+݈έI>ΘL›•„hOvδ‰₯#«2ήSdωρΜ?O¦„ώT΄άbΰ+;ά"h½ΌoaΟς~…mΏη"₯ΐϋDΛ*ΓTρΌ8pEGQp)_Ι«<¨~Έu1αΆu†Ώό¦δƒ0ΡPώ’μ])RζaΎ:Ž€w0pΟ)?~Ημέ³^ΊΆ0— lk&-g‹—03/KήyΛ@v„Βzόφrsίί|ψ#`ΝΑ*Έ‰? XψOΉ!‘έ~y/Ζkύθ0{ίϋύν‹|―_ό%Ό½ήλ#Η…?;ώx‘v…σ1^:Ÿξζ8†gόΖ?^ΰs0§r Π™ρ£@Ε#o0p:ώ₯bΰz€ίο˜Δε:ΒlJι!Ύ7Rω‘LN¦H@vί^‘Ž$ΫQ"?σž{Σ£’βFU**ήdόΨ»Οxζάμ@€ZHaχlυPhπά}”žΕ#Ω (‘ΗπhwcιiΞrΆ" d%ΰψm="O|:Δ•Αͺ<’'aΝ-ΎοΥ·’{y?ΖΌ«Ÿ¬ °»ώšŸWxγߎ„²’&―|+ΐ_Ώο½7ςBΕΗΝA”ίf―ύΕAνΧ“2,³ύΥ Bή ŽΚνςΊ\’£nδοG• |Η£^‘—Ώ?H$mΐς}ksρCχ­€GEΕ †›ŠŠŠ7οψxœXμΗ€Μj½$.Σ»ϊTΜθ±4³} u›ouΩΙυVܐ°ξ?}9Ξgγΰ3Ώ[ΏϊΩΨόΚ–$\Ϋœ~B‚4˜ #<Ύψα&œΗπNbού«~f";oλ;XθΔΎ$ΘΎξέΛN^y₯/Ουh–Ο[ό¦ς¬.LηΊ²+Ž2ΰΫε~΄Ο†?{; gς3€¬ά¨|ΤΑΎ …ϋ~Ύΐ£ƒmΌΘHΉΘzhψP‡%ž%”*ε₯μ~§‚@š{RώΔχݝΎ½**ξhT ’βMΖ±΅n|πα“1fw=OB+gΤeΖƒεϋ©fΦv#δ˜’‹­―߈‡/όWρΗ)|ι?qόφΰΏ‹Ώ°ϊŠ{/ώ;RΎ*…B3uρυӍ°o₯—Μ%ͺP%™Ρ—‚±Ϊ‹½G—c‚€ισΨ+πε#AŠΛ…±ί>Ψ"sb&¦γ3σώS>Δ{$Έ‘')S$I[‹_ζA΄<ς¨4ψžξ+ s±ύβAάx~/Ά―wcw(EeͺςΊ£jPΰI9)u%·7’d” 8WηΪρη>p? VTάρ¨ @EΕ[€ΰΑάιΟμTςΛ“T )T R Γ3bοΞ$&W[±΄ύ·βc Ÿ‹UΝΪε‹WβΓ'NΖΩX‹Ώ<=κ‰V¬ξώ\Œ/"ΕAˆδcfŒ Dѐ'‚`š—η%¨'kyί~*E {mύkϋ9ƒŸο*Πγ™πŽcω­„-.οII˜ζ«‚Y1@Ι`ŸAIΔIϊ„C,PJ8(£fζδΣo<$P ΗπeΉρ?³ηώ­ψίξό»ρψ§ώ•x§ώρ‘―ύr ŸΉϊC 1T4ΰ€Δ‡Ίu–ƒoμ'ρΡGNΕΩcΣVTάι¨ @EΕ[€ύΐ=qΟ±ω±”ξοί³χ=svΚλΐζέϋ|@Η―ΎπRΌσΔΉψxηΡψ€ΕvΔ—7·EΏ±pwόΩΞ}qκΨ‹1~ρ’€«Β‘₯HΏ"ωΐ -”αο₯vY‘”%mοζW ¨|ί~Μ§zY‰νt΅/‘ί‹ΞϊΘ«,( .οGχΚΠ«ϋwΝϋΥΎήΗΰ%{³² &·2\Ζ\ i]/εV L3ˆΉ/ύΝψ£όNό£₯nόεφrό»g:ρ‰/ύbόη Ώ¦ύ—’ϋωίÞΛIφQP Όͺ@Šπ–T?ι0ρΏωΑǜLEEEU**ήœ8ή‰ŸϊΠύ1ζΕ>Jοf—°’πςwτ5φ QΙ†8-LŸ’ŒžΔ<ώ±8±Μ#οŽψΰc1xδϋβίZ9έƒKš [¦P?–Σ[L˜ItŠ3ω S¦7"9E6™cE οUάcάƒNL—»ΡΉ°½Q잝 οΰ‹ΉΫί/ω^ςC°Γ 4΄Ιι!¨Yςg$3vθ¬4΄cτάFτζ%ώΝΈW:Η)Η_4Ξ΅ζβ„βŠ_ˆιΉ±Ώ:8‘Ύ˜ω³’`Ύβ©ς8Y•wwo?ωΨιψΠΓR–***ŒͺTTΌEψُ<kέNŒ˜αKpIdIψIpJΈ±αΞί –bΐ‡rΪs=)ϋq₯½Ÿ~ςw#ΦVΝγ+ά{ο-D|ϊ©ˆ―~6&£νθν_”P•LŸ63jξί#δ5σχwϊ™'K2ς|<―ευkƒ™ύ‹ΖJ€άΜΘω&Kό^ž΄½AΠτ7F1ΧŒξ9ηοω· Έ-6J#y-|ε΄]¦_2€Γ@Ψ›΄((%ͺ§-- ·υDόεΣwΕCοϋHΔ‡Ώ?β‘Ηγ7―n8κvοxό[ούΙΈka#ZΧ.‹§ζ΅Επ!=—ι['(ςšWΉε}§γWTT$θξoΎο±₯ψροΏ7Ζ;šΕ"΄$ΐόH²AχΣΊΦ±=\ΆΰΌνΖΙIΞ†]]τwώαή³ρΧ'WβΣ­a¬υ‡~‡ b2αλgης̎-‘’ /Ϊ_έγφ€δSΎ–ΰΠW‚Ύ£xmΝϊ§Ηz1eΆ―Όψέμπ—ݏτ――<‹>W0ΜBy`Ω_QΘ ω¬ΝLέy#°δe:mΗβώ—γqϊ?/-ηͺxΔoq»CxδοŠ_ξΙxξ`3ΗΧγ`K³V†ͺΗΒ^V#vχΗρΗί{6ή{Ÿκͺ’’b†ͺTTΌ…ψ_τχΕ’fΦΘD„b‡™Ώs‡{θ|'εw„ζ4†ύSΡήοΔόp½Άfύ‚ζϊ.ΖgΪϋρ’„nWSψƒƒ…άŒ‡€η, l‘({Υ~žυsΘΛΟΨ—0?΄ΗR@†§ΎχΟl |εΎ^©PšΛ*±λG‰κ“²G:ζ…]žδD)[0—°)D4ӝIάέΏΏ8έ‰O/ΖεΛšΒbskuœY[_Ω>k½~μNύ&ΓΦx’ϊO”?ι(?£Ι$Ξ¬βίύψϋ·’’βU¨¨x ρύ­Δώΐ}±Ώ)α*ωΗψ_±6³ιέ‘lt¦Ρ?ρ`œΫκΔχ?ί“χ²7™ιŸ9t؊Η$o˜‰pG\φrF / y u„­"Z Λβ[šuC?3yίΓg@ €°!px2ίώηWϊ*[–Ϋά ]9|ΏΦζ―?Jό•‹β‘BΈΈΜΓ’…Ώ€žψ)χρΊ“ψοφ_ˆ8wΓyˆ/mΗιE[[νΨοW>—X1ΰφ‰˜°@:SμΗρ―ώψ»cmYρ***nBνo1ώόOΌ/NhΆ?ΤL6ΆeΆjπΝz^²Γό™ωΈπγρ7ΖΧγβ³OH1ˆψβΕςBΌλδ{βg{§’/α7ήΫΘ§ΌΌk@q}Ο]^~/>ΛόeVΞ29ΰSΊή|hAŽ[Tή(8―Ca|Χ·&ΔoŠrb:`,?o,„Y²4 fkQ~ςΛezy ΄υχFΕ’Ο–ςqο‰XΎvΕό`μnξη'pž[^€ƒ-³άNμΖ}H,Ώλ?Ž―Ύπ‘ψ‘kWγ/ΟνΔ_Ÿ;ˆΝλ,•ΟYπ’Ίβ #}’†gς(Μτρ°x°6Č ΠK‡πύzρ™²”ΐAό₯uŠέKμr”•TŽƒ^Gz…£P@‚ΰg9€ΫΚ›omΐ[Šyb΅β Χ•B1Žα4žιΔE^h ^ΓΛWαθ“­XϊΡ0ζ’Πη= ή ˜ŠRg±#9<Ύ·Ÿωι+**^κτΐŠŠŠΫ/›ΔŸώ~<½Ύs+oj£‡ϊΣ·Ϋ|_9™jߎύ^¬ύ™˜_T¬υώg ώ•Ψ{θߌΦϋNFkYBRΒΡqΔ;Νβ;ΔuΛ+ „JˆZz£8X„ϋցŸ₯Χ‘{τGhcQς…Πζ©”Ψx£X²©Π€kSζ!~('$鍊©˜·Ϊ½˜ϋΝODgϋΏŠ>pO b{;»ρ_~v/:σ?Η~ŸŠ>{ϊPPXy V2XY`5’%fγŸώHόΨ;‹ ’’β‘*·ώΑ§.şΟ5¦K½θJΈς6ΏιxS(AΙνI@Vή„7ΩjΕώΣWb°:퇣u’Ÿ3jfηΜΌ’²žq Ά#„‘žΦt’ΰηώ?›φΈqŸ1fξΔρ žΫ-‘&­7ΰ5²^~δ±­™Ήu yBKxόPx μ{“ΓU ΘX-ψΪ•X~ζߎƒΉILΪίW&ο‹ώ#0ϊgTςsyΈΕ!Ÿ¬•ΚΉ£ΌύμύρΟόΘƒς¬¨¨ψf¨ @EΕνυΘΏτί|!ώΣO|5ζV)7ΎΘ;IT°(3ηί P”‘„(‚q­ν•όΰ} b2S ιMx’΅’E#H§άάχ ΛχΌ΅ΟB‘!RO‚<¦νΌ%€ΰe}ˆ#…bͺ "£Se(² ‘_1Μaz•‰2μŒDӊή3λ±½»ν3χDgIασβ-!ίβ‹>RΪάF€9œeξL&ρη~θψώψdή***Ύ)ͺPQqbw;βŸϊ+ΏΏρό•X˜οKΈ2kNΙζϋε†ήΌ#aΚfγSVψ8ΙuŸNξ― JΘ"ό}/ό8ντn’›ψ΅EΤσ!"ϋ|ΑIΗςΊε€N¬…NΎ‘c±λW³μοΟ #`ΝI&‚9‹΄ΦlΩ³mξύ³‘O‚ΦΒ?εoΣ+ ²B£#?¬W'ρ@i“ P&$¦ΝŸό₯δ.~E9 οI]όš¨„‘”— ρQ!Ώ΄HαςŽΝόΫ*#J J@«―₯±§²=~z1ώ«?σϋͺπ―¨ψ6Q€ŠŠΫ?0ΑŸω‘XΤ,Έ7τw/·Kϊ€h’^`G¨#)ΉοΟ} Gd+r…ΐqΘμ›²rͺξ?υ&x‘!θ=|½ΐ6fύ1f‡’{%BŒΩΠ|ς·Z(£gdϊςΣ„–p’'?^₯θD‡χ ΤUΖΆάήT¨ΓΚIO΄ R–zΡ^Ρ!:Ύ‘ΰχ(|<‰3KύψKψŸX­Ώ’βΫ…zREEΕνŠιψ‹τύΡιwsΦ-9g%€YΏΠ’&ΐ—€p λ"`-h Γ(‡Aχνeν²TίB ˜Θ„V‡xΨΟχˆ/А:μ¬(Θί‚}’ώ²ε­ΩVδιpό‰ίΈ Ρίοΐήνx“£o[°Ωω _τ½θx*v£Ρ$ζEΧώΜ‡γ±{λΛ~**ΎT ’β6Η?υŒώƒ'bΈ»+aʌ[‚^B<…©"ςXBY6‡₯8Ξ3Βα‚ {2{GF+΁„=]fαΗ¬_‚\nωΪ„¦-…‘Ν㇄'·€•_.d>rs_ρω–ε€£~gC&{ˆ3{ΤP @[³zžιoΝu£5@ΠΜSDq)NGŠΠρg4~πk0―¨¨ψP€ŠŠΫ’–ςΟό@τŸω¬gθάg7½‚μF@˜κ'υ_ SvXφΌmΘGJ?&σφV"δΟ}ΈLςυ½x‘XtδF9HA^eFŸ ρ`d―™Ϋ€:1ΐΜςI6τ)lΚΖE–ϋ+ έθΜu|Ÿ[φ4τYνP^‰«F¬Z(ςυ} >φξ“Ž[QQρ‘*oΌxi'._Έ{ŸόŸ<Γ>θJ82cξ „­σ‹τ•Ξ]δq†η-~#Ν<#μΉΐΜήρΖ βh{cJBNΖΣ…Δη€B'ςS„I§t‹Ώω*œe~x³"al*T\? Π Jfύ~‹‘+’‰Σp8‰Ώς'>?ϋ‘³Ž[QQρ£*o|ειKΡλ-ΗΑh?vλ£½'O^ά3BΒ–C°€•=gηΈΎ Ώ°§ΨέρSς!½ψ+Κ€EΉόxΧΏ-’εύR=ό "Ώ4ίδ™’iC<&ΜΫ `lvβšd3ΕΔy`χ?χόρ#.2Sι₯τL₯$όίφρ'>P…EΕλχχŠŠŠΫΏϊ©’Ϋ;γ˜”€½Ψϋτ/E{ԎφA‹Π3iΉ™Ι7σtΛΨbσlςdYέQΨΆ[?n,xs&ˆΒς’Ωλ„@ηώB.Xhϋ™uλπ?=&lα«3«(~,‘Cyϋ ‘2εޟ(W*Χ_ω™ˆ?υ‘ϋJXEEΕw‹ͺTTΌ πλΏ{%sΛΡι¬F§·“νυΨϋΜ'’΅%Ύ5‰KτͺG#HYξGxZœ¦δ5ΨoρͺSG’ΩΒXΛϊΨ;¬*ΨΦ ':<±¦wFΔ°Ζ ov#Lδ€Φ›χ₯ΐyγήQTπΛ€QαΔνύ‡γItχ―ώμΖΟ~°Ξό+*^OT ’βm€νύΠμI³εy͜—£;8γ+±ϋ΅OFgGέxc­}‰Τ Χ=ΓrŒ&šyηLŸΫLΓsuœ)‚sφ-;ςΧΔi4³ό2­7lpjΒd/A7Αϊ,θ%πƒ‘€θοM}VL2¦sΡ0-·xΞΎΣŠηŸόpότϋο*―ͺPQq›γΉs±½7ŠvwNBr!ΪV–’7*ΖΧΟΗή3ŸŽΞ€-”€:6G›)c)šqσ½€})ΐ-MΛ‘½€Gχ$ω5#G@Hk@`ƒ!”ςΩ’\Gγ6€½πj h7‘™”™υ“Έχ'&ΔΐD 39€Έμo cmρ7WΏ?>ώύu·EŁͺTTάζψέ―^‰¨ο›wbΪZτJ@«½])Γ+/J ψ])|8H‚u€C³ηjV½+Ύ.`w"E@wo¬»όςάΛ7$xY–GΛΛφψκδ‰:h€:f±Ύš‰\7[½<σ=8ΕSvvϋΫE!ΰ?-ε­-Α“Ψ½1Œ‡–ηγοόK(>πP}ΞΏ’βBU**ns<}i+s|OΒSΒ²Σ]Œ)νΞ‚””€Σ1Ίς\μ>ω;Ρκ, (9Ϋ–`έ?ˆ©‚B€2ΠΪ–ˆή—Πε;ΜΖΩLˆPFHkX˜Ά{^)°B‘C1gGƒΖνΗϋϊ·ΎŠ—xišΞ‚_™μ柭.°*±R2ιΖ8Ά7‡ρ‘‡ΕΟύKGλώ**ήPT ’β6Ηό• 1\3~~šνnGJΐ’λ’„θ‚ό€ ΞΔψϊK±σ΅ΟH ˜“Ώ%dπΜΆyΖΎ-!{ A?•Ό"πωŸŽfΟΒΉέλΗΕρ ράφσ €tΎS 0ψρ œ>  »š΅ΏόkΟϊέΜϊy„Πo l‰‡!»•9ό@ς΄3‰’­aόΜ‡ΞΖΟύˍ3'ψ@EEΕ‰ͺTTάζX—`δ#8έ~Χ‚³QXh΅ΩΘJΐ|tP6Ÿ―|.•hυc ή/BΐÐυyδΉ”‚)~« όx@pοςAμνη£½ρΙΨzb3Σσͺ’·W€XtΊύΈςw?;ΏςοǍ/άπ‹{HΗΒ_t(@ΤΑ›οˆ“oUμNbΌ?ŽαdΖOΏ7ώκ_ψpτŽRQQρ£*·16Ά&ρβ•ν`.ήδ‡s<ŸF ΰΕ9큄’ό₯p;`p6Ζ[OΔφη?)IR pˆ-°QXξ‡1‚\n–ΉοοΗξڝΈώ»_ŽNo#¦­‡bώžE/ΡΟfς:_‚Ϋ{ˆƒ2Ρ’‚²ΨŽΟVŠ”Ρ±±°S””xψI§'sO¦Κ6Nb₯׎ΏϊΟώHόk?σnrVQQρ&‘*·16χ‡±£²wκw4›Χ‘YJΧΡανyΊ¬X XŠξόC1]ŽΝίύΝξΥΕQ •@nO$ΗήaΝώ-Δy‡€τtύη?Σι‰˜{η?½ž„χ8(:DΗwΨΥΐϋP †ΓXxμ‡χ±_Ύ£ΝΡL1ΰv+ ΈΫγ©ς€)Θϋ6Ή1ΎθίΥ›‹?ϊαϊŒEΕ›ͺTTάΖψμ“7’Γξ}„΄ŽΆδf»έ–Lׁ2ΐ»ς;RψkN~‹ _ˆξά}Rbσχ~!¦[ŠΟύy–ίΕ¦%aΜΛx<λ·`—Bΐc‚»ϋ1ή’qό§βďήR<˜ρ#ηI3Wΰ#α>iΕΑή8Φήs2ΪχόdLwΦb΄ΎoE!ďΗωšƒ ‡;βΧlBDΉ΄7^،Ηe­¨¨xsQ€ŠŠΫ_ψϊ₯θτ$Όœ{ͺ‹]ί `ΏΕf@)Ύ-Π•ΠκΔAKα9Ω₯ το‰Ξ`.ΆΎτ?ΖψhυΊVψω…<aIuήτΗ@Ѝcξ‘ˆcz =ΉQ|λ@Β^ΦFih±Š€R’c*αήNβΜΗΞFοΑϋ£;•"ΑΣ|£@3~”Ά‰–υkaρΛ;1Ί>ŒγΗζβύΘύ.kEEΕ› )βŒ·#ώ‰ϋ—γΣO^ŽωΎ„;ΛώšI0»–wπ8|b"Ž›εύ)’χ`(ωcΊ―™ϊzŒwDοΨ‡cώρ»δ―ψκVσ΅F!ž]>\tn9@ΐ>Β‰’ifLyА μmι)<]ΰΰΕ CcηΔΛwΖ1ΌΈ£|)ΟR>ώΞςGγί{ ŠŠŠŠ7ξί·φ‡Ώσςυθσ Ώ„±ίœΗ3σ‹έ8˜Σμ7[‚»#‘Γ u4Λo΅z:ϊ:ΡjΟG»{<Ί‹χΔxσwcϋ³_ι†„v—χ H*³"€2 aΝγ‚mΝζ=S—B™O (!dφ―δs»hdϊ)‚‘^6Δ Ÿψ⃣X°α`sϋ/mYΉ`/ΓO}δ‘*ό+*ήBT ’β6ΕgžΊ›šνηc|ς@ «Ηr=ν˜λKpΥΡF!˜cω_α.@D’½άΠY‹ξΰξ8θœ‹ν―ύjμ~υ m3ŠLΟ―.³vn H¨{·Ώυ-ω7 Cs‹ ”T)?ΪTώΖηΆcx~KΚi‰•ψώ…Ÿ}―Β+**ή*T ’β6Ε/φΕ ψ’άΧμΰψ Χί%”'ϊޜ·ι’fώ θKCθJΘϊ逫RΪRΌ°έή=ѝ_‰ρΦΣ±ύ…_‰α³ΟΔ„/ ±~Ο^‚"΄IΓOΘDρΑfA ξΠμήv ^όγχί‡ΨHϋOmΔdkδΗQ4Ζ’?{j)~θ}§^QQρΦ *·#$x?ρ™—bžΗθ4λ>Ψ’π_ξΖDButR‚]ξΙ\;Ϊ;ΘΔ“Εž&όyk /αߟλF§§πnOBYτνΌΠκ,i"zsg%1ΌφDμ=ριΨύςWcόςΥ8`鞏%ό₯\HρΐΟ/ξQ~όxί΄Sž`Υ άJΘηε–Ι>…ΡK[±χελΉg"M€¬ΘΎ»=Š?χGκ3o5κ&ΐŠŠΫŸωβ΅ψcρbu(ǚfρ»“˜¬uc:hΗχύ₯t5ϋοE{}ϋ/Fοό_ι;Ί{>/νz‡ώΈ%εA,xΛίpΘ |Ήy `$α½'!.ΊιŽ„σtΏΏΣήJt—E§3Ρ“baΑqιλ˜ΝςuRP$Ψ™ιϋ½-„7²’m[qY\θυ:ρΩ•ε:¨¨x+Q€ŠŠΫΞτ©ψoώήΧbi©γ}?zίΏ΄Γ»1]–Π—ποIΘο½5:|ϊWΒ~ΪmEΉνž•ΰž¦Μφ—―οΗXέ|"Owg³_*š™ΫΤ1ε©ž"ΐ£|Sbχ4›_‰ΦtΥ3~Α ^δ6_ρCψ“–!g‹ΥL”…·Ω£ {·ί‰Μέύqόρ?ε_Ρ©’’β­BιΚ· ¦¨Ώψ;/Ĝ„νΛιτ^+F§ΡΉ6ŠΦ|ΗoηΎk9‚₯ΦAt/οyŸΐώcΛΡέ–B ϊρ=σΑχ|–zΡ[Π±Ϊχ€=o t°7`IB{Yζš&ψΗεwFα'£ΣΧΡ;¦‚[š½k&ίδ;˜Ωϋθh‘—χπZb?…ΐ¬_4’νΞεΡWxe‘ŸNόΉ?όΞ,hEEΕ[ŠͺTTάfψGŸ»7χ’sBΒ—χ,‘λ˜.t5ΫΕ”―χm£{a/ŸΨ™ΔPΒΎ/χΑb'φί³"ΊqΔΦΨ_έ·£΅žgΰ=^Δ-&ζ ζΎ„τ`^Β~A|ΩO Dλ€ΒQNHΠ―Θ_ΒݚωΠ_¦…=B]ώE¬tό$B'z‹ϊRR:Κ›Θ½BΠaω_΄οΌ5>τžγ.gEEΕ[‹ͺTTάfψ›Ÿx*ζxžŸΎAΗ{ΪΡ»Έkaή=ΏΝζGgΡ}~ΫΛόγw,ΕθΤ ¦ΔS ΫΡΩω˜Ϊ†μΒ˝8XΠ1”β°"EΰΔ|τtt‹€ξ΄’7׎ΎŒξΌ„χb/:2™ασχρΫΌg€Ω>ώRψ^βgi_~ώφ@Ό{2%θyΡo)μ)l@όαAμn γg>ϊͺ¨¨Έ P€ŠŠΫΓύˆOνbt7ΖωhŸπ˜Ωυε½žΔδ> ου±W¦χ,Dϋ~L%ΌΏy5F/EHΠ·ZΡΉΈ»ο_³ΰk£8 ―Ii8ޏιή$¦ΠΝK`Ÿμ­,σk4θivίUz})Μδ»μ]fσ ρFθΛD)@πϋήΎhQz( Δ/ίr²Ρ‘Rq …bͺόv§­ψγ?ρP)iEEΕ[ͺTTάFψ΅/Ό—ΟoE§§ΉΉ^ΣΫέ™DoCB|₯λχλο?ΆθM­£Ύs9Ζχ-xƒ^χ™-Νθυ۟ϊΡΐƒs±χύ«ΡΉ°›β‘’ΰo ΄Κf@ λρ\+&π­γsΎ%pΐ>–όΉU aΟ~ώœωΙωθ―bN΄)ƒ“sΎ·ίχuHQh­Š‘/AΟ‹‰X‰π‹”Ξp:ύΐ]qJyͺ¨¨Έ=P€ŠŠΫσ_‹ΑH΄Υβα:?λΟ½ώ ϋIˆs;ΰ`NŠ€qχ^D[ΚΑ䬔€“ύΌ%pc?&wΝ‹”—σH!DοΉνŸx3αψΔ Ί›γhK‰8t½ΰϊ0»—ξp A>=>Hχ-ΖTΌϋΌμ‡4‡!χ|œžήή4¦χ.Δτα%― LξYπΚ/$ )'€Λ ·1†ΓIό΄>ϋ_Qq;‘*· 6·βSŸz)ϊ<χ―™?ίΤηΉΡύσ1^λω^Ώύ§¬»ΰϋΌο oœ’ΐΎ[‚yob…aΊ(! ω₯]ίσΪ`ή! ψΓ{ηbΌάΝΧ —M†“V”μqΡ²‘ώ„%| oVΈ}0>rΠή R(D?>=ˆι‰~ζ ?šΖθX/σ)Γ›υ[YΔGπήRŠŠŠΫU¨¨ΈMπ«ΏύRllύΒMάύN}ή΄r#HΩ폀nσ`Νζyξ?nhJ­„;@³ϋ6ί ΰΖΝ½ύŽ„·{:τπα΅ΒV&䔝— ‘ L4󝙏)J‚h§σmΏβ—' Ζϊ¬ ί³­Ή%τχ?x<‚Ϋ lTžx7ΌΩˆΘcˆ1ΚόŽ€¨όπχί«K"¬¨¨Έmΐ°PQQqΰο~βλ~VŸχοϋk{L ι‘rχ.νω@^ΕΫΊ’šθΨpχβNL$τ-μ₯0 Ψ[lόΣlΟ γΡΓ‹VŸ½ξο 4_$ e[Ό[€Yg}>ώΣοδκΒΕ½?Άμ7KώμΈ΄oebξΖ΄Υ‰ξ•aL₯τΈU!Ε£Mδc_ε‰WώΜ¨›**n7T ’β6ΐξξA|κ©+ΡοJπާ~ί>/γ;\φŒ^Βϊ€έφ/mK¬Λ.ΩΚgzΫ—‡ž™σR?šwAaŠΣκz2ΦDœ%ρ‹1½o!ϊOmEχε½h³QPŠ@›Ω½„΄(^9Μ-)½ »R4z1^μΖxI‚[{@Ψ; ψνΰΝ€β#₯Ϋ“3:–ΈUζ’΄5γŸnObe‘π‡ξΛόTTTά6¨ @EΕm€_β…Έ±;r‡<ΰM~g)lΚλ\έΟϋσ’ͺύ šύ―HΒο*@‚ΩφDΟc’Gί;“΅~τΎΊaEΑΒ^ΗDω€™Ύ‚‰&τμ/πmn9@·>ŠΡ™9)’cWΏ”oδΐΔ“Π½’Yώι9σπ·εηυ:J“Η •ε?Δc_ΚΑΎξX¬ού―¨ΈνP{eEΕm€ΏϋkΟEΏΥςζΎΦ@Βα,‘έ}yίΟπ#‘ύΐ’³δ9KλV $Ό»šΥΗφ(o(jχϊ(¦g$ŒEί;·«Ή„΄x΅€Xp~Ύ)ξζοF–ϋQΨl¨΄ΰΕ$^YΚ%}:›όΰ}²oΕΑόxΫ ”―F°r‘ΩW{ˆ6šNγO~τQΩ***n7T ’β-Ζώ^Δ'?χR 4‘fχŸ₯Czjf žjζήΪ“p֟gόYŠG!`iΊάυ1xj;ΪΫ<&™"oNbtv!Ί—₯@π!β.cξΉνθέ؏6Ÿψkϋ6οS mώ[1αɁ•nAτ$μM‡ΐ7n ύ"OτyO€·"xP Γdx§Ξ,ΔόP]ώ―¨ΈQ€ŠŠ·θχΞΕ•σΌW³tΥΓSn^§Λϋ;χΎεΡ<>„Pf¦ή•?oηγ;sOnω>>οom|?¨ΩώA}e/ύΕ‚φ΄―Ž’χΒnt$€;ςχ7HKξώS1Yξδ-ξχσΔ€ςΒD”ƒŠŠςΒ-ƒφυQ Oτmο(e@―μνγΟόδ;c0O***n7T ’β-ΖίώΕ'½όίf™ιΙ_‚—WσN'Π;,έK+œ§Yͺο_ُΎfχ|'Έm“E l)s_έ°rΠέ›DŸ7J©`™Ÿ[ ύ )(rσ"!fπΦA<Ύ7χŒfώm…•·rμΰ~ΑΙΌ΅θ,³ΫŸ'”§ʁeKaƒn'ώιϊ忊ŠΫU¨¨x ±-AωΙO½Νΰ§Μ¬§ώ’,΄xmο+ΟρkΖν{σR|]~νmE0ηΎ>³}Σ Ψ%ΘΩ0aΆ―=γρΏ ξ i ¨Y)@ι`#‘fϊάηH‘ΰΎ>/κ^ω©ήΠΩ’2ρŽ…<› [WEΗmˆ.ŠhX%P>ΨΏΰW+έ½ρ$>φώ³qΧ©¬¨¨ΈQ€ŠŠ·7^ˆν±„¬„~G‚wΚ#t­ω5Ϊήέο}όκωKΘO%x½ΥNΒ½#Οm6σ΅ζdoό“@žzπ¸o/Ά~\P$ώ: iΩ>OhζίG™I—Y½ήA`α/eaΟLΏK\ε‹\’Lπα)4RF4ρ‘ώΕ?σύ =„τ·4©¨]²’β­ΒŸώίR|φsba΅οφ,3«η>ύ ܐ€NΠ]s‡ύ„ΩΉ„TwΌ'!Μξ{H˜νK(³#Ÿ―Ž6'~£J«~ζΏθžΕ{Ν^vn1(.³|V b©η§ P*xΧϋx!ΠDt z6ϊ›Δ‘’Α¦Ew#ήχΣρwγŸ$…š‘Ζ GEEΕ[ŽͺTTΌEΈ|u/~ψOώc°ΤΟΧοͺ'ς?²˜ΓnΘV:iΣSǞΎGœXΚWρΜKNί}Ό―Ωχ0­q¬ο%½„ψςR'ΦΪ±°ΠΡΖfπΌώ²άσ< )œQ”ˆŸ@ „‘DπΊ_ω‘Pψ‘D9xαΪΎ„½”€ήβ"ϊDloLβΚ΅QμL§ρΟόΜ{γϋΡ{co€ό­*”e‹ŠŠŠΫ U¨¨x‹πω/ΎΩίώlœ>1χŸθΔRwσƒvάw²ƒιN[κΪ}Ο±NΜ·‡±8Χ‰Ύδο½ oOφΌAΩ΄•wγ\8@xΰž*Œo 43oοη0¦xμ/`ύ€Ϋ ψ” X‘d··ό‘υ‹$πργ3ÍΞξ0v₯$Lζ :™R˜[\Šξ`Nρ»κ ζcaeEzXXY΅χ;Ί½Aτ—P"***^OT ’β ΒΑt{7$υΖ±·y=&ΓaLφwbwS~κv½ƒ=ΝΔ™;7³όœΪ!“»ΘO:*&2 (ωlΩ^,ιΨ‹hθ „΄iaO€b*ΐίπG°Λ«έ斁I“OβϊK(6ωc³›γ†>KΕο, _$œΚΚN’™&eI(”’Υ5ΉΊR"£gΕ Λ'OΛθΕ@ŠEg°ˊŠŠo‚ͺTT|‡nݐPߍέυk1νǞͽnoΔXΒΎ…-χξm—Dσ*oόŠ€³ˆC ΪxǁΔO!™BάΑ34^6ΎόI›ψψz4jγӈt»½jieΖδ_–œΈ|Y˜/Σ$―Δ…‡γΰ¬·ŸIMƒ•“νΔρ­Bγ0όTwπ,n¦rΘ3σλ$Šu»΄v,Ϊέ’τgζΒq™U¨¨xMŒ%ά―K ―Ηhg#½fπ{šΑ{ω]‚ ‘i…@ς;Y4"•^~!φ΅ψ*A†]GΫA_<šaΘ+Ž.ϊBg’—t¦Ÿo/θ7‹‚tΈ ‰ύlΙλι[H3ΒDs+!W TviV“Ι8–Wb°²&Εഏ…§’;X*±**ή^¨ @Εχ$˜Ιο\Ώϋ›ΧbσωΨΎ~E2LΒABΎέξXθ·;έhwεφ ΏH‡έ!οwίδsΤ‘B#OεœΑ‡\ŽίX±:Σ`Εΰ±d Ί,莘7§TRΗ°6¬γο βC=ˆΥγ/ΒΆIυΓδΐΡΈuΚa¦ψΩ¦€Ζ.Ϋ,εΔQšΠδ%™E:΄ +ώςΛΊI_P’ ύ²VHW4(Δ5Loφψ½ŠiΕ…€Ό±jP”ƒιxΛ'OΕβρΣ±tόŒ”‚31X>H·7ͺPρΆΗdo;vo\Žν«”°χp―–Π·x`τnΐΰ―£c%@ώ+£Ό{CšΆ‚›„NRΎ*Jδl 8δ‘"»qΝΠδIfΓ7}κF°½Ά™΄%KθβeΜ,ί9˜;₯& ιΊ.ρkLS/ωΊI ΛžΞ#y*Αx¦Αθ0zΗΣΙfΈΜΜΘεِγgSξI—φYM‰(½Š…HGΌΚiF{ δ9‹6#ΰH<Ω0Ne ₯`2–Rp2έ‹'uH9θ/Υ[·ͺPρΆΓξ΅‹±%aΏ§ώΦΥ‹‡ΫވΧρ Œ{Κ+p'yoχ•Πή–Š αΒ+lg›φ iŠ‹`ƒΰα6‚Sφ5tE(hΑD4Η―Β―‘!¨βœ°ΩŒ΄AzΏŠ)ˌΑ‘Hφ>"€oŠsΔLΒW +ώ8ώόμWhl5 TB[°ͺΝΒΠ>ΕnΎ‚¬p>š|~4< iZ†.i°ραOg]—ΚO*Μ*΄ΠΙ–q jΒι{ˆ›CaG‰‹=Σs¨λ(χ°bΐjΑD—ΉσΗNΖΚ™³±rϊl,žΊ7#VTΌE¨ @ΕmŽil]z)Ά―œ ώΧ£ένF«έΡ {xίή―·υΜk^žeg²dΗ9kμ¬[Eπ+ώ-²ΰ2ΐηΒΎ) τ$VΚΰί Εv―"ΰ2T6Εoδͺa7»Ω3нdq™φ5LΣΒΘι>„ό,­ŽD:LaζΧ,ΓΟhœ8΄–€&υIςyθΔΦ”ύ)ό(s֍"sdΗσυΊMΪ ‘5JzεŒΫ\qB˜Οσ½φ1=ό2sΒmΛ,‡>μ%8ω& ρŽXg0-§›θέ*τ“Γ© œNΖγ˜_Y‰Υ»οΥΣχΕκ=χ+ oZ¬¨xsP€ŠΫ[—_ˆνKη-ψχwΆ4Kοζ Ÿ{χ~ώΘ¬Φ‹°ρ½ΨIΎ`α_‚RŽΨuS,ΓΓ3Š€CPRpΌnν&ΝσκΠΟx'†™4ό·ϋ¦θι0Λ–ΙATά)p‡‘œςΰΌϊREMάF°O™ς™_ꦔΡgΖΥοDB‰½¦‘Ύ3‰‘& ] ΧΒ§Α,gMœΖ]ΜMήXςwέ‘ΈΩ§±Ιν-ωN’€2KFIdEκΠ©X9ωΊΣLμ‡/Χ1Γ ω7μnB^`Άπp™c`CΏ€KΉ ’Ν¬€XΪ.+ͺϋρxσKKqμžbω.)χ<˜**ή T β-Ηpσj¬ŸNœfψΧ4ΓοYΰ{y^¦…Βlz6cy·ΆQίλ—A—de@ΗΖ€{$Ίd3qΟΔ †sWzšPŠΚd::[l”Dςωχβ'˜²₯ΰym˜Φόν|%Ύ·„;^Zq‹ηΜ™‘‡|°—Κʌ3˜±‚?ϋ+aβ‚&‰ˆΣx”:ρΩ ŽQγhPμGΌfd@ΡSdΙ•(Šο,°‘= /ΦWƒΣj,ud:9©βΡε(LGp /Ρ3ΰ0QρšCm[ ΛΛqβώG€<ύϊΨaΕw€ͺTΌan]‹kΟ=矍ρh,‘ŸΟί7»ξŸoζ{GFi–jžΨf›όаά?u¬¦ Cβθι.ΎBς< ŸC …ίΜ―δaψΘΘ₯_帘Gb‚b£qΎ¦ ŸW Τ©Ι<έNnφS/7Ρž΅x4ΧvCjΑέDΙϊ6ΓΫu-UL‡χ^@Ad¨ οζŠ53-Βe<^ɞ36eπ±‹$”Ύs‰`|[Gh”9S€λž›IIZψΨdxΓπΦP2ω ZPθzΝhKͺζ¦(ψ“‰~ˆC>―ž…5ŽΗNκG–€ΡΩ–, ej€ τƒX»qόώΗc°r1**ΎͺPρΊ‚ϋωמ"ΦΟ=η]ΞΜτyαΥΆ8Ξρ9ΔzH+ȁ¦ιζ©Q7—ψ“Β;©mMΊF‚€K–`™1Ξd<†φΦT±βΆΏ½ ΑΒΖ! P5 OΒ‘©5ΣΦ δMH “qTήΏeJŒΙΜzΘ=Ν›ύ±!\¦βΣ†ίŒ"٘%…ΡΡξHa@ψ&£™υ{‰d[Ω³aώ…ž-±iΗΰ ‚'Όr"SMΈ±Mς!Ε:+G ‡Ξ₯-ρgz‚<Šιόf<Χ‘¬F‰ΧΠ5υ›•{„™ΰ=!i+†ά—Φ#nΑlΥ;'‘:ςΦάP€³m{Z²ΌΗ5!">Ώ 3κΌΣ“bMˆ7rΆbυξγψƒοŒώrU*^U¨ψ1ΪYkΟ~MB™2ΣοεKv‰ΛuTθ—α5²bΞό4«·°η°ΰΙlͺΈexπγ―xςhβΫpψ+‘ސFCβ“ά&ΧΙ~M¨iwΌK<[Όœ 3yΜ‘ik†&»τƒdζ™^Y+쁸"4‡nΘ>£“#λ qyN'vΦ‘f~3Ϋ‘Ο‘‰ŸΰkRhŠΧ, 8Τψ»`Iΰ+œ*άXYλ”M³φ ŒGφ_»χqς‘χDg°μψ ͺPρβ =σeΝφΏÝάΉοΗυ˜™0‘_°"Ης^2ƒ˜ͺBccŸ³{»miβ€lf―μΥίώelEΘr ?{–(:5ιz„·_ /¬cωΊαΩdA’ήBp |·ž8 ―›xoΔƒ¨νΑ!“CΏ£(~ YλΙH>‰ήΠM›:”§λϊ4QXΝ1f•-x†.·xyΖ/EΞΔGΡΤA±6Κε0+ρπJ‘Hӟ8N†8Ž-αγλ”.Η)ŒSθfΦM›t{T3k”σ’;ΩP'ΆMήnfFœΜM&Δ΅OŸt7‘ž8΄tΝΨf΄Γvθ€£Έ]²r»†Υ”o¨uy”ήdμ=ύω…8υŽχΖκύC ‡Š;U¨ψΆ°}ω₯Έώ‚fϋη_ŒNo.ολk°dΠχΠ–Ž΄¬Ζe€¦V†ΥY8ω,t.σΫΗ“ΎψΘN,όӍ‹Ξfp΅G1!]Θ}Μ|GΣL«mρ/;^l|σl_΄¦˜Κ»A4T…7~e°φ ί ±yδ$#MΞͺΙ™7¬γχ’VΌbΓύΝt›΄εβΓΡφaτ‚&ώh΄#W:Λ‰ΞπΓlόΈZΔ‘a·} 8¬ν~oΩ‚Ζh’Ϊ^o!:νžlΊhκ}ͺw―;'a՝Ερ_i"ŽέšΡ§²#;Ι;r;ύΛp"pΘ―Ι‹LΣΟ ³”8•³š]*…;·8πK|Βd(2ίp ~X#!7φ N[ΦΖ‘(:~$nZ]y₯94ΔΕ΄b­t]σ²Z.ωρ ύΡ&Š€όΙ#ρθg(£a¬έ}_θ=1Ό>Zx'£*ί¦qε‰ΟΕΥηΎζ7˜ωΎΎn:9di:lF<Ά4žL=ψΠΊΊάG–Ώiυ?\ΪGπ3 LΑ’ˆ¦jV€ύ‹έC·“C δ ^‚vG‡@Γά;ύ‰^vί4Έ’7 $ŽV„>Γ“4.<0 £ αL|ώ…›­ψ΄b<I KXΛs4Ϊ‹Ιd({;φ‡J'•χ‡[Ά'(_ΦOƒ™π» ―ζ'ˆw^―[Ρψ”Βΐ³–Wd†yvΜ5δMG|j_ήψc—C.θ½*` Κ"ΎJnwέ^>Φ†h›Ÿ_™ωχϋσ’?ˆΉΉU"Mfƒ[ Ξ“ψΔ"M>l&“S*80•σΙ:‘ΪΥπ·4εO$…OmψZ+^‰Ÿν­iPΕp0|ȞK+j\€\€_¬Ύ^Iφ Μ.‡ΜΤΡT~ψ—tP΄E@§,…‹¦@~Μ-‚Žψ“οxOψ}ςΝ°Š;U¨xMμ­_Ž«O}!n0Ϋο$ψ":vtΐ²]Νh¬Ae4i°LΑΡΦΰάνυ½,ιWδ7ΉF %}ςhᜎ2Π‚CΑ_†Όδk7Vl9hfόr‚ «ΰ/VΔ09ε`Œ KΑ G©ρpDsŠŸ3—~‹ΙtθxΓαΊ<:±ΏΓuΆ·‡»m‘Ο‹^RxN؝8φ,]"Γg³sρQ<›:₯›n7Ϋ-Ψ²œ†2‰ΌZfΜ"«ός;t ΠΘ°—ˆ›+οΊ³ŸŽb@cS§τ-ΒY6rfύZ‘0‘)δ-EW8γ¦mθJΗm’!TΈ…Z§=)-Υο`nΩyŸ_3έββq»;^fΒΎϊeβͺ{εΕišΉim%MΣsεGuA₯pgΥΡ³ώš·BΦφlχζ-'«]΄+^³Τΐϋ4μC¬ )©€»π°4‘q7~‚­ kςŒΑΫo©°I^%}—+LΗ¬ μΗΪ=Δ©Ηήύ₯ϊHᝂͺTΌžŽ«O|1v77%ψ‹χΰΒπ’ΝΕƒIq3ΆŽφχνO(aݏ€qΰWšƒ9­š>Œρ.‚ίΎϊ'Wq/2hlISlφLΣyΑM° Ÿ3jA|–φA[gf§m§]"Μx5΄6mα„=Ӎv5cΟYϋpΌ£2M$π7Νf<ή%‚ΘSQΚΈ:Χ.”(ωΩ‘“ΚΒg™£!κL«£Τ€ν™Βœβ@%#3ΫDx₯YΦ,KS₯φ“€vγQβΨ—?υa™&…9˜“™`-ρmw[2T(L}•K^}ν±—΄ͺ:˜ͺp{•2σ2nα¨Ι#ƒΘ ώ8ys{‘ΣνGn1ϊ½Ήθυζc0XT[ξΕάόr¦ΣΠΓK?>λ ,$]YόK:9Τμ3\ ^Ρ–`%Ύ’|ΔUˆωI™P:δv½Zo€ΈΤ€γe*X­γTdιδΌŠ#ά³kδH^˜φP™σ]δΓL¬θ§n!M†Γ,.Η‰Gήkg)!ί«¨ @Ε λ/~-.~ωχ<kχΊ@b Y 0―LδΕ@:Φ ’0ρμJ ΒOƒΫ,† z –S±ΙΦg?ύg³!‚›,eΰKz“Θβ‘ κ0‡‰—ωŒ³ELŒaΊι΄)]3ˆΟΖC1έ—Pί2cωήzΞήωΒ ΫΧ‘/ρ§ψrmςf;Ύ³€C+„xgεhβ&²Ό7ϋ9φ ‡³Iu•.Ξ:Ϊ…‡Ύ„˜΄"•,„gbr Oβ:³`€‡‰δk!› 8T€ΥεD Θ/Γ0mS@Φρ/V\ ₯9’ϊ_Έ+ΛγΝ­……ΕΡιtc Eaiω€Ÿb!?)‰“ωΞD"Γ,ΔqΒRΚYΚλΈdάtΖΏΙ‹}p§Υa‡ώ8©TΈf&4Sσ0 Ξ@Cέ€’„):Vd±2‚"P. WτlM7ύj‚"°―>­ώΟ{>kχΏ»pψ^CU*βΚSŸ‹ΛO|^Άžfόόe°` ρ xʁ»\dx ` 3‰L B‘'>-K>λΟσΰ†›nα™;ϊA‰'Ϋ‘α †„zθ ζ4sβ₯/ηbff₯΄Œ5{ίφ;fοϋϋ1’₯όΌέQβP(ΜrNΞ7Γ$ Θ΄oΙ1rVγ‡τ―b–ϊΊ M`«±‰d'Ε¬ίBXό’XRι‘°4ŽΚ„Ξΐž†…™L‡zΜ¦΄Λkn\n¬‡drλ'O#δ±δiv4ϋ-€gΥΈf «7άΚpό|KApμY fV,>r‰―ΛL~…ϋβ¬τϋ‹17·σσ«1˜_°«Y©ό‘{ ₯xŠγΊ“@u?ξ €‘³m‘7ώŽWLXόΐΜ(Χ‰ΨΣΔ…³ƒ“ž +ΈΕ'aδ|zc ΔkLKXš\&4TžΰρΒ“οxoœzμ…Ύβ{UΈƒaΑδΤρ;φ{sΧpσΎ‡Ž2ψ”!%ύ5˜Yψγ/;‚’ΑΈΉΧ -`|aaάΆŸhύƒ?aπ‚Hρf‚;F—άNO±ψΑ;}2δΘΠn^)œPfςνoΗξ<ζή¦wΡ³ŒΟ Π»¦Y*•9C2O“ ρw&dν(F^C ± σ*τ CΧq‘(M‰bεx+š°W˜e0ϚΞεi*(eΈN—ƒΊ*e.4 sIλk αžφB‹π€·tϊ\όΤΠbΛ°ŒOp–7Λm·λ`6T²5J€Ν αtSNΔ$]%aG|°ΊmAIŸyCA²‘ΆΣνΞΕΒšƒe+})έή@ω€q‹’ύ όXζw%g­ΘD¨m3\ŸΩŠ€μž™;@^M½4ζLΐΜ)9d›‘~"!}Ή}™ρ'Ž.σF[·‘n‰|:Ο8yέ6«’W)GR–O?φΎͺ|‘*w ΌΤΥΟ2F₯ΰ§ΣOu”οΧ2ˆx /?* r˜˜%rŸpbδo30<y„A(@\hν―˜%Mσ‡“MhΝAΏLΫ*> ηΣf"°φ“Ι(φ%δχvnĞ„ώΞΞυ†’bπ;€#2FGυ@|Χ…έ"σ:+^©κ`1›σU`Ύ@^ψ{ΈΥί,εnΩπ‡D-œp(ή6Β~ς„Mf Q…;σK80ΏΒΐΎ%[ o?cζkž*€ύ ou”s¦Ÿ©mΛ΄±d-Xhκ—ξ°fl\@‘hŸBηΤRZέ„Ζ‡²bχu1₯N}I'›OΊ(wζω~Σ~ΐβι„‹ƒΆ†Ϋ4iΖκ•W ”.ελt~Za~q5ω1G_[Μ,<ςΊP?|α²0lΨλψlΕοF( ARΒY¦ΎμΠ!ZhπΗmž؝Ξ,³—ένΌ(Xg ‰Σ₯–)5δΕΝHΚ1žτ«Ž"άυξΖΪύο‚ͺβm ΅ ZEŝ€νΛ/ΖKΏχ랍tz}  ! i)Κ€’_Š:Ή5ΖN¦ИuiπI!ƒΠΙΈ9ΰ™3eŽiφΓGώΞ`£ΰIπ^B|ΜZa…pFOzΆΰ½Xu&9ΨEŒ†»τΧ}Ο~oύΎΣ™0`5{žύr s":Ψ9NŽDcž€31sQV  ΝŽ œ³Bύ–ώ™bήΒΫ$₯<ΕΪ°wέυ‡vΛ@U|§<’™ΛΈγ2ή75}4=₯Π\$³ Ε›œ)ΘOγ)3‹7£Ί „ε v9ω65q…RCƒš¦*Σ!'mIξ³ν3šΪ;τ±U΄¬ xH+A¦ρ0FΣΜ’³P˜E²yΕ Ξυb»~V2M6ƒΞ/Ŝ”žFX\:έ~ί”ΎΥQfΥCψλ?S}€KUͺ°i ΐV„sFΛ2ڟ<Θ_αbθt"4Wδˆ€‰t»οC ΟΖn^rϊ$ωΖ‘βOˆΒΖC+3χ½#±xϊ~ϋUΌύP€;ΣΡn<χ©_н­ H<>Ε`[ J `'vŠ΄β‘¦Α €έΡ4“ ͺα,Ηˆ&ƒΆόγαx:€™ρ7~²6A"’―Γ΄2ƒeΰ±4ƒ~ϋϋšέο­Kθί^p“ΒŽΡStJ‡vͺ—οznzkIΨεdDΡ€cσ”1Σ†V’g^[ ŒvΤv˜Vϊqΰ½L§»8Œ€εL4ήv\Voΰβ‡ΒΡι¦_™Ι!ά=[tΖΣΞ- άά³¦ό€ΰ jΒ2N¦™9 _ʝβζŒ$ 1Σ ^n`²CZn<˜zY7½Θƒ§ε$Y¨Ζ‘[ۏWςό5~ά†iH™1’ο™Ποœ)§ Ω=ΓW³ μ©ΰ€ŸλCΧ(rr¬Ÿκ$©”„NΩΔΙεDGuΜ{\vθK¬ΰΙ …!―­#€ι#ιΣΐ]Ό YJ %Ύ]πO™²W¨’ζ(ςšέμίΔΕNή_$*—ωΈ &Gx8OΩΪέ† œkœ )φbR5ž€:ηPͺͺCM\Z:)…ΰxΜK)˜G!`eΐΧ&ι‘₯6+_LΔω5ϋ& όδ’Σύ¬τ7ΐυΜ¦c&X ·[Hžmžψٟ€/]n»Ι•)½~„»BhΣαNœzΗ{βτ»> qΕΫo+ΰ―ό΅i|τχ΅β}ο:lΘ―Žέλβ…ίω ^‘ώ¬nΜ{*Ν₯§gWVg.$BΘα>rf“( @ΔΗΕ``Σ2ˆΡα3‡`°Ε§ψɁ»ΙM1`Ήq4ΪΧμ~=vvΖΞξ5…1 I… ‘›žδ{ΜΜ+Ρ‰Ÿι†y“‡Γ|61΄ƒt²§NΈ›UŠΖ‹`›€ ΒΡ&y§Lvα]Β¬pρΘY―οέΤ0ΑŽBγ₯z•₯™ύ²ΌjΫ„gBX¦y±ΓΎ)ΛμZΠc#\ρυw˜I…ΑΌZΌŽWας#ίςΙΖq¨·δΆoώKώμΗ ·ƒ2ά™2γbfΘ-HŸ¦Ξfp/GΧΙ첎›ΐΩΉ\ΪΌžιΫΔ9B υ+σ’qΝΛAπΓ’ΎSς™ήMͺMΊB6,%NAρv[EΰλZρΒ’•Υ»bqιD,―œς΅ŸΈοeϋu;η ή __#μ`σƒw¦CσpF πuӁ–ώΔ)ϋ: ύi―²γ΄‡@ZnGό1Εš\ `TvӍmήƒωγχ8~Ε퍷•pίOβ/ώ+νψ 6gΕ«γ…ORl]ΉέΑ|vξ[ΐ`•–μό €ΜBπΞو,h4aU,³°b ’“ΈΠPΒ1αΓŠgπς?f±’œτ™ιςΒ  |–υ·Ά―ΔX3 f©ΐ2Q’8xι―όNΌ"‘kξΘn²₯Α*ΙJδβp^Σ‰έA_C#P?tκ`Fo₯ΙΩΊ¦ΡNn~ΡJƒ1Kφͺ9 ύL%k4όΘ7*΄ Νn™Χ…[ή ΞLz:Λ/ύ7 4πr³bΣδKXΖ‘ή,μF’s=S:δ©E―h4…Η°P'žHŽΐΒ·˜Žδλ]μ%^–²x*³βЌpH“q©'j§αD2Έ ωMH:ς^bλy5ϊšϊΠ: iRJ·)d₯$ιŸ aG,Ί„JԊd“8@T˜/Q*+(](ά2X]½;σKͺβ±Βσš(’γdU‹‡Myb‘”9λΘL€43.ώΞ3ƒπ—‘€|"AmZM ΆΤΝ“<ΆωT(›βγ€Hϋ Ζϋϋ±tςΈ‡>.βŠΫo+ βcοΖΛρόo²*υ!x^‰šψΙ+οو,Ν0žιNjιψΔd`Q@Θ:|!P1B8΄ `©~ 2fΚΰ“3y’ΫΩΎۚεοm_ ‚'gΖΔW,pδΐΗ@;ρm PxλΘ™+‘fτυV˜ξœ€ςWόΙ££bΟCD ~ϊ!pτΌ]ΞχiΙ7qIΚ›λ«Τ[‚:sDν;V=cG°6Β΅₯JνlΕόςŠΩsΟuoγZΜ­“»―$”“Ι^ŒTέΉΉΨίΈσ««„GΡ^<έφ(nΌπtτs1ρΥσqόΜrπψλW6bυΤ )nϋΚy₯΅KχΌΓωm]V§Ω,H)ۏ…c§b¨όŒ†£˜[Z+ΚV+†ϋ|rNΩζ΄}/» ͺμ Κε²δl–“uγΪqe=₯[‡ιŠ?+Δ‘ε&έ„κΝmZά>!ΣLξΥ*93G`'|»p«½΄κ””Ikο£η,Fq4e)vω³$=Ζ Έύ!fϊ pΡίΉΌ¬heυL¬»'VΛ~Sω ZR(gδόΤBςΣΏa-θ =αn«‡ΧΔOEΠ— ρα- δ:‘ˆ˜‰yx΅@J/œ|λ '>υ=¦\“xΰ‡ώPΜ»K *nGTΰ{—ΎϊΫqωι―DO³O ©[cb Ϙx=Šδa ™τqhrμΩιΔΰT:;\LΓY9##~% {₯[›WbgηJlKΐx&!…₯Σ,K7<υ“Σg||ζΟΈBΛμΦeΐ_IΣΩ°sry…©Ώ“gΧπsa%Τ•fΜBo00O8–μνOt«―ŒXψ7Αf.žΚΉΒΉΏŽrβο%ŒG^ζεŒ5¨οφcY™ξ\y.Wο—0ΔΞζΛR.pχνήΚΆrοΩΈςδΧc΄·§ίυώ5Kg3Ϊx#v\ξΗڏJΰƒBΚɘμވΝσ/Δt4Š₯»ξ―ˆΛηΦγ$τϋΨέή‹ρθ Ž9[⁰Ÿ?q:Ϊύ•Ψ½ό|,ΚΎ/Ώ{ύz,?sR.F;»±sνR,?ϋ›Ϋš©Ζ¦βςdΙpeεdμo]εχΕυKΟΖΕΉͺΥ‘5Š+;Ψ»R@Ίά"Α]μΤ?ξ³v$s6 E ΘΖΩxΚ.ΊΖ•(4Ύ0„4τψ†Νvϋj7dr5­Π°ΑIώˆ²7~3kΆ£τ(qυg ΎN{ ς˜Ήν‡εV»Q[aοΐκ±»γΨρ³~IΡΕTύ‘vD™UΑ%*‚993Φ„Ο|νmE“²ΨΤItGΓ(§_ –.ύςv χ—θ:©YκZΠ‡ –ΣOψP?£έν8υŽwΥ½·)ͺπ=€§~υΏχμŒΨ9½g(Ú)8εΣκ’‘I<ζ , 9KKŽς*cŒ©upΞ”ΰΠ,uBΞa%D‚Έ³s-Ά6.ΕώήΊ¨5»ΡLŸY’γfjζ…½pLŸ†‘ώΜ>r’AVα”α™έ™…Ψy€g1Ÿ;$Λ}ψ”Σ§Λ;XF/όΜ‡4[0™ςU<πsϊ:$Θ…ϋ4φGCΟΨ†EΨCνR9lΡό°χ5ˆžάΎ AΏ’Yφ ™­Έ~ξiε―kχ=Χ^x6N>όpμnlΔΖΕ qL~½•˜PΫWbύΕβΜϋή§k6§™ϋ5ρžΖd_‚ϊZ,>νOΐφϊƒΈziΛ³φω…ΉXZ›cμŽK6βδ]k1Φ`έΝΎX»ΑXWš+gΤ`?Ž+O}=N>ςΈςΛ“ γXιodά]_SΌ;Ά―λΪn߈ω•ΣΡ›[ŒυΛ±|μήΨΈόt\‹q‡ΗαR€S}”ύHΝ¦[u ₯ΠJFGε@Χͺ‹Ώn6-R—MΫ1O·ΫmΰWUzĝΉ‹TvNf§Ϊ™ώ¨_γJώi=Lίv(μ>κŸΘΌ7φ[@€vC#κ»\;VΦT·KͺλωΑŠιό΄G‘wΣ‰τAl ŸΉκw.²~©+‚=0³8Mώ„ӏyέ7dn»„ΛD‘Kυs”κQ‘Ϋάw+ΣΪ{_ω蟴»βφAUήΖν܈'υοDg ΌΜ _ t\ž‰g”υXΜΘ0ας[ΘΙΤίΓ†ψxF/»—˜ ƒdοaΓvXΰn\āB‚₯γ ¦­ΝKše²‰[δ2};  SΑ™p:Κ—Ÿ™–9{Kγ§ψd›΄=ΰΘlωώwÞAΛχίqKˆxCαPXW³©–¦.ш«dj„Πμώ·ΒρSΐο‡ψΨΑL*žM‰89ΆOβ…K&Ÿπχξzωi\»Ηλ±0w2zσRDϊ­οοΖϊΛ/ΕιΗή7.Όs˚•Kΰ—bos=οy8ƚZŽ―ŸσΪÏ«:1Ίρrl^ΌΉν«—ε^ˆΝλ»ώƒΨέΕΙ3Λ~ΏΧ‹:ομΔΒ‰“±ω²‘RwΛwίλaόκσOΗ±{ς’/•Ώ½7Όβ4·pLe™JΨΏkw=κzΨήΈ‹«w‹ζ\lφβz«ήUΚ^VL jŠλ¬’Pi"̊jjR^rγ'΅wR+ϊ†} —Ύμ( ˜]kh‰J*Xss˜8 œ&4Y>‡eR·   Κ΄S)d#‘Ό\VΩ2iz q2vώ.—έB“^γ.ΘԚΰŒO}O€ˆρς‘•΅ΣqόΔƒj³(gμ' MjSWδΔν―ψ₯U'εkΞψE!wϊ4ΐž>ώ¨_.―¨^YnΰΕ@ͺpY³ε΅Κty'O“ύ½xτώγΡS{©Έ=P€·)6Ξ?/|φΧ’ΏPή>φš £–N/»΅w:L6ψ•ΐτ7›&π6ΚA\Θ Η4M1 uv šνcs㒎 ž9Zθ+Ι?9δbkς4₯Sόxš‘„Ξψ3όšεvnx γM~ότσ>Ο ₯ˆi·ΝjC[‘ τΰί€3¬‘Eβ‡φτΜδSΘηστ rΥ ³ξyf¬Ψb’Λ²r2ώYψ$r`Tύ)άυΑ!vkγνΈ{υ$J΄{©μj6ΏzοΩννΔΦεK^Y»οΑΈ.Όtά‹νσΟΔh{'–xDB ;/?c•ϊβΙΣ1Υΐ»·Ή)W/okφΈ(Εl7R4Φ―mΗ±“‹ΡŸλΗhw/zΛKͺ·^\yζ ΝζW]Χs«k1άڊΝ+—€<ƒεe)Κ‹"nSPΡή^Œu¬œzΐεΩΈςB,;λλ0” zf{ΧBωh]dKK P7:α*~΄½€ρ)ͺ 2Β7vV Š― Šΐάά\*ͺ‡AEΥƒ|γ#M/Σ-wsMKΈ†ΩΈ@L±΅:κ‡Ž€Όμg9ϋ¨M?jΏŠ·UxβΖs_Œs_ώŒ?uZzτk ;iΚ:ΊΌ,tΪό—°ΓAΛ#@5‹d](ubΜ†’±3ΣGπom]Š­υ‹~_‡ε^G• ΟHΒ‘“§γγ†Vy‘)²|Έ™τδ·ΙAςσnzσ! HΜόΚƒΒR-y˜ΙxˆΦVYΩΧμx$a_L?i{oΙ €Εz:i“ Ύπ3€¦.™ΡσΚa§…œΣ;+ΤαΑAΧC@ΙL‡ρθΪ1ΝΞ·c[uΩ_XΥ {σΗ$ˆ5Ϋ½ϊμ³~‘ΣςέΔζ…η’»΄€ψό•w„ράρ„–"φΧγϊ Ο»N>όˆσnμmmǍkπύv¬P»Q\zy=z½NŒΗΣ8u:?‘Λ{x/ΑHJΓξυk,£θζbνξϋγڋϐιX»χ‘Έqώ9 ϋ{c2ΖΞυΛΉAP3ώή`Ρε[Ώό|,ΏΧΧB2!žάέ‰±Ššbά՘υΖΘλ…?¦jΚυœhšg†‘mκχΏΖ~~’μΜR{RζTΞ”‚ωΑ ]μl>%žψ—Έόf)ΞΦΩD’ΕΓB›‰ΔBJ\²GΧΚ}1ς'š΄7fΆ£4„‹ωΒ'ήN«$"[±ΛΒJ±ϊ(έkΗοχ~^oNνqDΔό’!υb/nσδΧ("i@ •lt)m^|ά~QΘoYp–μ§ χ΅*Μ‰ƒ2{ο» Φϊ~ϋUΌu¨ ΐΫ η?λqγά³Ρ››/>―]V Sϊ)Θ₯֟‘ ;/~ϊηHΓ–ΔD’_Ξ—³λ76H† ŒΧ_Œ-Νψ™Υ2ΘfG‡:Ο ΏρLώ9Kd‡Μ-Αΰdπ#O©„’SαGύΌ³\N–¨™εαο{ωˆ3β{Π!Z¦˜9GγΨE°I`™Ωk`Μ₯膱2r1ς©ΰΥδKrKΰ*ΏΔ“―θ2X™jάπ+i{Μ ₯[αMΊ~ζ[ρ2ƒxηκ’wγ³€ΊΏ½§ίρx΄Τk;ΏψB,ž8νž,εγΪσΟΩ~ό‘wψΐ%ό“χΗΑHΒώ…g₯@,ΖόκqρΖε λR5–ΐŸ›Λ7ίΠ¬ΌΫΆίφζ^œ8΅$)A“ƒ˜_Y‹υ /ΈΞVΧbgύ†Λˆ‚Η΅\Ιζ0ϊk +ωZΙΤΙ—άύV?2ΩΡ‘_œδ"¬9»‡[ J.u4;ξHπŸ‹υσΡ“›4;wr—ϋβγαΨK―ΝΈ’—ώΞƒ*7?ςΔμΉψΛξΑ—CΚμΩ ΝR/Βί³nH•ιG€6Ϋ]‰§S―ΧM₯`nσͺ?μάRJΪm¦ΰΆ(λ‘£0q³g54„ΦœΆΪΥMmΓ<΄5ΉΑyΒη0§oΣ€ώ%Ψay?«ύΑ\?ρp¬¬ή#Ύ€™mRΫΘ›4δCΆœ·™ξSr@yxI> ·B/³Y`/€χˆdΈr‘²¬Šλ6“eχbωΜ}qφώ@‰Qρf£*o\όoiψ”—c_XΈΞΟ—•Aƒ1@ώΈdρι|9 eG€ΚΘΑzμŽαn―33€nμοoΗ΅kΟΖφΦ͌Α@ ₯£H3lίC6'200δL˜ΞJŠΏθy΄ΙSx’¦Ž"πΝCŠqΘ»S… ΄²"ΰwvχbG3όύ½Ρμž=aΐ‘<*ŽΈ4OΉπ"i™Αι”ψ‰t䧐K–&‡ρ¦ŸQόˆ‰­oalΐin:ŠΣΓυX• G€n^½δYϊ₯σR ΞzCOt₯L-έ}ol]8s§ΟFŒ»±wε9…I`ŸΈ+«§bϋΪΛR€€Œ&šέ/Ϋd¦Ώv<_0sγΖ0Nœ^ŽΡΞΆοε_ΊΈ§ο^q>wη{ϊύωX9sol_}YωΩ·bqνά3±zζη‘°sγJμo_₯9X<—žQώ\ΞnYΧn)Ζ²??ή"†γΉέθΘκIΏ¦ώ\G·ΰhXΖK·|δN[‘˜Ωf˜9oρ^…Ί@!ώΉ–…Ϊϋ zR€, ζe’˜¦P‘6Y›εΓΑ΄Ϋτw;4»όεζ[ΌΖ—έͺ‡Ÿ;ζ!ΜJ8κk:…$ύš Π_‘j«νnǏ­qύx,5WeœWΧ-F2ΈΙ4 φ<8!πύ䦳ΏbΟ8́’Μώ›\ $,GΨ ¬άu] x‹P€·nΌπ•8₯ί‰ήά7ώΨF~`!š‹K‹|Βτς4ώΊΪΎΰ²xΐ•0›=p8 ,υυ$π―ō«ΟJ°oΕ³q ¬83 M†άkL&€ ‚ΧK‡EΘ³œ ³bψ3ψ!DΩΈ—ό Ι |³—)n Z1K˜νξΖΞώώ0Fά‚PΣυ “3wN²4³’ι?¦Bΰl/'ΗW]:_”Χn6ξM(³―„θΉ'‹iBΜoζ¦<>ΠJˆŸTuΔΞϊυΈqιbœ|θ!?ώΗ²ώκ}χKπ_ˆξά|lΏ|!N½ϋƒͺϋέΨΎψ’_Τ³ΏΉΛw=—^|ΙΚΠΒβ\¬[τυΈvιF;±;Ϋ»ΎχΏΌΊ€ΈCίϋΙΌ~m3NœXvyχ·7cnρdΜ-χ‹šXŸ_½+ΆΌ¨2΅c~ε„κG ΐΖ•ΨΊvQtΗb0·[Χ/ΔΙwΔxoΣ ά΄΅ Ά Σ­ΨQϋτqW ³HκˆK|€.e·β§ό" Qj½‘ +–©x6νšλ;sζΜΝ™Ζ]\MΊF‘Aξ£ΐΥĜEk€+q›λ+π$+σR ηη’Χα•Ο™ΗΜm†gη‘>ΆΣ“(‹Ψ—ώgΈίB‰Qb3iˆ‰3y4ώ£Π7ώ2_υ₯Ί]Y9ǏΏCvήIύ:ΠDΉdŸ·θ— σΧ(ιΠ§r/ΰ ~ždˆΦήΉ΄nάހΏ―ψTΌY¨ ΐmŽν+/ΕsŸϊe όKΕηՐχϋΉ<ؘQ 8qΙΟW³:όθ€tAμ˜f/‚“Yχpo'\ϊΊfŽ» Τ@.jΔ₯Ώ[ρ0ΙΩƒ˜Μ2H3ˆC˜+iϊ;V™AΔt½@ΌKEΣΐ‰πfvΏ½³»ϊΓqΞ\H#Μω§ά€α™‘ώ%_ό\aβΟ­Žχ˜Nρ=˟Ρι€θ­‹›ΰς·˜ίΈ«9g„gηΪqje9Z*Σρ0Ά7y3ίΥgžŒc>δέύמyZa#)IύXΉηΎΈώμS±¬™:3οΝΛ/Ε8ζUŽ©κ³χl••+―i–"^ΊΛΛσΉμ«l©ΨΖΞΦ~μνμΔΪΪ²χoπΰψ`a-n\ψŠώ™˜_>­ŠžΖφϊ…X\=£kΣρ£ž€Εχ¦γ½XX»WΧ²οηΏΩω?™ςB£ƒΨ’Ή<έρe‘ξŽ^ „B&Ϋ3DΒΈξ(‡yσv qQ*2ŠE©ΧlλιΆ’'σχ)M«’Ά[`Ο ™…α<ΐ!ŸΤž[Rζb^uΏ8'SŠ~΄-ςάδ‘Άj>ͺ/±'•7Έ“φ¦'J²ϊt*Aͺ%Ÿ Χt*ίιΚmE ¨ΎWV§UX*Έͺ^1Τ}&DZτxx`Ο~‹%yαΫOl”$3Z6ι¦=ω€b‘/zπ‡>‹§se©βΝAUncLφwγλπoϊ5¬―)lΤ{,ψθEͺ²i ΰͺz aΠԏΛlmΌt:† Ί-θα1»Ηεy Ϋ+κΠΜΒΛ°tKόε= σ’ώ¬?kπΰ;―Ι»Wδ`ΐ#ƒ ΰπfΗΉΒ °‘OvΖ3ΛίΪέ–Π:€nΆΕ? }) ^ι ¬0’iAƒ»ψd6uΓ¬ΎYIρΪΈυd„Ί^Π–ψΔƒWήΦ (>^ΕΫΧζϋqίΚ@3τˆλžŽΥ»οήΒ|\ρ9 U ό‹/yEˆ—φp«€™? Μς±qαωσ±Ώ;Š3χŸr}a_LŸrnή؈…₯…ΨΩά‰₯• iεg€r#T™KρΫΩΩςϋV€„ ‡[ΡιΝΗήξF΄η£Ελ~U”αxC΅»ΎϋΞp[³ή΅n^Φ ίΑyΙλњξΗ΄­φ««S/ϊ6ΐ‘Ϊ’N”/„zz^»†ΞΧΠ^΄¬D£$Ό&nβ“qMς–ζ‘ΏΟΆΟ Ο™O ΗΘlήL{H'›3yΨξ¨6;―k·€B0?οΥ‚Ό§―`·Cβx9ιOέΡ―ρΜΐ^Μ"e‰c_G”)t²-P”v+VΦΠΆbνψ±Ίr―œτημ Ήβ’P⺏κ€»ώj5)πeζ*@φ}gˆ8Ξώ’Δ ZΖ€Τ4 κi²Ώ~τg£;χ&;―'ͺpγΙ_ω[₯Sv”£°πΥεkfδζ‡a 2φQg€γΡ­αfZ€ΰ{δ ½ϊςΣ~Žί‚™Ξͺψ―$΅Ή‰‰?Lϊφ>LUζΉΙU?/4lJb7Ή—κKžα½?ΗζφVlmνHΐδ#Mp™ rψ• Ε‡GξΖHJω[αΠ`JгέϊMέΙΣ;£Ό £ΰ·ΰV!Žέ\"Υ[η0Ν Λz&/QLξ³Βΐμ Σu€tR P^0Χu˜E‹ΗVQψΜ$ϊάϊQœυK/JΘΗ±ϋŽΝ‹ηγψύοˆ—Ÿψb,žΈΛ―~ωΕK1θE –Χ”‹Ά„ώ’κθ 6Φ·cνΔͺ„ρ4vw€H)_ύώΐ[.ΗΙϋŽιΆΏΫˆόψ.Α»WaT&•ƒΠT–”OΥI3‹δΩpDOεXTYηUΖE)2”j:Υυa@τΤΟΛc)sΉœοϊ’™υKΑρpΥ€ΰΓ?< !έ4ΛΑιGΎVΞFΨ,1€Y¬ΆΫ&„~Aϊ™hfςϐt–S±κ¬ŒeyŠϋp5εεΪσ"«‹sΉ ³š}‰:o> Ε­ ΧVαη6*+«yvSv’€Γa&ΧA[reΜj©ΠΜ”hԟΰ}κΤcRO‹&ϋ•£3ΐΜΫ|σO?r`iO€ΧιίΠ:Μ†Οiˆε¬mΙtΈξŽ'ΓήπΚΩψ‚”₯…y+ύ{eJΈφ•£Œ ιH·Hf^Ά— -Љ-ύ“Χ1#βU(ίBιI!Y‰S§^o^ΚXΉ- Ζ©Θ‘ΈΉ œ Ξ=²K‘ <ΐoε¬(ͺΟfΕW~lz,ÁΑm#ή9qΧϋ>R|*ήHTΰ6ΔhϋF<ρ«? οϋ7ΟjΣγ:˜,δε™oιΰG—ύ4œϋξ,χ_8E ˜}+&ϊΟnΰε{ύx˜§™H8! Z υ€PŒΌœgήL-Ÿgς7ΆΆbC‚ŸΩf3`ΐΆΙ―r﯊yσΚ›Β]n™ΉIμPΰ#$omΚζπ₯.φͺπlV΄πc\k–ε¦2Ψ_u€πn”€‘,βbοj–ΞfΊτ9ΠyFGYddvςΪΐΎQά ”Tυ4nΖ]ΕUΠς©3qυEvήίη<πžγχ?&il^:ϋΫqόGγΚ‹/Fi-V­Ζ«1?―zŸΔυΛΧceeΕι(GΠqy8Ά_“ΐ_ιρΆDvj—AYyvΡUn7-ΚKήpCCΈ₯Ѝiψ•6G~Gš!^“^vcά‰5iΗ»ύΨΟK)ΘM’ηF[63±,σ­pP1oF‰gφΌ©τΪ’¦ΐτ·½Ύ)Lk:β7f±Ϋδ ½£f†c6τφ°‹“όŠIΉ\ξ#p|LΪ±~=΅'n,K!˜—‰Β„B’΄6,ΙΗ~²ζ:^‚끋kξkeΟ€·“βα―λšokTjb8–"°Ίzoœ<ω¨όJΏWΈgΊΖŒ-τ bΈ―”2`w‚·όά?e²šGϋΝpΕΑ γ-Uαύ|δΔ`εdρ©x£P€ΫΟόΪΟων]šΑ½KŠ₯o^Έ’K—ΒHOa^B¦3J«ΆMώ” NŽŽ©8š§Ζ•ΛOΔΖϊEuJ . ΄uΣ”xP{φ!ΎΌ¬‹€ς€…ϋψΔ1Θ‹€ΡŽΉωίJ χ·Η6·vβΖζ†_·›ώR fž §<€5.³|x³ΜOΨΠ6fΉŸ|ΚΝϋQχ(^­iCƒBbJθ‘‘αG ΙΏ§\½π %fƒYŒW]fI‘_ ΄ nΠ¨EΧƒΗDbβΙ_ΞT πG‘PΌr (“ΙΨc'5ΓΏπRt­n,œ8ΕΠλyξώ?%1άέυΗa¦νωΨΎό¬κ}1VŽ-Ηh{»ϋ±²Ίβ·Ά5ΓηC:—‡ΊJϋdoΗ;ά’P~IΣΛΙ2Ή8•w–ύ½ ·ω+g&£”ςΔ#σ­ΓR7ψνλςœίGιkΕΙΞJτ»RτpqΈ»7Υ‘ ΨΆΜ\Ά¦«δ·κkFβ 4Ξτ77ώ%sΎž.υ\όLτΝrjΩU6£΅yΛ‘ςά¬ ΊuJ“˜ΨΣb·«ΰ¦ŠHšΖD™&΄YX^Xπ» ζ4K~u2—Œ*GΓƒ~€Ύ ΌH8ή\cΪ-m@ςδ6«wl\Z:₯όπς,…yh²ja;©Έ8C(ΩNΌΧFc•jΒω MρHh³mΑ|š /±ρ‘?ψ§ŠOΕ…ͺάfΨ8Lœϋό'_ύy]*:=;Τ›%K:l ,κH„‰~Ω-sΨtGθΜ£α^œ?χy ρ‘Ζ{:ͺβ0 C-BfwJI|ΑΟ#BΔK ~;Τω%€ˆC'χΐ‘RCvύA_ŒΔ[JΓφξž„ώ–wοS’Ιτΰcn˜Ω#ΤG{ϋΞ=ejόY``°ΠΩφ£x΅ζΜ“ž₯λθ±³^iPξ”Tω§4¬£΅'»xšoώ)ΊWΰSAΠuP\'ΝIαΉ§ZŸuψ€]δŠGxΊ1Rα#?*³lž\ˆ‰&fψ«gΞΖώΞvμ¬_υNVΰΗ+{» w«ntFλ±±Ήǎ­YQΪήή‰UΩΫͺλu…_εͺΓ}ƒˆ₯ΎςBtΑSςΛΜ|.6fΣΈmΐ5 Moœ, θΑHυ :žcχυ ΚΚ›; »<κΖζΑ Ξ..ΕΌKRξvTxψίzύΰ?3”αi-΄˜&#ίφ΅Δ² »Κ¨yΦ\Ϋζ°―Ν΄gœI‰ͺxΨ–ατ؏ςJα9λŸΕO§χΠ―¬‹„γ6¦Ύo°ΌΈΛR°ΩLHγŸJ%| kx₯_SgN’Qφ|Ζ½ μžΥΛτΖ>]GoTTψx"erεξ8}Χ»Ε›ρ@eQcΚ‡HΣ}@yύS[4š[•N_$τ>oέ\·9ύΗΌΰžοϋ‘X½χ‘τ¨xCP€Ϋ _εV Y=ε(θΰΊRž…³,Ξ]:9ȏK9λG‡°›ξ_1._zBZ8Kς€!j’ΡΩ!",Z X Jƒ§‘^fφΠΡ‘S³Ο―±aη#Χ77cckΫQ2_Θ’’_ύΌΚ 7B‹₯~§-:”[›ιΡΑς¦0ΩŽ}ξ½70 Jή—'ΘπcΐRΖ,¬)«γ›ΛŒ'f&ΕΧJΦχ½§ΑŠDQ(PšRœfΉ|]d°ΒΗC+όΓvΎΙ/o2<{¬kͺΫΛ/<Λ§ξŽ^TΎ'1·|ά·6^~Ρ{Dζ?¨Y–”&)tW\3wŸV;iŎκiy)ΞΗ±!~«]1<ή,Œπ'έ©2—χlU(Ϊ_IT8œ2pύ©“Η¬'ωQ4Ω‰ζΗ5)ƒςΟ1‘]W“,#uc؊w³Ύω1ΓΛ•¦’Ά9nrσΓO&€‘©¦ŸWn°”λy”‰MIώ±gY`ΧTe’ΝΉ|Νaͺ€³Uφ΄ψŸΆ™εˆ½Δ)~M?JEštd:œz$,γdΤ43»>Ώ &|XY[’2°²°Kσsf₯$Μ»•PKψLzqΈΌ¨3(,MϊfΟδεƒ}šΆqίύ?¨ΎΕΣ R ƒΒνΙ'2§6>(‡K© ω1eόNϊωΝ₯N{ΰΪ9%ΎYoΔŸMŠ7UΈ°uιΕxρ³θ5ήφG'RΗπl˜Ž«„&7ΚvιŒ€‹ΪΨρερ¬_ψέϊε,ΌΕ/C9sx ‚G±7³= g< WSα%=¬"Ψ£-!ΘηuY§coiΆ}cΓΟμsO<yq*ΝΜ„APόΨΛΨΨη₯tV4($ yHx0?‚£Ν–A$χ0ψ—ΝyβλFyπκθύνκΒόsγώζ„©ƒΊ*9υž.ε$œ±yΛ ι8ŽiςxωTvb3XfΎIηf%ΖBAe΅‚CSη”Ÿ˜ϊΛσ1ŒΗN-†tΨΫή²€Ÿ_;λ_οŽίσΟκΐ`ετl†ŽεΟλΗ]ώφG~ ίݚρŸRq+ήάΗ%‘§~~ƒωνρ8’μδΫ…‚€Ω” κ&λLώεRωΥΜΕEή” eu4mΗs›ΣXFHΟΈπ5ΧWΒuˆ7ύΘgc–φε0K!ΜΖ?iPRΨ“0Η-qΰ­ƒΊ'ΫYF[lχλ"έF13tζdρΏ˜>˜sΝ1sFL PΑ28Ϋb·)εο›‘ΙŸoˆ~i~>V—ό2’ŒO»r …gŸδ‹ΐ·]u‘!:ΛTό¨―œΝ'/²5™μ{ƒΰρ¨Ω©qΒZ³ΙkO<ϋ‹Η…)3ϊ4<‘WΫσΖ@‘uιKI-gφΛΙh?ξϋ₯3χ;€βυGUn#<Ϋ“wsηςξQЁ0‡BΒ—N:^RΊ?Ή³a²£<ΕσΟ~Ϊ4ΜΞaξκ"τfBρπdΎ°σΛ}œΔΔ-§sοκ—Πε‹j𸱡766=sΝρα›iΑΟ_$+|?6ΠMΖ*‡’ΩSX7˜•λˆh'“ζέμ€™Eμfΰ‡ΝŒ—ίΒO^Ά^nŸ4yL€ΓʈθΰeΎ \@~Δχ­'’zT½ΗC‚<πω9ŠCϊΠΞςNt³Π™γ&°wν_‰Ή•“±uνj»ϋ!§qύό3џ_ςϋχ·]ˆήά1]9εAYLα8 •εΒp}yήΫλΔ*“΄Ήu$"eΥ’Ή›ω@πsΡ(Εε΅Ά¬ π˜™7ϋ‘5Ε!jΦ†―”μ<ρY·Βζ"P>Υ·Λ#žό§Ρο‘ϋωΣΈΌΓ₯»΅Μ―Έ ό|=<κv›ΐO&v/Iλzς +²Ί–¦5J[Ρ‘FΊσP…˜$Λα34i›ωό₯–>ά¬t5Κ)΄Pg/(νY>Ύ9š|έ€ ,-Ϊ4₯εkdΎ˜>Ωp2²˜ΜJAζΔ}Y'’δJνΤΎ—OΕΩ?¨kΊ+ZƒβΡ†Θu[h)ηΈŒ3ι­zV;ˍŠKCRa»,ŠΗ'©οαŸ"BΕ€ͺά68ˆ/½Ϊ―z₯ Μ. Αf z°Ε²Μ$aɎπrΑώνkqώ₯/hV˜3δBζΑΑŸ|ΰY’βNNήρ(Ωπ—³§vΜΝΝK±8ˆkλλ±±½MŽf430 5…mΚΓlƒϋόήΨ—ΜefΪ3š#|@γc°ΰ^~γΦIεΘΑΈΤ))y"nC“£_‚0hcΞ{φ98™Κƒ£@TύψΠΌP0&Rf,κ†Uψ‡•šύύέάΔ ³o¦ύΡ²2ΰΎλx/.~ν«VVOή§ΌNb΄Ώ#ΊˆΑβšΙΎgJmΎΠ‡£«“QΌΌ?ŽcύvάΏ@ή”ΏQt{š­)%πθš…u§V쏦±7ΗΪRίν«Ή½D=ΈΕ©ŽΨ<Ω܎ΑΝJΑσ6€ψνΗϋ?aαξ:Tޚ’ΫΤ΅ζb;ΕWΈΓωiΌ°™Χμh»ω¦8R? ^ισ-Bι:e°5mΗ·PpY¦Φᕬ"ό”ϋ’‡΄ΨΎνΙΛΧ˜g ώ$’8ͺ VΉXEB˜ςH­3„Šƒ’ω‘M;usyς€›­ ¨ίRλξ/…u!IpρJ€Ο:™G±CG{§ž&“‘κ€οxό#0TαL,”ΖX’ZωQΉ)5ζφ§mˆΝ½ž,˜ΉβΙΠpg;ήσΏψ_šΆβυGUn¬Ÿ{*.|ιSώ( ]d‚Άμη‹δNCbΚϋiΔΒΤ@‘}E!‡ΰc=››—γβ…/«sυECLτnq’"θ9μΛΜ$'wOxbsGD‘PeYY&ƒ"o€γώώφΞωBλ<FF1ˆΟ7³|„ο@06ΛώNΓ';Νλ(š¦Ι`ƒπΉω3HΘ―Y‚ΟΫ ύ9š;š$Μ]aΙDΚŜ/¦JNπ§N<€e9\:QΟΰP,΄U/MJί²π/y< Η-uωψΙΥh ·ό5Ύcw=έΉΝϊΟ)­ύX<ώ˜RbυG @Y9Ώ?ŒΡ$Ξ¨έ-α1£XT»Ϊ“΄§Y?χ²ό Άόψ΄λgΎrA|ΪρžGοΧυž¨ύπδE*qΤρ¬<Ξ›²­kΈΎ5ŒίϋΪ% •vόπ{Ξψ4Ϋ©Θ”Ηύύ―q―=”!aG]Im«>―nΔSWUΞJΆ#G|Μ|_#ό‚Š,8΄½6\fL~JŽzqμχ­xζήaahλα©XvvΚ*ΐ˜6ΔςΈϋ4jΈ£­Q΄αο€ΌGΠτ‘μ'¬T΅½_`myΩ_6$ΝY ”αM€K΄”Ϋή(₯―˜TnVJάΟΤΏ[R~όχ9«Ύ΅F$ϊ¨βCξ<¨(€DJ‡Jt|’Z εMsˆ–9)³wΏχ‡bυήG‘xQ€Ϋη>χk±uωΌ…,—„r6θ Ύ‡θΎ“štϊ%ϋ“|ιTάΎ|ω©ΈvυΟό>ΎΤE±pΗ'wnύtpΛN·u :!dΩΥΟ?>«{ucΣοαg9ZNόgq”Wxr_ΐƒ₯Β‡ϋ{ψ€σ­ΈŽ5‹+˜ί!lϋ T(+N0AόζVIΞfΔΘnώrcr4Λ’…Hφb uIHGnή³OrψfΦΛ¬ f`ΜA;»£ω‡iΌ:Ž–4υγ(ƒ.¦8“£‡-Gg΄γ'#x>ziνžΨΩΈνžfΤ kb¦<¨ΝLZƒΈ°Ώ{jχ β8aτΊr+ώ<ίΆίRͺžˆ`fζ2dτϋνψμ—/ΔΎ~=~ΰ=χΕ£œvένR—ΤυO΄tχ€~ⷞΞ߈~ψΑxΰ,+ͺΥ 5H³BY© *Su&ε„zγ6IGuΎ±=ާ/cgŸ[AΉα3…"3γ¬Χf9₯ασ«β¦o@7ƒ •t·^—”gП‹Α`>]%Ž?KMYΉ^j‹ΝuLMΎ;γΒƒ—ήδγ°τ•=·AoΥρz)·‚t9θ\ΓΥΕE+άƒŸ­B@Θ€l~ςŽα€Μ#DWν‹ς’s<ςžχηΑ½R©φeO9DΟϋB”°/γ΄=Β!‘rΕj‹κ―‹‚ˆΪŽκIνaιδέqoύdπ‚ͺά&xςWΆ€|ΉΪΜt#/­ιοητ‹0βh›ζb―\y&_KαŸPˆβ;žΜμκŒξ|ς§ΐ'IL+κ° άΌ†–Ομ^^_‘fςΜ€θ¨Ξ«™ΡO-g~‹ύ½]§Η"xωž§Σ…4σ}+ކs_–ηΫ!υ°?‹"—θΨe ΑΝΏέ’c"ΤχηρQΎ){ 3ε•όj°ς>ϋ3p½zήΜκœ<`ρϋFhΚΦ€Ό Dε‡ €Qh4ο^š•Πl<ϊ±·³.ͺ™όΒ1 Ve¨ίύƒω8?ΪUyΗqίόA,vUζnG±–Τ $όη‡β/ͺ4ό±%~JΚ+JŠΟsΩ―ΔψΏύ^;ώΘΗ>ΰΗΛΈf ΒƒήΎβ9Kd\iΚΤιΏωωΟϋΪώμ~OΜ•gΣފη'LΘͺ•”)ωΙ\*\*―sΐRr+žΏ>ν}NεΜ)°QΌj$aΙνή1τ†Ρ±γ‹xV˜7ΥrΉF―@‘ŸϊΎΥ―~(· Λn/Ne\ͺυIωaGЁŒ‰RFΏπu…5¦~΄MΜ&c”‘rρ m”cʚο·ΘvΪ”νυFΣSι?πk‰IX^ΰ«‘ §o(Ό₯±Η΅ΉΛ W4Ψ½/@1‘b7Φ5yμ]?¦φΔ£ΆS·“f…’(V0T&+9ςCΉπ•Φ ά^kήΰdœξvΌγόIϋUΌΎ¨ ΐm€αΞF<υ«?―›fΊτ4ύ=ΠhdΙ.'_)W χ*‡Έ/Ή³²nsσR\Όπ κς$./ršΛŒΰl΄|3QdΟ$HΟύڝ–AoOΣՍ­ΨΣ,e~nήyj„hσ(_ΞY HΑO:{»;Ψx«™gΣ–<ΏšfΨSt$Lšε}" †JΫΚ΄„ιhε,„  ee†FαSΠ§°§Όδ-γ‘β«ηι΅ςΪδσ•₯AC;ƒβ^?WT`A>œη—Ό‰Žkuz~.Ž΅5Έ00vbOΧw~ωn Y |ΥΡ(ζβœ„ΏΖΘΈGu7?`΅ϋ“₯θ,.ΔTΊΟ 7νHΧT³3·/ʎp‚§fŸ“yΝn{ρχ~εsρΒΉ+ρρ_άwχρξu-x΄ŒπŽςD}σΨcWτ­ψδgžQyβG?ψ ”C•Cυ{ ΖsβG_ε₯r‘;€΄2FD.αNΩΫ’γΡ4ž½4ŠΝ=·ΒΜ#S<š6άΈ †ΰdUivμi-~Τ咍dC›@}Cpψ½ π%έ…ωe»Έ&£{)ˆB?ΰ«”™?η­δ5o›ε£¨6Rs›QΈWΛπp’Š£β’ŒΡo(Gσ} —₯Ι§μMνskώΏ 4ν΄ιKΛ‹‹VP Pς6A“eεΰ’’#.' 6,PfPŽήω}ΘύΦαδ“ΌΛτψ ƒ±#―£"5ƒŽΐ„ƒ?Β°‹ΤyοoΗ{ϊŸM’ŠΧUΈ °uω₯xρ3ΏJwρ ž}Μ. x,‘Τ%έy²C*Τ`·ξξυ8W6όεΐ‘ΨΌ θ2[ΨΘ›ŽGηrΗ”‹–~Hœzφήp―\]ΝJzΔX‚χ›ς4\<ΛgΉW±svŸεq—οΜ—·6i ™ŸW<φ¬σ³+Ύ c QΈΦΈ›Γ|u䠚4"²€@ΰ'½κI~š0έηυ˜Ω°9ΟΒ‹Ίφ̘|¦ΫƒάMyΌš"¨ζ”I  ³Ά“κ†Χ KΘΕ²ώ^μΚφβξ^Μ©(gζ’OIxNΫ*»κ’ϊTbΣ“@FΏŸKμ€ΩάΓNΨΪχλ•kρω―Ύ?φΓοτζ7o|Τ읖˜ ν:Χ!ž sCο@ψ»=Έ]dΐΧ‹Ψj› ˆΞœΪ%e—΅­―!όΉf£ƒxώς$nlkφΘJ4„‹•9xΡ\(ωQ§2›pΫ‰Υ§}΅E?iB h!δ:•8ίΝυ€=² ­7…Wr#/ΆΜόΨΏΒ[19h‡φεOΈλͺδΑϋ6Ά6Χcw{Λ} ή5mασFάM;€τ•ογ«Λ~¬nnε9©t½(@±λ’ °ηςώAτ4^<ςΨοσ˜E[ξΗ„ΖγvΚ%χ”?Ә][ gσ¦ΐΌΞΔοοΔ{~κΟJ̍Ώ―ͺpΰΖKOΕΉ/ό¦:Ba  #Έϋ¨Σ0ˆ5ΜC‚ώΎ­j«N`Ÿ}ζ·ΤΛ†Ώ¦£ρCπ™i1εΟώP0Τύέ²K`Ύ|u=Φ·Ά€Pδ’³ΒȏύœdαΟΛqvwΆοβ“Ο― β‘,0ΈΒ+xE)ƒ»σ+`²DΟ ”μ8–£iωI@ε³υ)θŽςΈί(4eif¬ΐo!$G„•λΛyGAΰ^όa>™myDƒ "Ε@αχtζΚ €βˆf*e`[βΚx74€žQs‹ οθšφTƒƒΨ"ΰρΛ:E)›λ³k[ΧMiΣ<ξ£)ŸκΝΗΊ0Y‚gφ©ιΊιFRi“Τ.χ©Ή-ΰv*Ύ½toˆ'₯ςΖΥγ6U»Ο ³ωܜHyZ*o'@!b¦Ov:°RΔήo “`yξεq\ΫRέP^#M_G_ΏT*œͺά”₯Γ_8TpΣNM―‚ξyu`7vΤi£ΎΎ>―αGq΄ύ€›ΒKΨΝi(,W₯ψ§ς²ΐ3ϊΛ+Ξ³ΙtX(*ά·΅DKΏΫΫݎW.ϋZd~Υ~H€΄+Κ{k>_OΈ<ΚΏσ¦tVβΔκJ ,ο—Ίu~tR6/°Έ λš..ˆϊ`Ά+‚DΖ¨‘‹l%+ω‡šΏŠηψ\?ŽΩŞu4Q»{πG~2ŽΆ»βυCUn\}φ+qξσŸς €θ~!‹‡%Λύ`ΟhΥišnΟ…ΓΞrλsώψ 9#`|«ήβΑ -fΖHΧζ䁈 @WΦ7γκ wJΌ{šA2Πs―Z½Q>b₯‘ΉΟo> ΓΟ3/ ΄˜L)ΈηΏ6άτDƒπ½πB?|ύ61₯ΛLh2a³3fn'˜Jτ*Cг~”Χƒjβ(g MΤΧ ά.‘.RABΘ₯@¦ng·rˆζΘΤuΞnR B¨±7€·βξΞ’xh œJˆΚάloΗ΅Ρv¬ΆzqŠ= ‰9Υ£€Φ ­.ΛγΌ‡₯!K=#@Ή-9Η>‘ώΚ ƒ½w^+Όΐν‡3ΙπMέβ7Uλ™Δ ·­4PZ•’—dδOΉ|‹h€|©Α@ρςΛ_ν­+ΰΊ6Μ‚οŽF7+|ˆžœs¦±³»η―^#Aρ’?3 wFΝaΩΏώΔρΐN^<"§`cέpoGBGύΘ€DΪGέ·‚¦Η Μςw+}F@ρϋsšωjΔϋξ=Π™—ʁγ|ΚƒΘ©θ$_πf ŒGAY¨ VKPf~ό”Wς˜αEΈ/Ο½S ώšΉr-š₯s£ψ&γΈ·Νwύ)oΔzk?ξ―Η±n?NΆΉΏ¬ΊZδιZτD£΄:­±&Τ¬, l”&ζΪ°…OΆφ£~ŒΫxΙOΝDAͺS––χu\+q‚‰μ(sR’EžECqΰ1œΜω…Fξ\ͺfσΚΧ F|αQ2”φ†Oκ₯£y“α3FqωϊΨν’Ίjβyf袌 ‘[f#H½t¬ΒΘζΌό4νEƒ6Σά²bΥ:όوge»ΔsŒ†Ωw ρt)œV;–Vb‰%!˜«7ΨΌq-n\»¬zΘ[IM^χ²ΌNyϋ&hκƒ1€IOœXΛ'xΡΟμφ•θhσl}θΡQXtZ νΛy¦mͺΝ0ΉFτw9hGβC[θφ²ό“Ρ^<παŸ’pΚό+^?δ«xKΡUρ(C?ι³  Κ|/Π#3$γ:ΩήήFlΏμŽ’Κγ§hΔ—έχΐ‰£ŽΗE‡έση_Žη.Όl/δ€Ÿ Θ Έ€<1λN«~dΓ,€©pΎ‹OώθΔπχ€L~Ιϋ7m?xΉU6Θ!ό½λ{<΄PcP!ΐ½Rv…{ΆOά’Ύ’7Νΐˆγ%oΉ©CηΓUΰΜ•ΊΥΐ§rπh%ώžω•kε=πΣΑ§XwΆ·bog;ΆΆ6c²ΏΧ6―ΔωηcnoΗ€PŒ5θN’ξh&ΨγΊΛϊI‘?Ρ kŠ‚Η>‹–‹Ž)XlΚ“½ΥbΆΕ α=ίΛνiΖΧι±q±―tΨ )·he‰έΙ1σJόr(η:q½T•™7ΒQίF ρP%>ΤJμw8ΡξΉΌΌΊx*EαΑΣ±ΆΔ[$'¬)L&·!XAρͺOc*N^ΚO{Θφn₯W‰6ι4Χ /6œΞ-,ΖΒβ²οΡsΝ(»Ώk‘2γ6W¨ί h:ruε Ά7Χγκε—}Ήf†όΙ7ν‚v-―‹“wέ+o΅κΊ΄­¦5ez£α4u πΑ΅xβω—β₯—/«.₯ˆϊz–R(K(u/=9ω³Ί—ή\ νDΤY·Ψ]B)~–Ÿ±Œ§,Έe0ΏΊFδŠΧuΰ6ΐξϊ΅ψϊ?όΉθ3ΙνΩι)ψΚ¬ΟrΕŒŸ{ζ7- =xJ(6(τ(":θμΆζυ›WolΖ₯kΧ­Ε£8δ[ΞŠΠ—Λξ^r 8zΐ’GnώS^HO|ιΈήΝ½Uχ`ηP` v;βwšqϋΝwd‡Št=ƒV8fΟ&ASίγ‡/ώ―ΒΝBΣ}˜Α#ΐsω²οlΨ¬ε{»J;MFr«.+a@>Μμ¬φύƒ·σ@Ψ«+M“CΠ#0M“Qœ?–ΕΗš< ­yΚΜ₯ήLΓ|‹AžΘ/;ύ—VVœκ‚2eΩΉ)TD­°ζήςΤ ¦k‚ˆ·ΦiπΨ'9–ε₯H,v£3'‘ζ' Β‚ΥœfεNΦυĎ +ΊfΞώΊΣ‰ό7‡\Θπύςn/fέ²¨=²!_ΜuRωDλπΤ‡όΔ ε`Μ#c΄˜f\Χ€²:?74-ΪAΎ!Σ±ι: Ν«νJ`ι›vο*yΞ7Ύπυ]Ώ,ˆGU‰ΧΌhΣ.cΉF:βμ« ίVΪΤΞ‡a™S++i°!OiΊύeΞ_ƒ$ί(ί2-ΎXΩ·π‡νάBNΌŠ"~F»»˜$Π5Bργ―Υ΅ζ¬@?a`EQΨΪΊμοϊ+Ψ4|`…Α΄p„ ƒ‚ζω —<λGhΫάd(+₯L bρεž(JdVHΩƒYgΏΥ―ΡΨΙ+ή8eαœ”€ο₯ΨW”hr€GώX.ΗΞμ&bήcdf³ΏΗ.m?ε‘>ΚlΌ ί7”ΫόόΌΚ³F}“5ΟψeA‘‘,l:ωx½ΰΊTΎXy9±Άωρ?!Enίy₯œHΩ*Œk’³LΕsY(«ΐ>τcqβΗΝ³βυEUn<σ[?Ά^ӌJBWƒ­‡G:χυeζ»ΑE¨ΟϊΏπόgό’^Μ@K»98pA’ΧolΕΉΛWEΟ }σΰoί ±o;(AμΝOŒεaωΒη`-λ‰ο–‚ΠǍA!…€Ω-΄ϊ9MG>‚£ΝŽ― Z0ΘΗOακ{"G°r_œΈ³ΨπΖΌ…›ςΒΐΜ*₯άήΑ-αΟΐΖΕ±ΚΠ,7»>τkκ ) B?uς-‡φεŽ…ŽΪ….Ζ‚„ρ‚¬δ«ΎJρ™asI²NtЎŽΈ…’’Ψ¦R—ν…ό€GA ΝBXͺzeQx@\ϋ αΔΣ π”[΄4)V€GzΏŠ,—–Ϊ.MΝ΅ Z—ΠιΘ€―κ‰δs%̞:© c…ΡŽwχβι—˜ρΆ½*°ΝG©$ Ϋ3ff>QκΡpžI{ζγ6Ο>”lσ‹^ώ)¨ά^ }ηW,ρI:Ϊ₯S;šζwŠ’/Ϊ?6#ς„Μβ²ύ)·5Fjc<ΖΨ¬6‘T2NάΪΧnPO{ΊV?όƒϋΞ>#)ΙΜh[^έ0• ”T·€wdw7γƒϊ_Π΅ΟλσέΒ«˜£}υΝ]+Τ9«hΎήHM;ηi n?υη£­γv­ΧΧUΈMpώΛΏWŸ}Κ‚› 0¬={&­KδYBčρ ž}ς7Υ0x™‰0`1P3€2Έ·γ…‹—cS3&–α^΅ΓSώ25ˆ0ΰδL<7Waw—DͺΠEk+ιΙlšΟ˜YŽΌUaοz:ަ ­]ςΓξ˝xΡω',‰mG‘@¨ζΜ!g\Κ1’ŸγH:oΈ.ύ―ΠΥLM΅ΛκzQ>œonahq‘‰Η²…Σ‘[HEΐ6]s M… όΊrΟu’3ί‹Vώε^8+1bξ(’§ζtιuΦ΅γΊgΝΙ -Ή=‰–{μΜβ'Ό?ςΰΌ‘γ!«Ήš‘Eή²[ωPP8ξ³CLΊΜ¬Y θPj“ΉόΞ#bΙ³)νaΎ3_K¬ Ύ‰%Fm7!<1rύΖ8ž;ΗjYΎ2šH›7nΔϊυ«βCPvβϊ|—‹$‹?&ιΚ%Ï"’ύ@<ȁΫύƒΚ"šάΎ-AΡεDΣφ]?ί ΔΧωaNΈePVεV֎ΛK5Μψ ΖAφvvœ.·ίό‚¦ο6o X;yςξψΘοϋ©ΨσΚΚ•ͺΚβی΄YuN9d—j½…εxοΗ”y|'ΰVdoΫ §μαΰK•ο1•Lrκ§~ Ϊ~₯ν±Όͺ Ν‡·Ί +Κ˚Ϋβχͺp›ΰά—~#φn\ύuOν”ΨξoΠ)¬0#ά;±±~1Όό΄|Ήtt6–iΰΣ5Mβωs/[yπž€oΊΠˆ Ÿ%WVς™νvιEUπL6—Q܍,˜½ aέά` φ†)€ΝL¦—ΖǝLΗόΒ‚°‚iκ!Mήλδ.yK υW}Pzε@MFߐ'ςΙ¦?662zfaa―š’»ΘΩά—Ω:š―,₯ Ž²8₯œY3Ν?œ86θΔ„Gψϋ(…ΠΈlŠξ™5τoUκOιΟκά< ŠΥΧ§8mΚ2™°Ϋτσ7z‘­vsΞXJΑνΒ«²εΓ1\6u2I^h_VΝμuύ _oR‚JjCΆΧυAϋqZ:Τ>›Ίδ‘Υ —Ηqωϊ‘bΑγ—”wC}hg{3…2ΜHόVΈ^…₯LύΫCωƒ?·=”ΚRΚƒB·I2ΎΆ^mΛMn·*»ίΔΠ v‰O=σň…₯%―45ύ„€έέν™‚LϋŽΣΐ΅αEaκOώ ω‘0Υ—[EU»α %ή ΰkPθΦ}ψcqβώoοSΐΜπ‡Ϋλψ|“„Ω=ν‡F*³ΠdJΕ€ήΤ^σ–₯=ςΊC!ϋTךku§Ώύ•SΡjΪρΫ₯5UΌΥ`0YSυ’o σƒθσνΚHΡ¦Ή²ΔκJΰXΒ–eχSkӘλ)RωρB“[ΰC6Ησ…1p{(F©ηRS3“0ΚAΉPl',W;!Ή2γφ¨λνφ&Ή―›έίˆΣRηΊx…¦Ι_i/ΉiTi7ι(΄/¬ή p›ΓmR+—/ΈΟΈl΄Ϋ¬x²§ŸULtΏUαO ·ΗφΛΟΖφ₯ηc¬6–Φτ1uύςAΟυ…oός ’4™c+eZνΖtΌ{W_ˆ‘Ϊςχͺp› –vΜ―σΰ㨙νPB “ΑpgηΊ]že‰AϋΛW✎\Ζ‚ϊ[€;‘:τ ή€ηA\ƒίΜMSt¦uμ‚μ¬ RδΏI³qg\<LAϊ)%Eγ2gΌRj\ξ°”₯^ξΣ²g 7)&BRδμΈ:”`F#ατ3Mfa^ŽΦ€ŒΙ3Ρ(ƒωω˜[Xπ ušΊhβ4JεΚΩq*6\ΡcύVά»¨r πyYΟŠΑΤ΅ŠΜ|™)ZΌj—€›€»Iuδ€RΟx°Ioμgϊyhž~J;εΡA2Ήϊ£ƒ'˜ϊ[nςβe…7+C™vό3fd£ύέΨΧ mgύŠ‚k1ΪٌύαVόόη~%ώΖg~!~νΙί‹'/Ύ,~­θσ€­DŠ€ ΅“±!ΕΩΣ-Ν*·cgsΛ/N"l€™?υ½ΌΊvΨξv<Ν΅wζ”šΐΏδΧa–}‹ΛŠŽςb’DΩXΝΰz6mΫyΡα|p8΄_εΝmΊψ»½ΰ§ΓΡΘTfΞ+HΚ“έoΠζ―]»”eq·Ο>―—2QFn,έuΦ‘ίΰ‚jfνNYŒ¬8ΈόxrϋΊ=DAYHYbύΊπΟ?σdλΌΫ;Ώ_½ψ?Ε_όonόΒο|>DΓ{,¨―Μ±ΐνΥΛwε·ηΩΗRΈί™‘λ±Όz|Φ†}(] ―,i?˜Λ€ΫΝ ‘°Ό₯‘|+>oGDQKΒ$aEΈήΏJ~|ύεΜt•y( IΗm[·ηάGœwς’ιΨTη„αφ₯cC/₯φψζJΞς_”šλ‰_{φΟΚζ…gcG3~ή’ΙKΡ\‡όάI:¦Ε—z΄IlWΰL!pΊ$­Ήρκc‘Βύ„…Ζ-ΌJΧώΎEδρ§e%ΐ› ί¦¨ ΐν5Π|δoKά ΰ«₯Πi˜4ΈαpΗν—6; χΤσΌ Θ›‘ΎΠ”έ³šΈ.96n:  ‰ρfŸFΰ“_δ‰ n<άΖϋΉ›?;hv6lfQ&pš%ŒBgEΑ³eΔÝΫι–,>qdΎΡŠΌ™™6eXΏΞχ$Μ%πΖ—‰AƒyDΐr/—ΧΞςχQXFζσ«―υcŽϊτUo‹} 4 :M} \©ύΈό4C&ώΦώ^άΨݜ hΝ€ζΉλ7&#¬lxΓΡυΓ‘λARΚgψIΪBžYŽL3l³D:–S¦ςcA¦Ps½αγ<ΟV Μ‡Ω …Μ<¨›^[ξΖγ'ZρϋΟvβŸzο4:}#ώΖ'>ήΟύƒXίڌξ‘k xEμρ΅vœ<ž«. Μ³=,mΡ4uΑαΊΑοΘa`Q|²gaμ"ΘM}©nF±ΰϊωqEΪΉΒέuΗ hΙΧ7„λ½δIρ\gδΥn3oxε‘Β%1޳‘νIιQ»„έξ oΌ|ιœλ‰2Ί.TΗy₯ha”?λΌ·°¨qπ.ϋΕή捸φόWbγό3±·½.”bΪ©Ψ™‚zΣXZ”"κΕΛώι㱄8Τ%ΎWωι"ΛΤu.cvxQRΉΝBέΣ7eΚΣϋΧΟ+κΫS ψZlΕ›7:΅1fDKgξρΞ_mσkΊ7˟Νwwχγ‰ηΞ9ŒŽυ]A`v+ eƒ•ξfGΙΓ}Δn:ͺ#› sx  •ή9sρ&Ώ’G’pΧZu)nkς+WιΈtL05|“Ÿ;)nσ́~¦”ΑΨB–ς@ǏΞΝ‘™}]α s³Π‹/=Ÿo0$M₯οMJΫe(Yhξ1γε2θ`sίκ\_^ˆΞΒ| NFoqΡ_ˆδ±OΎ¦ηΑ§Τ-‚/³έ؝Δo<ύTό_ώTόά—>_Χ`Τη•ΊπΤΡξςDυrFˆySωγ=dΚuCιŒ“ΌiΐλΘt. t ˜HI”σ‘\$Oβ%…Α—™$dˆΤE†‡―§˜*#ρ΅―όΝυbIΝώΤ’„{«?ώξ~œ:³ώΠ8ώϊoώzlKyd%ΐι9.{;qφL?ζY-Q;››Ÿχ¦Μ­ͺ7ςI^τk’+ρŽ‚Rdωp °sΝςΊΝn{σ“₯€H\ORq½™κU Ί¬Χ\zq=ΰ#{ 8Λ°?& Π‡ŽΓ,αΫΤQSΤ²}Τ³F( uI?ξ,,Δ‘Œ5ΛΏρqυ…―Ζ” nΟmM‚Ψ ΗΚΧ‚*Υ%cόΑνΊηΠ5‚?©75g»nϊ0†`υX€ός†Ζœ`Α; Χ;ΧKρθgϋΧ/ˆ?νΰν…¦-VΌΕΰθ48ιX³Ή“=κΞ“όΠb³ν-.―ōυΝxκ… Ύιz>€ε6ž…ϋͺΉό…;;ƒCE$/ ’ Θ "?ώπOΆ,ΊΙ+eeΓ et9…μ €.R₯Α‹Ÿ;eΌΡΎy+32ήθ†έŠ€ς Ί)¬σ%3Λq˜ίοGyεk|σ)Βv·wbγϊ5ΏΩ›Ϋ¨θ›ΊMSΤΓHυ° ΩυΙA “φΌfώR½§@ν’?Ώƒ…ε˜[Z“Ή‹2»r—^ˆŸϋΒ'c§ύΥψΠΓ8Άρβϊe΅₯Ύ#Oδe2iv{σΡνk “ A‘π’:ΚCu€Ίςp@½ 4A€ΧPYυe5@vBDCτ\bmβf=ωh<\ό"`eσ΅ΦA™ηΉ}r %ΰl7>6k?³w?0ˆK—ΪρΓ?Ψ‹Ÿώη†ρ‰?]ε½ΓΧKγrφU¦ξU=ΉœƒXΏz9_Ή"΅9Υ=‚6'ς@Λuœς)fV‚!“œ;Šλ•,•Ÿk‰;Χ=7ΜfΏ5ˆί?n+ζ“Κ©,ζKN &έ¬§LΛBŸΓi±±TŠœΪ;3ΩhώτϊΟΫLθ8ΏT‹ΚJρθ©CΧ9U9Žω'άή{ύΒ³±~ξIͺŸ,­J)ζ;κΊΎ< 鈻RŸͺKΌ\§ΕN€λWnΠ(V‡ώφVΫκ*_Dk¬όϊ6@©g+&Ξ8Άλ ­ Χ/šζνΪ`Εmμœaε€ΑsίχΘ€ZΎ]η3/^xνηϋΏ ΐΝŸ{ŒύǚeΠ =ˆρ#άωaΠ"΄GσΑ`ΙJΐlxΣ?P)―δΩy/ψ{ CΡЏŽfΑΐ ΰ|’‰C’ΠzIVυD§εͺ ”¬œX1ΐ!WQΧ_9\σΉ™·ƒ£|XΑΰ@ƒF©α;ξ[[³p @ Oμ‡#žΆ‹{ζ°«μT‹?”.jΦΏ|έ·‡“ψ«Ώψ;ρ _x:ώόOt㏿-~πΑεxΟύK1Xd΅aΏ`ΟlΈ‹"€9XΤLRΗ+ 2₯τζΨ€Θ£˜Š£:δ…+͊Λς-6ΑKθ΄U·<©νͺ ώΎ»ςΒ#~ͺgςΤS½³ΪfΏ—\!νξ`^³(φ@,κX–B³μ~ Efnω˜›γqκψ}ρΘΓgbwc!6~c»_Rύά%α=jΗEΥρ‡ίέ‹ώΙΛRp.*Ξ«4₯ΉΆ’΄6γΉ§žˆφώΜμΌλEίͺ½+WuUuξιΙ3 £le,p8a?ΐ1ΈDΓ…ƒ Η\|ΰϊ±—`8œ0†cΓglalp:²-#Ϋ’lIΆβ(LN=Σ=»+ΧNuΏ»Φ]5=£©γΜ~χ^ίJοŠί›ΦϊΉ3jŸέ :γƒΐŒ& ϋ1Ξ₯<ΣΎ΄‘iE―σč_\χ' ]‘½r9/œ©ώκ―Π„πϊŠΏž}”΄έ/Ϊ#|Ϊ³bRΈ‘εhΥς…[RF£Ε#±*f:ΎAž8|θfϋΜsyΈπ•ΞεOhjF«•s'γΜߌ•³Oχί Θ͟œ(Ο‡~c’Ωϊ₯HfΨβ8'š[nXšWƒά>aΠθ•1†β6zmŒ₯‘~Ύλ$Ϋ7"V ­–sŒYγ²ΔHι!\ΐVoέ€½Υ(ΒΪsΰ-ΠL­¬\― ˆΤΆDΦlΎΉŠΎΩ‘f4η?HR5u‹ΞuΒ$3΅„)iŒ3s ΩB›ι<^ς©GΏΒͺ(Ρο€σ™'„bE)τς¨—»«+Λ~cέϊΪͺίθσŒ΅q`r$¦yΑJkZΒ^J6wYRπψ§2Œ›kέοώ½DwκdΜLΔιΏιF7fz± #’26„«n¨,c”_‹W=ΜŠƒΙ*²r–‚–“Ρ01)#‚‡ρ9)Q22N'€Δ—O.ΙΐX°"Ÿœ™ριY?ε0‘΄‰)ΚΞ*ΌG8ψΒAι+͎ΈκΓψ˜˜uΫάπ8‘6φ¨½S«έψδ™­ψψ}ξ[1?·xΔϊjΔΑ‰fΐ<ς¬ΪΔDUˆ™7”Nά«B U­€)†_ΪΓΚnm¬ϋ-zΎE‰"£=~E€›χόΛςvϊΥ›ΐΌ#ΐŠVmفž— >NaχWΗ\έσ•{ΤΊqŠ"xΌc€Υ`0§Όυ―b<χ/£…RΜ™ϋΓΟ}α1»fόΞ£χΕRοDΌξ6Cσ2yΛVLaΡΟλΆbf_ΰγωtΝmr>(λ›ρ„“\ω²―΄­ž %xn;­ŽθO+~­>‰_xκtμΩΏ7VNŸ η1WΔέΝό4―Ot*PW©7}αξo”V’πΉ$ΣxΩŠϋ:0|ψ̘Œ›»ο‰ ω'‰Ε#Δ­‘Xužχ>:6V.¨ω*^ γ©©f=8¦:5gκtQͺΫ ZNƒ6-ž―ŠŸ>[QΛ±sΕΝ™ycf½Ό΄½ύΟΚί/yωSΑόiήΌͺUωάβ'―„='Ξ™Α£δ₯˝†άΉ2ΚŸž™σά₯Q|U! tPD7 dνΏΙs0>? γ/ήrkly±η»s8'‘ΤAΖsύΟΖxΦ&—`cΪ.q²3ΟΜcΒ‘.v :_Š Uυqξΐ/η‡x4Aο$‹)ΘξE[ty£ΐΠΈN`L+#i›α`[^Ίλε^ρx›\iφξ3Ρ•³¦»B_Ψ~ck†ΚΎU70$…‹π£g($a™«τ4H―uΨW;@μln¬ΙΘwžΣ.}R T‡$_f}·!—Ϋ΅i πjc»"Μ-ΠGqτ¬)₯o„ŸΥ ΐ}σ@ άϋ°&!Ύ²Ό‹έ•θρ^ς¦Ξ'ςŽQMΉΎΪEΣ\œ^x$Ύσ»›ΡΌu$Ξk₯Ό4Ρz`+.~$bs]BtFŠβ'O’\}ήΪ}PΊ§’1Κ­εΣ‚Lˆφzή€919ξν3<γ3Z‘έ+g/ΖΩǎGC«q榃aHy¦‡s”maLΤ žWg 0οϜϋA|?hdk&χτβ wmΕ+ξaWHskΗ‰3νθlmΔψάzœΉ°€ΥؚŠηDωi ψ<9sθJΑ―쏼ƌβ§oκ—sA¬ψ}ή‘—¦ ;#^υ‹^θ΄“8£ΫŠ5·€5ΖΘ1Α«~κ«ΔΣhΨ£―:Œβ4JSω³sΖε ^‘ ›ίτΛΗqSؐωIΛνΖϊ=;·P >R˜=₯knάΕ=Oπ2ΠK^Υyΰ•Ϋ܏Twω M_™˜”lΨHc 'Χεϋg…s­2ά/0ί`ΨΠUΆWσr^Œ!ΰ0ιŸΞυP„s¨ύ³νΉ ΅ζ:RΦ‰Z«¦Η’ϋ†pΝ WΥΔPr ;YΉνvάυ¦·˜ΈΩμ`ΉŽvΕ@m[@ασ3±#o`‚\ΡΩ‘³Α’Ea σ[|)`S’Αεl#€9LSσεT]Ώ~AΗeςΈ£!7Δ#ϊΗo΄s΅ξ<NL²Κγ¦1 z1uΰΏ†ιΣ «c.N‡2πqγ”2…]'ιŠΞJλΝ7:ΡξΆ΄šcŒk±Ή&ΧήΤΈΉTΐ—9ΟΒ–;Ύ~2&•Ύ±Τ–‚ά܊zρΐ±ˆ{OoΕzk+&ΉFOύ¦f‡Ÿ ͺ‡ΎθΌTa•YΕςP^k§4ο›ιsν{j~.fηΥ^;ŽΌϊΞΨΈ°§ξ{"Ζd t7Υχ ­Ϊ¨“vuΎmζό菂†nœ‘‚T š₯σθ•~Όίb~ttζδΏΉSGΊΡT—^w‡Vc!ŸμΖ=_Ϊ‹ι± ‹›$²κυ£˜2ئ僉7’ Mυ›ϊj»μRxώEo©xSω²μ—6ΙAi(Ž ϋHX V~ ίtFˆzθWλ+m;@½ΩnΌ¬ςx½0ΧY'§gζ:x>›Ώγ2B. Œκža€TΒ@–Ϋ…SošΤA“2:£φΖκšOΝe»΅ξǞx…n{}EŽ7ί΅βδς©hG¬¬vγψƒRπΝnμŸ”βλh•;4­­u+αάfΧyβ\W½ξSι—O†ΗCOrή0ΆP.S3S±Ή²f”ΥsK±±Όkg–cLyη?ϋο8³ϋβΨGο‹Υσ£½±™΄ΐΏδmΕ/η8—tΘίξ/ωalΞFμ›Xˆ|d,žψ”Κ=Ρω½­xΩ͍©λŸνΕοΏ»{ΗηΥ―Ί +%YλτάI€Λ 8΄Θ.€ζ\?hΩͺYγΜsŸΫλ6'&¬ψYm£ψYi’ψΑΛiαύΞ— m¬­R“ο-ٞ»€Ώ€Ϋl' 3£ξϋ@砍Jί^Νb”–Ύ†1ς«ς‡ξσI™τYA›7τ£oœ΅μΓυτζ£w‰–Λσςτ[srδ•χψ|T‹YΛ'5rgΐ΄oeίυJ?ωR&«Œ4γmœ45‡@6„δZ”ΐό© }*C•―‘Ν6«€Yω”¨wδΈ#Ψא•²ŠΈ€.@iOmW#ΛΔLn!ŸΜΑΧ₯&gf€ζ¬ψή?i~Β€>ΦςΕΥEδ²YZ§ή*Ψλκ.ӊ`q²ϋf§cT hd<Š% }g5‡YŒ?ΖέZ‹ΥΝσRΜ17эΝhΕΉ3­8ΏάŠ—Ώ―»³wήԍε•υ27-­Xψμι†άΊγέ6aZRΪ +Ξ²z&­»Ά‘yNε3½0'}"Φ—WβπέwΔΕ3gβτcΗέ·n«£yCwί,Ÿy/N?t,ΦΟkΥ£Ύb|π¬v] ­˜κΚ)9ODπήvtΈΟC}υΟZ`š<Ώω΅8ύ€V{ΗΡyVμ0§;χ,ΎLσ(šΐΐ`δ,Œ•ΖguͺβΰbSα€1ί ΚΉΐΙθœŸτwφ1ϊ¬tK^U.<ϋŒUJg}}Νί`ΥI2mU%a₯A‘rTJž?ϊgάGαSš±/tο)Ρx9WΜaςύρ"@σjΒ€€²’Νλο.ΈσφWš‡αW Ν›_ϋzσr$esΕQωβ•\αOzΨΎμ‘ψκς’Λ{Η@Γ§,O¬PΩ؞ωœ#ε{ή•WωΤν9Ÿ"πŽ η<ΡuΤσ+²r[Y ρ_p8/ˆœΞ[)Ή‡αϊdτΰiˆΒ„ά ΐκΠ©f[σφ7Ό)ήςΏ*–ΛwΠ―ˆˆ«@ΌΒ'nΏ„%¨Μ Έ’ο<ύJ&£*Ζ’UŠΰ 9ΜN0«.¬x‘οΰ&‡φqΖΚ±»M1ΒaμKrΜƒb—ς²‘$‡!ΐΗUΊUŒ§-‡Ο;ωΉγšχρ/:-[\]ŽQ­Θ1Ίχν‰{³1Ώ8—bΟό¬Œ‘­P~+fΜΕΣχ?ͺ>νΡΉ‘a‘±²\‹†ˆ [ΣΆόM#Δm ¨ŸVi<Ά₯Ψί~Σ«csγΦψOŸŸ½―{rOό/οΌ-Ž}sΌzώΛc~|VJ€¬ϊœΫ2O>·:oš•8rh*&ΌΓ”O†π fΞ#7Φ‘ψ9oδεΣ*ši[>ŠΕΏ©~blͺ1ΣBΝ³)<ΰσSΞ”ϋγ?1υ₯ζ«_œ{ Hv—L'rτczp›šοTώΠKŽ/•#uS“©ϊEšΗzƒηψθ‘Ϋ‚χ4`hNhΜG_υŸζΩσNž†ΘΜΑ³ŒωΩ\[5JšCζΑΌ.ωΡβrήκ.G¦ά:Ϋ 0™:9‡ΔI’}α³k¦$\cΙ4šy»ξΐδ97―V¨Υ©œ€‡θ|-Σ―ch|— „‡pΑ_M[:—ŠBB²"π *βΒcΫσΧΌ9Ζ”ώ©~ΠΚ¨λeΪ'τW/"ξLηL‡ tΌί~2Q8ŠυœWmOE’Ο8sU„PΖ*w ₯@qN’mύπε|½K\ΜΎ Βucό₯F4D’ρνˆƒ5Ο°³Ό\C Ϋη„~ϊNρρ4^Ψ)`9W’¬φ52Ί―ς>ϊu%ΤηΗ›ώΨΟΘάDŒLH U ca[%†ΐσϋύ΅BΡΰ\φ‚V’3·Ÿ‰ωήH<φɈGOF|ύ=#±0¦ΥRk+~λ7zqώδΑΈsρpή!Ξό©¬l™#ŒwŠσΖ*’ η¦|}ομρS13?+η.ΔρGŽΕ‘Ϋn’‘°#dŒsna..œ>Nρ’œ™₯“ηb|j<¦φL[q]<~&ΓΌΚZχyβηφUBσΟoj,Wa/[<w.ά‹c·Η=‡o3‡bzlNs’’ΒI"sΚ<•ωƐς͝Rάγ±Ωε₯.R²SynςΎ“Tφ4κSμpQLEΩ₯πyЍ9ώu%nΕ’…ν›7δŸœ¨rj„6QψΤ‰ΒB±kΕ­§­σ’>€aQϊ" ΕH›άŸΚŸάΪωλX΄άuΧkbvz6ζ‰·ί|Ο„ρyΞ4ή4xt„&5ηυq?vε˜C†Λ2xβ<αŸ<ΟR!ίη+yΥ™ͺž₯½J/nWσɜ‚O8Ο{Ζ]%mP¦ΤE™Ί[ m8žSΔνΈ=‡e8NΜ8λzΛή!\09·`¦€tˆ'„Z™E”EΞΪ…sρe_χ§βοόΓπέδ~vώJ€ˆy{“”0ΓΧ!ζλςUΡτ―ωa#&3ωX\UΔ5½†…£"¬Ή&b­ š―ΜΟY*• νδΚ‰ϋ"|y ξXΈ ‘:=‘-zγΦ¨aΐΟυ§#έa ”±|JRωΖ·ηΊ™ΐΈ*Γv.;ΣSu§@Fc°"‹Zυ7&₯ŒxηΏΚYΨy>¬U­œύ" <χΑ'wΧΎΫβΤη¦βρίmΔ+o’π‘α°oRωmΕρlΕΓΗΆβΞ½‡8#LŽϋΕϏ»ρ—OΏiB‘LoσR–ˆ OŸσΝ|η₯ΐ[Rϊdqί ε–/,ΗςΉ‹±ΉΎϜ#·ƒ·ŠS ΒgΥΔΈ—UΗψ$«4ζ[}ΠΉιρ(W‘]§αΧ|ϊCΈος|rSdct+¦ΈŽ/Zς#„ήφg4 @B^ν!€™ήiΐˌxΧΐδΜ­0ηύΖΔ#‡ηƒ7)‚—ηάӬՍi†ΎΒcΠ —ΨΈΞŒA™+ςΌΤΰžΛ'ϋLWLκΐσ=2τ­*J¨ 9hZΖ€β&CΏ?A4 `ltά >3ӟκHc€>‚“»86> >ƒ7Πη™ι=qΗ­/}·ή‹Gnςΐ'NγHZeΐxΜcΖmˆiώ™wψ9]ς6>x„¦,Ήγΰ₯>Ν!τηyOΎ•?AhH<‰#‹ΎBJpžq]g^’pˆ”V^#}¦Άδ“­ί–ΞέυΓ€λΈΰό±½2°ΰ‘ΦkΜ©HΓΣAQΆ)ηφμ‹―όΪoŒ~θ}~9«‹Λ΄ ±CθξLι ͺOfΓ―\P2φλΑy($ςJ_ŒηΕΙΧΨΩ:eU²26žςΝ€Μ„ύθbζϊ‘žšc(•;ζH Ν : \χ-Σh;ΓEiƒζr ”¦Όβ“ΒwΏ2ΚoŽΖΡ)ω Σyν_ΕΉρ‹gΦ Uψ^Χ³ε$=žjNΔXs:V;ΛqΛήn<΅ΤŒω…‘xr}4ήχΨXœ|YΌκΐνωž••΅`Ν••3£σΔεήχΏ±²Nœσσώη₯ΐάtPt₯•όΩσ±ΠxϊΙ§cοΎ½qράKqΫ+n—Bœͺ,+™½{bcuC}΅Α°ra%ζΜ«Y&dΤΖ„ίcΟ 8ŸΫ½PœΎω · ܁Λ1ίΜ‘h@Κoςβ"”Ύί.8#ΛK…X!ζ Ÿωbζά…Άλ±!ˆ/α-+WθΪΚFŠΨηώθOVͺ“ΥXΘi€φ•[ρ lrξρ »ωZ8ΎA₯B§.Υ‹ΡMΉŸό”Φiρzi΅brΓL…[½!€>σ]Œ―ϊΪWάω%oΞΉ'€δYΩkF{7@η„,q'¦y,–-›±ΆΊ"C¨εϋrΦW–…3mγ DΈgv|#n99Μ—§ΞIΜ―Ϊ)αzžsNsχ­€Ί€Σϋuez^.β€g£π\$¬ΆF'fDf]Ÿ ΉξιΒu}μ}¦i ~”‚β΄"΄ D)Š1³M)a3vΰpΌέ?Ώτs? {€0Όαγ«“•ψH3Σ’ ύ’Η/‘+P[IPΠ‚ΑU˜€IΘ€JΎœ­υBs²έ‡*H<̝βΜcνυΆŸ…H.νF―@β@” Ίγ+xƒΎ…;…tώζF»qΗΑρؚLC¦7RvOά} Τƒ#y )Έ–κΫΤψ¦K2G΅ZžkΜΖΒτ€κ£˜₯?:P‰η zα{^ΆΫΠ*Lυp}υΤγOǑۏDh΅ώΨ}ΕΎƒϋγΒ™ 1³g&V..ΗΝwάjCαδρqϋ+οR»xβαΗγΆWέ%EΜξΔh<ψαΟΔτΒ\~ΩΡ8φΙ‡γΞ·ί#‹ΆΛ'ΟΕΜΑy#zΑω²§~•ύΫβ|(¨ώιθσCBπYqήπσΌεάζΈ²2λΌ9앉xπΡυXY— ¦™8+jκ¨tδ:Ι–—[ΒςY+œυ–Ύγ§WCΚγϊ[ύRvυ’CφWψͺΕ_wθs¦s œxuΥΛυώμsΦ#4²|ρ|ό‘oψ³ρ–/ϋΪΨ\[σγ¦9Ή:φΊκfΞΩaG„·I2άͺ8Ws+γΉ΅ΩςeήΥ€10Ώχ`΍Ο|šΫτ $–ωƒ~Œ–²€PΕJ ‚|¦dυΫY„œM$‘ΤΑωTί-‹uΎΝ硜ζ 9·?FΗrΗηz„‘pΑΕΗ>ηΜ΄΅Œΰ·€ςGy±ͺΐG©’."γ#!¬"ϊγs{bυόΩψ‘λοΗω³§bfvO©υ €ΘΖ„£>$!:f‘]Βv…yμಂώ PŐ„ δ"–8ωΚqγψι ‚ ΄AnΛ ΄ΗC φ£¦gl ΰτ£>zh£ ƒ‡_‘Ζρ9WܘΔ.ŽœΠn_hΔβ^­Z47|qΝD›τ’›θς²+ΡQ Ρͺ SHvJι{1π\7[ΓͺI:AΛΜ2φ:NΞ>+Ϊ )·ΝnΜ웏3?)eΏ'Ξ=u2ήz³”W#ώΤηbvaWυ‡Žήϝσvn}ΩΛβΙ‡ŠC·ή³ωV>~δ•ͺ ˆƒwίΛkͺσxŒq“δτDΜί|@ύΙ9ςύ$τAΰ±8­δ©ΜDf'žΠΥo j7ψ<0P…] αQ}5μ8y¦OœXΧ rΤ ½ž=£ ¨₯c%C((Ξ acΤ@ΦiO@³λΉζ^―ό­(θ;}‘SAŸO+ •B nwBA₯σΞ燝 ²<7π¦Λ…ΕρΗώμ·Δα£·+ΝϋΜ_ξtθ؏׹αςG½Ο]ΗBσΙ%”|+hK4:o ζ š›ί·ŸΙ“cNΣHΆœδϋc2 ΩΚs°%ƒσΰ1 Ο-gΣTXζΉ?ηξ—ΆΚŽ’β–Ι.#κΓΧΉοοαΐW?¨ΛWΙ=rsγz„‘pΑΏώ±χğύ’ρho‘πSB„ά‹2¨+ %ΫgE¨Σh!ϋ‘§½ϋγ£ΟΟΕΟόθχz7€ΥΘƒΒξ  Έ¨€eΰψΔΥΛκ“oζKτg€*δ‰’0.ν™σH,m“@–… AΰŒς§,}(ψw‘ˆΫ¦?Š*ξtβ™ožω–bbώκ§οIvHK%Ÿ[Ιqƌ#Χ­―>:3Š Ÿσ·ΕΖΈ^P™ζ{€τύuEΕydρι7©©Ό·:Qϊ¬|ΐWάΊ4ώΥ~ζ9Ω ^ρ;ͺEJ¦΅¦Τ‘}qβ‘'| pπΦ£h[±tφ|Μ-,Xρ£&ΨrŸœΠͺξBLΟΞΔκΚjΉσvτ“Η- Ύκn+SΖέZ߈γŸ}8n~ύ+’)#ΐ» J§ΛΦbf^Β™ώ±eΑ;α>ΧΕpq§ΑΖ«ϊŸ|<Ξ3q€p&»s‚₯z—–»ρΰc«>―ΜΊΒsg!Ξ Žς§=πϊ>^† ¨ηͺ―ξΞΑ_©τuΞɐΛ6²^+=+*• `E„O§”˜Ol€‘δ9»qΪΏ¨Θ7όιΏo|ϋ;egςτI^―·’gXRŠž­°Λά3\Žα%?γ“ΣžSΣέΤ΄=―]h>x’€7„b$¬.GnQ=:ךρ™œs’ωŽ€g^³yρtžSΜ§ϊΑΙΟyn̟Š ―ŒGάeθsς ‘*"έrŽ}YBI\‚›‰¦ϊu½ΒΠΈŽΰγχΔ©³Kρžo9η7Ως’ β„ψ°†1ό¨™_J‘ !ϊ^Ώx'•Qsf6V/œŽŸώΑO<ς@ΜΚz†ΐ―2²RΠ§’‡’Qδ`4§σ«αβƒhοR`fςΑγCιY΅•ϊ͌‰F$ύŠo€‚[r²­Ў©°…s —δ]P5‡nKHŒ'“4οfΉ½›/kρ˜¨ωΜύ€lˆΧά:#SάΩ―ς:§άιϊΕ§ †bΠu_IΉgδ\Nε‘q!U­ΌC>.εΠxά/2Τ•ΪŸφͺδ–  )ρ峡š?*‘;O=τXΤjŸgε/œ>gN<·άqW\<{69O=ω˜W_U ½8ωδ“qλ+_§Ÿ~:φμ]ˆ'?χPάω–ΧΈ/K§ΞFsb,ζφKψΙΘik…Ηδς[[YI\Ί°²f­κΉc†™Γ£ο S/u8ΠΗΧtό¬06[½ψΜƒω8ιu»™:sΕKgΩ ν pοu^κM}œ7σ]9ίξgJΤβΥ½κK ηΰ‚CΧΎ9΄ Φu½c@)Ο/μ‹?ΧΏ3ζχΗΪڊΖΓntYwη4ϊy8ž`”1€πωXΤ̜Vϊβ#?χOψ| sιΒy?ΒΙΫ© ciρπ­1±gŸWό/8'λgW(Ο[έήWŽΫ«»p>ε\0βφ }Τx­‡ςԐ―5gχ9~=ΒΠΈNΰ_όΜGγ{ώΫ}1)‘ω‰o?Λ2zQξ¬2 L_°²HΐΧEΙG©ΛA»©2H:₯20FγsxoΌλ‡ΎGεσNδ«m–ΜAίάCΗg5tHΌ,Ύ ₯šd©V?Ϋ™θ6aLGIΘ«PӁΪΩ²X,Ιi§· T›2¨'될’˜λƒƒZ HΪν›mΔΛo‹R™‹"dxΠ 0ŠήυjNTž$”.jF8rώ›ˆ-Ic :Ο€SΦ@ύςzm)œΥu)ζqΣΥ£ϋ\LLOΩ λ+kͺΌτfrbΖΧm§gχΔ”Vd§O‘0s‹σح͍8/ΓΊDΙΟΨO|ξa­μgcm7ωΐή[Ih―ΖSΗΞΗλήzG΄ZRΛΊ ϊ4Ν% Fg±Œ%Ο'0”ω4XŠ#€K)§‘\Π(”ε<_J^}ύΤ}+Z]f9ς(Β‘ΤϊΜψΠ3ζή«Ξ½eNε1 ζ< ‹τέ!Υ\ί£αφ•ζυVΣΠhJ=7l¬Kροέoώ?οψͺoŒεs§MCή½˜πΨS©fœdFξp7σΨιbχhfn±o`Aμ,UyΕόahπnŽ™}‡cfοΙΈ/|w³'~i]xΪ<εΣΔόsžJ1¦ΩαΌ(κgν«_‰GLAύ*MžχTΩΖxŒΝν7ήυΓ§ψΔC§β―}ίγΰβT¬JρΥ+'bqšWW*SDΥ_9ŸΥ‘t‡ΒŠ;–G{ΰ’s‰Aέ|{|εŸψsώΰΚƒŸύ„κε»ξWθ²mΣ1½HӐR|3ς- ž=―`ΖΚ@ίy.Κ―Ζ pt;gx…†o ‡ρ™G'Ου €§ΐΙ4|υ~ΔΝ΄ZΞαςσŸΔ2„Δ%:ζ›±Έ'·σ2Hζ‘k…ΰΡIΟ›σZp}€O4Xi (•sX•†‹Ώ@<΄rρ\FŸ2$μzΡ^وξF;&fxWwνwc}iEJ{ΊΜΫωǎΗμόBΜΜΜω-ilωONLΕωΠΣ³“ϋί|³ΟΫμܜϊ‘ΥφΚͺ…ιhs4–Ο^Q°KΛλqζδ’/=μέ7caΟG~Έ 07?­qpΞS°nOšœm—€ !).<Ζ,η―sθEΡγτΒύ£³π™?^Ά€Šθ‹“€|b10=Βgώ¦ζφΖβm―ˆ)­ϊ}½ύ‹Κc@/Μ>ό¦αΎ¦‡!9 ”–<6q@.Ρr<—„―ηw €λΎμϋ―1ΝuDΡM[tΧώfΌξ¦ρΜ6‘AD)x’°Ζ HϊΧΦM|Nv˜?€rΰ¨;^υ†ψƒοό†ΈpφT<$C!W/!\v NΉΎ!`Fq―ά/―τό,˜§ r‡ύΫ ΫuΈΧ©ŸΫ+ΉΜ‰q©Ž/qˆ'žqΟ|RνJ>yi@”<‡ΣΉ]§λ\8›J νj˜αλ\(²ƒ‹ )P”†FΕ9SžΦ >‡ώ•z[隈yI_‡T|Vv©Υ[;ήγγxψΎqψ–Eυ"χΙ/σ©0+EωV&Κφ5r+ΛTφ7>cε±2ηe)@?κNβϋΤYΎ5ΠO0ΰε¬ηyBΡ°κδ³ΏΙ }UαΛq| χά¨mΨhU»œ‹μ«Β€©Τλ>)ΞX¬ψεŒλ΄„Αs~#ŠŸΗΉΉοΫώαΔkήψ₯¦3v„˜M”&Zγδη0­™cΦ՘QsΒΠΈΖπ—ώω―ΕύO.ΗT34Ε“U_χͺιhsƒT!’>aB`ςPqhθD˜ X₯βοi”Έ ΘηΖ/Ω«ήφeρ•_σqξτΣqβΨ£VW|G£dbϊδΥ4ϋ ρ'\…h€ΈΔΖXB΅RόΧ―ΜK_Iχq2s/QαΛρ+ιΥ‘™žό|'Ίμ`> Έ}<ζζζUΗtœ9}i(Θ₯sηbfVΒMνQvjfRJ’gΈ'βΒΙ³1hQΖ¦h{²iεwθȞxπ3OΖჳqμΡ3ŠΧΧϊζΉV ›Ι󝅂‚- ίc«JηYςyΦ<_9ΏvšΣ“gΉ1SYš4|σž? Ÿ>_ˆΔς=72섃aχά4]Ž61FψR?ψοΟ7]")ΚώƒοGϊ€$§ϋ[ϋ&w£/HZY:Go½+ώΒ7ορμΟE›VΙ°¬OMΨPΓ§γ/σƒ_ ~ώς5o$ΰc O厐ߌ©9ηSΰ~ΧΙΑ›cο­/ϊ Λ ήΨX…xςs^θ“ϊ ½ Πg*iŽσζξS€*\<ψΡ{g‚ςδ+νzސLφP†p ΰΏ½Αψ+κqp~o ΆΒ&dόΞ·Žsλ²Π$Œ 4όN"(1‡γ"ΊQήΗ–$B‚EΉXιA΄Φ2›„W¦pTΟτL΄——γόςΟΘύ—˜›ίλW¦^q($ηγ%ΧeOΣg™—a/αtHŸ εq(^Θ&s'&Υyˆ§]0ΨΏEK:G₯Vάβ„kœnΧΦκόžΫ&bο_ Λ4ΞJ©EΒ³²ΚΡρŠ@€zΙΰˆΔχΚ…ƌ²¨[>²˜ΕκΚΣb|¬“3y^Χ.Ηϊ…•X8ΈΧυ=ύΨq)ξιX]^Պό¦8{βTμέ»_+λ|œκψSOšξφ.ξ >όsξμΉX^YŠ;ξΈ;NΙ€œ’Πή3Ώ'N?n%qτŽ[$πš!Όπ“"ήψ¦[γώϋNΖ‘£σ{/|ΰTΜΟOΗΒβLlΆ»qΛ]’‹ΕŠυΉάγ$ΰ-μ”ξΑ\ύ'’ΜB ¦ ₯έϋΟ ³‚G±`ЎP<§ ͺ€%ς²Ύ,«ζ1>˜wœVΩώ™W0»mœOΚψk7`Έ¬._ŒWΎφ-Ύ»ρΘ-’ωζΡΎ$ZρΈ/“’t_4A¦Δάδ9Μω_>Έš#nœšέγ]Iςxz`zΟ’VύχDύί•‚φς|GC„>πMυ\‹»θ?cΰ§μΠωt€σΚXΈώŸχˆΨ¨W<4Άη ΘΧ%€†Β5€­ψ–ύΫqε`ΰ/׍SΛ[qβbW2.‰ Z‚8Ν<D،x&Cn&D§™ q*%/υό¨ §°―½²l‘ψ5ζ―ΖωώkΌϊKήζ]nμ±€»Rΰρ‘kζΚώΠkδhœϊ;V)PΉ~Κ«J«œυ૬ŒλS ې±y™χAW”+χκσ3 œ‚+‡’Μr§„k{ΩφξŒύ"$„Γͺƒ•¦*I£NžŸσ§ŽcψI°H8ϊ²M¨-€”)}u Y—Ο>œ-ΑάέhΕΤάt,ŸYŽSŸŒγ‹ΩΕ9eζγ‰ΌζwcmÊ{εόRLL`ŒŽh^»qμΨγRΤσ^1sγ; γω]vcο—QpξτΙhoδ%¦›oΉ9ž|τq· nuz±±‘«vΆ}ψLμΫ?/Ε‘xπώ“qαΒZ<4½6΄–³γή›Κˆ¨ˆω±+sγ­ωbσ¦EΞ¬θΉωͺ†ΐ©Ή˜˜ήΑcXγΣώ»\Œ™Šu–T¦άΨ'ŸyLw($hKJΙΧκν .tVιΣ4&ŸkΘ~΅ζ†;Ψ‘I+υ=iαΖ>²uαάι8pψζψ[ΏοΏψmΔO­Ÿ?ηοσ²²χιͺςB^QšdxXxF(σΡΛq¦=#NcΞΙΟyβSΨ‹7ίξzνWώ—Έ―Δηȝ₯‚8―ξ§εͺ<Ξ%)ςsρ•t“ςΈ(OM‚«sξυ Γ€k៾'~ϋ'cfJ‚]JΈΧ‘j˜hΔVs4–VΊρ―Ύq>Ύβξ‰ΨθBhR¬ξ%πRθAˆ/ΒC‰[ AyVςD˜* +Ώσβ26nƒΈUυ5ِQπ‘ίόβ7~ρ?›''§έξUυ³OŒ žό§TŸ£tΜLγ§#JnΙwΆ}p΄3ω ‡ΪΒR( 7"α'ω·žˆ£ΗΆ°3–Β–!}‘( Œ<―rΘκœO VΞr¬΄‘°·bKΔΛOž‰ O‘’΅•΅ΈύΥwΗ‰GŸτ»ό7VΦγŒVϋ‡Ž–‚”U‘“ΗxνοώΈpώΌŸαΎιΘΡ8αœΫžί³ Ut'N<)£`1ΪΌ…M #λρΗUΪ‚”ϋ>­λέ€Ϋ^y§w N>q*ίΑχ ">πώγυ_r³ο `>ωΡ'γ΅oΌΕ άK‘c†DάΓf.­Q‘Gc„Ύe˜€ΖΘχ<$&Œ‡σΪρΘ“柾€˜FδηJUσ&¨ΒŸ<όŠΏΓχŸC^ΫΗ¨Κ~ܟPβ›λώδξΫΏβλ΄β«16§σΎΒSά,ΆΒO#]3aƒ›Ή" Γ(i?η ς³,…™/ ‡Ίΐάmllψ‰Ύ›qΰΞΧψqΐ«έΝUΙ½³:_IsyξΙq§RΟ€“ ν@sŒΩ ˜’flΞ{σ[ΗγΗ-z†puαγœŒ_όΰ1Ν‡S€ΰΕ?–’–Ά¨F)Αφχ΅Lh€Λ>LV­κd@„}Cp^Q Ν_ΐƒλ±ώˆν˜ε@ύC_χ'γ»ώέΟƟϊ+ίζ:y₯gύπΖ˜¨:ΖΑ$ΚςΨρ-`κψλ*¬¬Θπ%Μ½C Υ'―υέΩ0^Ι!¬ρ…粬κTήΒͺο¨ϋσ;ŠsΟ8_τcLη°,QT¬`WΧ…,­θσ‘8Ϊώ°+Z«γ pNέLΞ ”™π1 sυK˜¨&|]›—€4BNώ-―}yάωΆΧjΥΏSσ{όq’3ΗΟΔωSηcο~Ÿ:ύaΎ8ΗO+ί›oΊ%.JωonκΌ«iζιΔSΗγΘα£1­2+KΛ9V9ή²g$…Ω)`άO܈qαδ™ΨXέτ»άοΉη¦ψΔ'žŠξ;₯~LΔΤΜ„Vλ|ν9’σ―ΜcVΨ«~Vό¬ήύ2’©hj…ίŸtz½ήκ'ΈGz(Žσsξό†j!ΘωΞσŽΛ$Vψ©Έk™ΔΑUεVΞ9u¨ωμ~pΣΫ¦\G4…3Mϊτ+w£σ°&ή_]^Ž―ϋ“9ώΕOΌ'ώ§Ώό·Mg-Ρ―œΦ=|\3eWR|rΠ<0–΄ ηypΨψΠσ,Ϊ“ΐK|ŽΎϊmWUωuουN›M‡(x/Μ{Ξ5~ γ»|9πuN:;F=„«―ύ«οŠ5ΙΥq)ΰ^+=<£ηΟ₯Žr3·\Δύ–}13₯ΈsυΟuLvΨώUΑ(σ³Θ2 Ψφn?τ‰*š‰2„“υ08B’&ƒΣ…μWnΞΞΕΣχέλ{ξύύΖ€„?6Ήj»@!QΚBΆ0]?΅Φ±†ΑσΏΖλAqu`^œK2 Θ[~—˜' ”Τ~ΊΣπ²žQo»Π5χؘΉ1£ΧΌLΒMθ}Ά“ΗΞLΦJ$“]g?μΏ§+¦nύl4ξφ‰σήΦ¦O ·Ή03Ηο4άr4Άd?y:φ+<يǞˆύϋx{ss݊wuu%χΗ… §•ΆQ}Ω)V‰›Ϊ(Αƒ‡oŠσηΞΖμμ¬_—ϊδΔ‘›nŽ₯‹DΗl―φbrn&έq4½XŒLNΊOΛΛλρΪ7έm)Мϋ¬»Œ2Ώkm{^EˌSŽ_ρ6 œsώb܍V7>σΐŠκ 6•°Αœυƒγνh˜ζuΗά!}r“ΰΩˆΜ £VΘspγ1†ΜϊΪJάυŠΧΕ[Ώόkγ΅_ώυ:±’­sͺΙ²rζrsO°O‚ϊOƒY3VpPψiHΖ8Θ0η„z˜©jHŸTΌΎόˆoώΤͺ-wΣ΅«½φF΄–NipΙOΠcδŒzW ΠΌ<ΧωGΈβCXΎ€GͺΖڜOLί8„?ρλχΕ·»ΔήΙ1QžH†][^άƒΕ(Jσ5\E/Ά·βώ‹σρφ»'c³£<‘’ό _`υW.P‡b™9Rg_‘)Μ›Χ ΑšXΛ±bpΛ–Τm°‘!«|S«‡οΏ7~α]Α €ν:ήΚΥ· ― «CfΖ3&ωςRλίαΎ’ΐ9±dΊLΑʌm¨ΡΓΐ ―ί’Κ0η©ϋ„8ςQς])Ϊ—ί6 {š2κΘLπͺ(GSŽ‚A·βΊ8‘N”YΗQ’#|M]ͺΞ Κί;κ»K|χΆί\[›nΏ-žzτ1ΏΕmzj6N:γc.»wρ€―g?υδΓqΛmw».ζΧ]P»§N>άlaΝ=ύτ1­τΗbίΑCΚ;{χοΧͺ}<Οz:fφξ₯³gM/ξΈ)}θιX^ZWΏρΦ|β@uϊώuνzœΪ΅'œ΄Œb©qΠ{μvy³ θ=Ή§Ο·KUB>γΆB«Pθί+~βζω )(vPd»ΰ4rƒ;\ίί·`Όό5oŽw~ύŸŽ‰Ε}|Ο‘μςyj»fZγ΄b˜#2ω–粓 =σ‡’—c۟©Μ4εωάζόz>…ƒΐ₯…±‰™ΈύKώPΆq  ½rΖύ†Ž-»„6 ESζΟ$HH`NχXΌs₯Lηrμς™Ϊ¬ό›Γo ‘Β-ϊ'cfdΜŠβ²m4«Ε(ΒC‘‹W%Β—NΕίύΪΉXk‹κDdX¦Uιϋ‘%šwπEˆ&ΨbΤ;›m@¬’&b7 qfz‘θ>Έ[r;S v?.8;Η?ϋΙΈχ£Œ½οWLψ|‹Η©: o?Δ@ρjœirE‘ΦT£e¨$fZIι§υ±”YΛ0ίΞβ8ˆ ”:² ΎΉ™fΌβŽι‚ΖΑΟότ ―”έΞL/«ΦΑη2…2’_`—οKcxκ3Ζ‘—έfΰΨg‰=ϋc^ ωΔ£{ύπ˜_geˆ”Γ‘˜šΩλͺc!­*Ϋ*έQμFAc·Ή±Ρ˜olΕ„ϊΑ˜7€XVW–}i>ρψΰΒ>)ώσηβ° σθ‘X<΄?φήrΘn·yί@ž ‰qυΨH³ΐΌ"œsiCΐψPΉΚ ¨˜3ψγάΕV<ό8―ΞΊΆ§5KΩY©ΐςzΎ•~‰‚Ϋ韐6tεyγςΡr™Ž %γ*žs˜>Σ( Ο;Κ΄₯2­ΝυΨ{σΛβΐm― υ«λΟΕΤόήS_6VbsωŒθ†D5Fϊ¨>'•'m3ŸώμΎΗBBςΊ@ψμH6—Τ›YŒΡρι»ώ`h\EψΗ?ϊ{ρƒυώ˜›ΰΉbM»Vφ0‡o~2Ɉܸ@·)"Όηζ±ψΙΏΆΛ2ΜQφΥΪά±P ςεT_s¦AαζέκͺW> nCιid«Ϋ@šς΅Œ­DQι{7xW…/ςΉχΓΏ=Κ΅qE„”~νe―82ύK˝xΰ±U+™Μ.ωςϋ+zUΰ{A”žχ„Ϊ†Αr7"`Μp)9qθ¦ΫβKΏςΖΛ^󦘐‘§“πμχτ05~³Ξά€"dΎ ]”}_Α+Ο»&(s ΏύΫε˜μ>>ηAαΝΥεΈύ_Sτλ꬜·‘γ%“‡›ŠpX„eί)γJΏP‹πE|€λ—³1>Ht…ή±r`h\%8qz5^—.φN²ežδαχ`‹XΈ›"‚ήz\ZοŽΘ6Πi‘ΐόΐwξ f+»κκ揃5‘|ͺ!ΙΟHU―F† •―ΏεͺW\JΧ›|˜€LL‰ziPάψ|₯kι|<τΩOΖ§>ϊΑψδ‡+¦gζΤΧ±kk T σ퐀I/pΙ1>GΎ^‰N»#d°ηΖ¬Ε9 ΏΜ#zfLηιΫ&cΞχw τ†ΆPN4Σ+{ζυκD1NνωR`ΎqΔ pΞb¦™'?ϋ¨ΒΝwί& ©ΝN1gϞ‹±=ϋβμz7.vT…S³jΪu˜#ڞpi@`5«hC}žΥ"j~|ΔώŠNΰU<7u’1‰Œb4 έ3tϊN˜ρ6«ΠU˜J[†ηςΰ;Δ­8}?½©)AΡ°½FrΞ ˜ψtΒ7ϋΉμ6d>ήpΐX0d0°Έ/γπΡ[γ_gβ{^“‹ϋyiΏoφΫ±Ϊί L‡œ/§XRgΞ?>r‹“ΕΎΏ£Šs9ΐF¨P]ύ'η…O¦w4¬–yκΰU_ώΗ”{ν€'Ί›ωΑ(uΦV€© τš€,<J³αi)σM£,ŸΏ)­λ†ΐU‚―»ΏŸ{π|LŒqη²`sŽ@ &+(1+τσ[ρ‹ίΆ/n^lψ΅ΐή•@υΗ€π‹γ1BΏέLυδ«}1 Θ¬ΧĊ uDm€ήJβ-Νφξu.Kšώ οΐ»Π?ή™egΰΓούΕxτΑΟΔ'>τ›19Uwψ¦φuΔδΏΪΙΩ‡jΌ–υά$γ§Ρ%Ε₯sΛ|ϋ.}―:…Ǎ•άq/2ˆ»nŠΩ •U>β„-yVGTζzτ£΅~ίΤvρqE σ―Β@ˆ―ž»'9wΎαΚΧκ~£«›[q±΅kέΡh!°…ž"―Τ»hƒqαˆ:1C•¦R¦H€Η^L6Ψ‰Ω‡ΣMŠ’mhLvΊFPπεgΝ›6 ͺ·²—³1DBFI&.αEŠ•κZ7ŽIρŸ“%γϋa¨Υsšp)ΕWηιFΞ5Š”•~³ΡŒΉ…½ρΘ‹;_ρΊXΌυ>Μω•ώ @?«‡n<ΛΦμŠ2―’o”»©ά·lΤέ«pΙ*η<︈fΉ ‘':ξxγ—ZAλβIί@―ύΎ ω̘/§‰φθ?τΒHΔΧ 3Ύδ7†™ΌΠ§)ΒςƏfό:…‘pΰ§υΑψΆοω`μŸ›ΠŠ'‰h„E›| ^’$μ ZύπtNΚΕV/ΎσfγΟ½}6V†ΈrεŸ†FWΩπ€MώQp’V$W~ͺΓ'Ό©c%R‘RΙ8ΕΝ »Πž„ΜλU₯ρ4Πn<ό™ϋžO~ψ·}M’οζχ5ω :Y¬ώρS‘IY3ν’3©υΊΏβu»ίF€0Z‰cŒΩП3•―mV]”^)―²8^%Όxτ8xΗ=Ωώ5‚³ΗμΣz`ŽΎΠŒ ΗZΟ’Wh‰±δ=WΔtPΐx£Ό{]_W66»ρΊ?31"Α‹fϋέ7׈hx pžΡqύ²beΔ°.IύΆ»ΗβΏi!.nΒ€Βey@‚ΝKM_@aψ^IPεar­Υ?Ο ήKτIDAT©4‘Ώ²Jg¬Š–€wιφ@ιηj“+œœŽγŸύDyΏΏ(ΖΝƒy©ΰ:Ϋψ|0ΐ:Ϋ!ΑΐW!aŒH•ΞŠΉ™F90σ³o"L•XΞGbe©τDEΩ#t$xΩρΑ\θHΈv:Zυ·΄Χ*E “GΈK\£Β΅G_0Πι1ξ+}“7˜γ”ό»ίuΚψΑΤΨVΜLŒΖ΄άάο€N”©<Πzo$Τ?Z⟠Αλ⧍M­ψ7"ΪΚ`κέ.J@;"ΤA‘Ά=7&0”y»|Τin~!^ρΪ·Δ]―xMΌφm_.ΛJ<υ|Άχ?ψiΞΩΊGΩ+ΚηoEˆ κfZYύ[±γcdX±s£!gZqδ„~ΎΓ*γ@υω{δΙ―ΓΖκJάώΖ/χ+―%¬Ÿyά‹)˜"±Π s―±T19œ ’Lo₯τf9©1ς €ζμφΝ†Χ# €+ ίρΏ?υ‹IΘk%ŒYΘζΐVΎ €&Š[ ‚Γ€‚~ᦂωϋbEB=Wς¬ο%@« ‡rυKa”Ηξx6ˆΰS₯¦V’’BiA κyƒ„`PρJ¦Ίάg‚/|!‚kb26ϝŽ>σρxδώ{γϊ- 2ΆΙ1š\.q‡o0`©~θ9&ΞςCNv\ΜN5|Y€ϋΖ4τ1­’E,Ζ•CξΆΫ½h‘₯ΰW‹’η^q‚Ξ?'ž<ŸaώšOβœs“γŽω%ςΫτ€ΤZڎט%$PͺԝO°#1ΚWš…Ό:Ÿ!Ηeύφε ’+!¨΄€}Κ΄ŠW…χεΜχ HαΗδTœμ‘xθsŸŒ‡ξϋT<πιω:α oΟΑfƒ9žcœΒœCή²WOG R Qτ|­’σŽΐς989žXj¨ΈJ3]ϊΟ!ΟΎΰ“gp€x)8d_|]Έ”‘~”kΞ$y%OγμuRΉlOe²θρΩΔ”ϋ}γβ’ Η3ϊ7έzgΌξM_w½ςuqτŽ—ΫPŽΝ σΒ΅Κ¦spJ5ΡΥ‘KŽ$Rή«έΙ)ƒσTŸ€π6]†|ΞSξδG8. jžCξοI4&¦βΞ7}Ήσ―tΦ–bsω¬c¦PS₯5ŽΔMa™!_rXύO -Κ»~&Eε_γ›?£ΌΡν:†‘pαΟύƒ_ίωθι˜β’ ˆΖ³-ΕΟ+5G΄Ί±πσΎβΚτc€KψK¨KΒω;_3ιΣ±Ί)|”>D&|οHyr@ξ°Ϊ/uρ­ωυ‰Ϊ‘~/ϋ Q*/Α ƒ„Aη7Rα]ό>žΚΏ,ΰΛ|—2ς£.2ΨIώ‹  άΏ½ώ|γg8Λθμ—Μ’ ρΕ„š΄³|i„3―ΖvA-8Eϋ^ωAγ%νRυ£Xq|Ε] +ύr'ͺ(TςU―ύ«NpZλ±χΦ»γΰU|ώRΠΊπ΄ϊΣiεdΠ?5ŒƒpŸŠ3Γσ)„0ΠίaӘ'χίκ΄λ†ΐ‚GžZŠ·ό₯Ÿ‹ƒ{¦Rω‹*˜hΤ©Aρnμ“Bζ@^ΥƒΓiι'ϊ1lˆ‘^{λXόΤ7/ΔΩ%R—ς}3 ;¬ώQώŽ_uϊcA(}+p  Rœ:p[Ε @iΦΰ,%dKq| δ»ͺΉl°Ϋ 8χθƒqόΨ£2 >~φqώΜ©²C CΘ†ΑφMs/`Q‡,ˆv€σδ δφ½ν.τΫΨυυΣfΑΫ‘ηΔ’Y λΪ;Ζuγ‚Νk·½νΒηŽύ7Ύν+b^JώeRψw½ϊKς:ΎςΆŠΒΏl«όg¦70Ε¬Χ3=ε”― Αο‘0ͺ«|ίΑϏ°ϊΪε<#ΐΧω%―| xTΖ—ορIώm¬.ŝoωΓ19³€ky ½δfΞ;F‹ζ€Ξk}吆Ό.γ"//‰OΒ#Θ7νκ7:Ζ €‡]μz†‘p…ΰΟΧ―ΗdLŽk…*Ž@Ϋ¬VΨ[p#ZΡσII. ω6@Β’ ’₯­ψΨ?ήΟΖA>«­t©τhŒ±κ—‚Δΐ(ΰrΐ«bΎœˆ‘§ Ψ1 .Jαatά^q*1τ³JΐF@I―yτo Žΰe‡―…Q€ΰΤJιxέ%Qπ€ΒΌξ΄>“’4 XΦ‘K)Kθ«/ Οƒν©6±žYΣ%KΦ©0₯HΉd?.Υη£(Gžmη£Dυ]ϋσϋΔής‡¬π_.eΏxϋΛD³λβoΙ)όN‡|ώsqY‘Θ–mHe_OͺŸί/H(mοLJ“fE―qϊςŽ˜ί+}|+OΆχ«ΒWΏώΈP”υςό―ӊΝσΗ%'šξ«/S1Σ&qΙ ΖιΉbrR2Fγ`Iπ“ $ΧΓb«15Νιk{sγσ‘p`im3nωϊG¦EI ¦%7՘y 1WΦRT(PΣ”ΚΓJιε½Τڊοόϊ™ψ3oŠΰ…cΥο0Ψ ΰ2βͺΔ7²¨rοxΡκ°m&²νΑ$€F „R<σTΦ–Ύ#aŠ\νvWԐw@κ.Œ‚Σ'γ©ΗςNΑƒŸύΈŽp5WΜ_Ξ{Qΐ³°°S―Π‰ΨQλ₯ΪΏjpυmo”Χξ½ –Bδ2άΎC7y+¦[ο²;z‡”=Χο[›bΚ|η]NΰT™A3 ΨyτΨ§Μzƒ@žΞ+§ΦŠ\ύGοyXύrΉυΎψk.(DyψDεΌ‚αρΏΝŸέ·½ώΚ½vΠαΓGλK7cOεN?%ρ贎2€S\B.}v”*TβŒ”…V?|ΝΑψžƒΡ˜Έ~_\ah\ψ[κρ ο},¦ψΦ?œ 2Ωβf.…ΜT¦/b²)½Δγ@„°)6oICY·•ΐcRψ{{γτ²ε.žη.aVΊƒχ@ΜVtp4iƒ@Ύ›- ¦]iΟ„3χΙ„]ρJΉΒ2;‘$XXΠFF―*0?ž͍ŒOqωΰ‰‡m\86ξϋδGd/L˜ϋ7QbH½˜v {'Q½θΐ+]V¬Rτ(3―κ₯Δ'&§β)w+ωΫξŠ…ΕύqσkίΌbWZ^n{ΰͺΜσΛ"ΑŽΣeυ/9U˜Uγδ|¦BΓWής*?£\ͺΐ@Θ<s₯85Νυ‘G+Zπ`8΅6ψϊίkcοΡ;² Χ6Ξ=Ω??π>sα(βς-χΟdož’F‘‚‹i@Ί/€$˜Ϊ«δΠυΠΈΜ°ΩιΕ‘?ςcqdŸ¬?hςiJ±‹pόΥ_θ„à ]ω€y'`ΒSΏHΔ΅΄Φ‹Ÿό ρΚCωVΐ|γŸ”• ). o‘‹πΰ2€ —1ΠΥ‘κ7š3Q;φ  —υΑ\ ’zjΥLUr}£Aπ,M\`ξΛͺΥύΡ;ξφ»,Έf’GαƒΟόΧ5~άΝ„¨fΆςΰS'FΚ$&Ηcs’ζΙιΌΎŒλόδVJ9aƒG€¬ΘκκάL£hcu9^ώŽ―·ρ}­€UϊΖΩ'$%ΤY‹_κN«σ°Έ‚ΫΣΕ1Xdr1 ˆΙGρ³ΔΒ8dΡΕ#Ξ“{ov©λ†ΐe†τCΏ?φξϋcfz ~I‚b9¬( U8I&Ί2υ '―‰Έ„γ²Rΰ¦@ε!_ξΉΉοϊζ…8½ΜΝ&(w”½|ξπ₯^„’“’β&@tkƒ ‰ ΐκ‹ €.,ΡKQJWq“ J° )Q#”΄νη]'σ(Η.σƒq`#a<Ž}κΓΎcϋΨ£Δω³§β‚܏<Π7Œ ΚqyΑρ‘pέA‡ηεVπβ₯ͺ¬1ψ&¦¦γΦ»^ι—QέrΗΛcaίΑX”»ωΞςψΚX{‰9―ΛUύσ…Τ];™S―rŠ—Y±+R―ύ3δΊβχc€$ΘpΒ‹X 1ΟσŽrήIα ntμΕ+ήρu4pΝ ½v1ΊλΝ»ό˜ ώŽ@’‚,šR~‘ž˜ΓάΑepΔ%― mpφψμb4gοU—Ž|νΗόΤ„·έ™XσKD£4ιŒ)ΒI5`8‘šO„ΘonγΖ@ƒΚ‰¨Θ;s±ούφ…ΨΏ§mξΐž_l@N ΙΫί~50 Γb&N˜ΦΤ†ϋα&.ΩχKΠΟ~.ύVM­ό*kφυ²€/%pβpΕ8xβ“Šq)ˆΗΎΟŠδ³ŸόHΖϊœ „ΊkPw€Ί‹ …/κ=P•{]Α{e_Ξ«ΎδνΎ1τ6)z ·W½ώ­Ύ'δΦΧΏ][©δƒVτͺSεoXEl0ΐxš%IΣr£$ηX™?<Š³0±—σ?Κy¦άΌσŸJŠΰϊ„>υ©l5l4hžΫ›­˜Ω{8ŽήσF·w­ nC3^1ζ-Vς‘·šς½ZΛ42s!TΖv[=ξaΊήώoŒO9οz‡‘pαίόΜ½ρΟ~πγ1?7n₯Ν"ρΝ2L³όT(r”±™Map-ΛLDςI—‚ΐΪTΊbu3βί4ΰΝΕΕu•”BαE1^ΑŽεΝ6P4ψVB¨z9κ£:Ϊ²§/$8θΌ d‚` %ΣΖς5ξsˆΥΧ DBΒθ €/)p.K< „†/-œ=ύ΄ΟγΏ*ž9u<Ξ:a%T Κ¦Α–“κσUŒ ξ,TπΉώ‹ κV|”°o(Δ;VΘ¦rFA±=ύw`(w °}ŽΔή‡ή―4Ί·λ’ΤyΎήΡλ}Ϋώr|hζMΞ3'{΅λ¨’0Δ±η<₯9*l…=οΗ@²’η1Ÿθwω’mΌ€οψΙ­4κΉΔθΕζΪj}Ν[cΟΎkχˆ—"ΦΟ³ότΕnωϊcd’‡,ΏΔ='@J,/Ξœο¨€2£"-mnςlǞ[^Uς—n£?γ"€Q?Φ'As1½RΎ¬τω «8Α~d Ε £(Oρ†;χΆeS EΩI¨Œρ•ΙΖjIv}ό»φΚι©^οˆm°υΟn@Qώώj w(«Τωκ‹|dAψΈ©Dα$CIΑ¨‰Ϋ™—†Z‰|ŠeΝ ƒα#ΤK iπγ@q; :gΎ/6d pΞΞΘ@8+CσΚ‹Ž>σ‰ΫX䝕q1h$ƒFD5Napχαr"ψlΰ—Γμ3^½£xε±Ϋέχ7―©οGΉΌΙυς%I|G {Rx}ΕNy| ΨK½΄wΝοΈΏ Lq2[F€²¬˜I³’–#ΫsFˆL¦y9—:h^‰c|‘1JΎ|dŠ’έ»ΐΤωθω© λΛευΏξΠ5φΚΉh―-%?¨ΓτΘύζ•λΩCFš«ώ‚ecά5ή’ ΧΫβa­όgήζ Cΰ2Α―}θ‰ψsίχΖώ=|ρVωΞRe ¦¨+| Ή.J\§(Κ8Ό%—ΑO4όžx’—r\XιΕχωΉxΗΛΖ…‚BOΕΞ}^ρK±τί X„>ŠΑߐO—ός!…άM… ¦δ V ©δPŠKκΞΜgΚPTψη[τ₯lή" θΌυa·2'œAPZ5"ΖΖƊѰ½s@—Ϋ@|T]W郀Ών{¬Έ«¨α,VξSRς’œN³D4"¬ά·σ0 2Hx¨ΨŸ0}&«2οϊΩψ‡Ib•N7£¬μ‰ ΑŠ~ΐπK0°%?WϋΫϊQœV„@ήώ§|w+:ΒνjυΒ rw½ω+έΪ΅‚υ3OxZX|ΡOυρΆϊnaμ9_‰\#39ΑΘΣn«εG§φήγ3 ά04.όΡoύεψάCη΅κζγ-’Vέά$c"Ω@l π•,¬nαΤκ^ΔΖΥό$, l1ΛΦ&VσVŒNg²!ŒΜμΘ0xεαFότ·,ΔΉκ‘²§nωH`ήΐ—mψ|~τΙϊB{τE~{Ά/Wƒƒ©ƒf€˜~βηΚ”rf›έ α‹‚FΔΛθΛ‚ν "δ9”2+ΓέbΖ«τͺΰ‡peΐŒ†‚«#™›S‘ŸG>! „J’π%‰ŒΰΥ?η_ω5μσFŠ|ίΎ ηTiT¨x'ϊhonΔβΝwΕΑΫ_Ig πέΝ 'ŠόC‰̍G*?-) (|Κ1VxMq.#5dtwΫ|§‘³G_ΉΐΠΈ °²ΡŽ›ΎμGγ¦#sΠ‰ώRΒ&Ώ ¦Ÿϊ…όί1…₯ ΙP) h ‚'_ΚkC‚“΄ ­ϊΨ >Ξ­τβ7Ύ}!ζ§Gecp Y+{)ωϊn|n0σύ(ώbψ|cΗkΩp₯΄›u{λKΞΙτI@xjΊ3^ˆN¨ˆiq=ύΚ†0„!\V@7—λώGλ0AH"^΄ΒV<Ώ©T‰„«RwžΣΐΙλύT–Έΰq™GKˆΨX „w;Ή<N/6W—βŽ7ᘜ½v―έ8Bι¦ζ€λτO‰Ζ„w >&> ·|Œδ(σΘΉe|z6&‚yΓΐ ίCxψ7?uoΜMiΥUˆžƒ4€‹e θqΉu;ΔS.„Δn€ΚcMΒ;¦4R8+γͺGŠΑ¨‘AŸΠνδΔHόΗί^Yω€SUϊ0θ€ΣVT0λVΗόΥ8ž:PƒΎώηvΑ$_ΎdόΤŸσY€ΆŠ³!νΆΙΒ†pٞ‚Eα³τ ȁd9δŽ"b@/LPβΘ€'‹›βΌSιΚ²,όJ χ Έ-š²,ΒΓFy%S8ΙοϊΎ—k©όmΌ΄Φ%΄˜*Κω؟,Ζƒt8ƒŒ†±ƒcΛ1+OBΉΛ·4ΎΡρλϋΣΏ—‚‘pΰ?ΎϋΣ11ήτ ΨZ3 ™πΕ(Vtισ2 nlHΉ³°ίj‹ό°Ž1„ΟΦΏιΟϊU<-@΄₯„ΙžxχοoΔz$1•Ώ-¬οB υFœ΄β³0hfVω@%r $9PΫ§γ˜ι0N–ΛΨ Χ§]qCΒΎx¨,™¬Z=CΥmNγ•εΓ‰G―ζθGΒE g2/&rΫŠΡ•ΉA•ΐύ!³‹|­ ½rΑ²˜Nεˆ‘Γu%βδ<0g Λƒ#ΓQΗ}·cEωsΰFΊφ_ah|‘π[Ÿx*ΟiESʚ·ωAX¬;š Βι[ρlϋƒ―δΧϊ7»²LΕrΌσ—7δx$Κ ₯’l₯ΓeˆPup3Χό­΅˜•1a$Ή|AΑˆόͺ―ŸlŽ:δN@–+ύSCφ8ΐψκfς% „I΅0^Τφ^X©! aΟ f\π#jΌpqŸΙ°L°'Tδ–πIωA˜tΚ'`*–²ΰΪ>ωδ Θ—sSΕ€ΰf•οΗγ^ΫνρΞϊEυ-7£a°ςͺ|v—lb$€ƒŒcœ9Gή]ʟΗJΗ¦ζ ξCΰ‹„ρcŸˆ…… _Σ·bΗ24EΑ$‰3 JΪ$Δ&ε>"e?ΒnΥՊ_nM _Ɓˆ­φyT°ž°©‰‘ψΡίސ‘QYβLΕ‡H]—V¬Šξ·β†¦³$DNά Ž9…Γdˆaΐ4ΝΈZ’Ώ«ΰηΖ“u(πŠaCΈTV‚ΏαLΒζg[φ8x•E”Z"˜wυO”\HTΕNDψŸhYHH ˜‹4’―SOύ)w<ΜΈIΧxσŸŸ$)ύηh…ŸwJ…Lw¨8€ψ’ρrώΖ 9Ιp”»΅σϋ)pΓΑΠψ"ΰβκF|πcOΗΔδ˜Vτ"ΫσŒŠ8PΪVΌFέμ:ρ ξ0€Ζ}ή(Ί4*ŸKJ²AA–š1ιF7ώύ――ΔΜΈpmMPeRωχ-σς+,―<ΜςLNac˜$›ώ•f· Θ3!Ρuδ‰—©…ŸY) c0—φpu—(6„! αΉ`€o’ΫΝιζ5tt²+<―tρ<IΩ@fBΕΌC˜!%kϊa9—Λ¨Eœ )~…seμ₯†Γ$ςΞ‡ΙΩ…άnΏFΐ³Ξ+rω£SMΉFŒN6bdΊ#3ι„'›ωDυΙ`§`Jνώΰ―,GΛ7B˜XλͺΕoεOZ¦§Q °0ύψi*οΈ)šŽΛ‡‹ϊI°D+ΤαdvA`ός¨†βψΉ+(βRψΈΗΐΛ¨ηα1w•‡0„!<03'm/©μπe2@Gx ₯ ²\ ŸLΙ…Ύ)ςΓχ ΨΧ¬xζqζ{$H*eΪyΐεετγ]{q=Χ:›«ΉϊχΌ€qbc/e&y}ιVς21eΙωψ`IVY~{φ|CΒΠψ"ΰGᾘžfυ΅ ш€ŒM)Π‰—즀P“Œ'’$q6ψ`†ΑΈΟO‰υf₯όεF 7„3;Š Ε Xχα=Λ1ΓτA~^ΛS⢏‡―…€ΝΌŠζ.€\Ν¨p‰(/\τSω@ИςžͺΜyρά2Tc ŸΜRu†Š4Πφ†0„gΒ‚UΫΓ“;XΗό­Ώψž―θV|ƒN`/5]VIΨι©(ύ³r—|ι‘L3Oα“NέV±»\»σ‡έΧρZKgc”―ώθY9AυΦ2Λqƚωιgzζ1RΕ-Σeόά›ΥλΚ8ആΐyΰιX]κzΥjf)«s^τγkωJ«qΐΗδ%ƒU!FŸ –R·αЁɔΓάH!Υ;~坾ώD{Σ ΏόΕγΏΉklΓ©ˆ•?Œ˜mNcWν„‹/ ΐ¬°ŽΫJψYœ ΡΡκά½μ m ,θ‡ϋ’1˜‘Άϊ5ΰjΒ†0„KCŸ?ΰ«Βϊ‰λ°Σg~ΪζύMbώ-CϊxϊΥK‰…IEiϊϊΏ£`,§F]ΞΙ"Ι²ΖψDŒO]›-ςnkέtTύcv4rχ΅φ³8CΙ³1€lΜisq0’εPμΒ3£)έ΄Θ €νπΚ6K ƒ@8#j+d4ΗGcSΚΗί·bc ­sΪΖ[‹HϋiΕΑπ^ϊ |§n6ΰ†}ύPτθ³ˆƒΖ€R””‚b4ΓJ¦iϊP_Tq+P‹@u;Q†0„!T€PΊ"%Rω§ QbUΘπ!ς€€,fΌz·?β@*Ν²°ΜpbΑ€#ˆ1¦ #χ$7lΰΣJ>ΧgχρšΐζΕSΎSί£¦Ώt >2Wφχ<•°q˜f '`'@žς­ žΒβ €c³7ζΝ†ΐ?[ΖΤxΎ¨ΗΪ­2ΒΚNΣͺ(“λΗl(„e|J1‹K”ν€bdW€΄ΐXf4κ r…Ή‰pΊs³ψρίX‰VO„)BΆΌ Œ]w²jΓ SσS\?βΥ f Qb'Τότ³l"Χ5-1HΆA@Ά­β„ί'Ίͺ†0„!<ϊ|B …ΌœrIaρ“ο1₯Β&MΌ†‘―Έρ‘π«σ……θΕ¬μpuΔ ςr‚qΊλ‘‚‡γsΜ{^›νnkÎ7 ";m›(ι@D#ύR6B?E½—‚Ηΐ―ŽOΙ(‡ΛdŽΟΞΫΏQah|pογg£΅&’)V!ΗQ+hŠ™»φEMΙ<"¨n/PγDsεK„’ [ε.Ό¦$ίuŒϊ@ώ&@½―ΐDJ˜{ˆŠep4&šΎπο[‘Q"‚.Ϊ7Ά1@ά„¬δG:ΐ°yΘήTG9€#μCΌVRγJγυA‘ZΔN‡RdCΒΐfπFa‘A6ςζ (3Ι%dŽ/τΑϋ…ο“½vΖ9ΪΧΑι%βς|Λ d ~r~]XX P¦βZζlΕάΎk³°~ώΈD%ΚΉ'euΥ7Ο_φQ$ ;εΩ• &Œ δ―΄N«ε{˜ƒ‰Ή{υ €/~ιχžˆ)ςΟ”έB?dhΒTšο ˜€.ΥΩβωeΥϊ„M©V€ςΤ•ΉΙFόΠ―.ϋšΫ Sz%_ΒΤΰ0i5LΊ™Y=P¨ΓτΙL0T1΄ž=ΐ/c0Pn;' βjZ…’FΓdΩQψRΕ†0„—2ΐ¬ι“ΧΜΑ$9OŒœ²Βᙝΐ)}~ώ#δ(—7,8*ΛΟ ² ™ξE‹k!ξβ.‹Lα@3 ΧζωφΖjt6Χ4&„mΚ²AyfΩ::šσUΖ…KΉΗhΏ,΄τCfwZ~λŸΛͺ_›œΏqoώ«04Ύψε=ӚΊ$„―τΫοJ‡ημd@2[({ήηΒg[Ώ%’ΤκΎK˜>oƒωΕX€LypDNδM…΄#ΧΣ!wh!=vbb4Φ[ώ=ά @1Υ[»ΓΤCίaΥY~ξlόDB"Ca§ϊ@2F@­Ηƒι‡ΣPaW…»Pw„‡0„!˜exλ(Μ‘F·ΥΥ6+Y ‰o‘}_ΈΘ˜ό›ύDΨL–um#ΰ©<Ξe †ύ#έ¨―¦KŽu:1wήώ·qξ©Σ\Bߐy–sχ¨,ηΘSŒ"r™ΓεPΙηΊ{ x·½Ι™νΊo`_|ϊ³ηΚχυ+I$ŒJη9¬Y•‘θp<.TΗu{n 1·ˆΐ0 pe₯^ŸŸν‘ΤΗ…‡…½Φ5^ΆE=ΔΠΤ(<:шΉΉfόΨϋVbuƒν¬ί_άΞ§œ-¨a΅Cž{]30φbœ€κž(Ÿ―ΪxΘjέ灈ά@™šTQ†0„!$~€5Δ±ζIΏŽ›ˆω%y=―ϋoσΆωP^".™`Ω eU£Γ:°`¨‹𨜣r\άrˆ2V ”‘-V<Λ²j^8t‹r―.΄Χ—ύ‚χLή`y˜Ψ!‚ιΌς˜7AŠ#I/ΖƒΟ8”™e1₯‘’ΫmΗτήkχVΓΛ Ο!©‡p)ψΠύ'c¬#ς-°ΩžŸ`KΆ”οgn•Νύ(ηQžλ—ςoΘ(m*ƒ/ύρΙ`Vδr\χχˁ(¨xO«ϊΰ+‚μ¬tό€Œj’5A&mΘ ΅‘:»£ψΏΊβ/Z/Ή(ώꋨ3zδ›Q\%yͺTi°fΙ3”’}7„!ΌΤ Ύ$#»Ω‘$›w’gt€K’yΏ8Ξ±Θ~uuoγŠλ‹±i( 0”L‘ _δ*o»ϊΫOE§½A“BŽiΕξηφYΝΛ0ρN¨|ΛA­(zΊ^Ζk|Ζ¨bΌΧλΘu£³Ή3ϋ―ώΖ•‚‘παCŸ|:&€ ₯‚EEAšp'ήcfKΤ«θ ˆ°ΈΖƒφToύΫ’1ΐ[F§ζ5ΐ Yr½ Rκ εΏΤ‰ΈΨŽW¨!•(‘ ;υπΤD­ΛιeΕψD•m,²1ΰv71„!ΌΨΰ4.ށ•Ϋ9»ŠΔ°Β6―ΪW d ω€Ω!›π‘?*IB©“ƒ ϊυΖ)xΞ‘l:Χkِeρi·ΩœˆΙ™=tμͺΑ*[jŸ—τŒ6XH5dˆlζ<(EΟ[ϋrΨ*Rπ<ΗM~€•ΉΐρL¦¬ωΰΝμΏv―4Ύ04^ <ώΤRŒ‰Έ˜9”Δ‚΅iΪBΉ•ΙUh0Q*|gK±AXIˆ$&Ž‘JqΦ)—Ψ! M‡Ρ™±ΨšΛa0¨iGJw΄»ώtπwΏ{)ζ¦U₯ŸH°«ύͺόνψ‘/_΅ ?μΣ“šn#€0ΎR*€Γ“ƒi γv.Γσ=Œ‹70U cΓCPŠXpͺhuF-.δ†0„#>€Ξ“30«@N±ρ.ΎVoξSP(ɐP£¬m/πεγ†0„Tβ.<Πg‡’ζ8 #FΘ…ƒ|X׊Όδg³+ό]ψ>Άk*Ό ωςυσͺΏ/Γίυ¨E_O7^Κ δ—2ΏοzΡ‘2^,ά’ =ΝTψ}rΙ“8πSςzΝΐK?γπ‹ ο ͺ―τωΙχ=AΘα^€<ιjΛ‹% –ΡΫWωΫΛO?ͺ#s!ω'ǏΕwϊ{ >μέO9δ/ΎT§Yέ„σΥΑͺ _Π•αΐ\ΜΈϊ74^ °JΒσƒΗ―DSŠžƒ xs\DΈΕVΉ˜f­χΉ™ )X”5κ †’v z­ntΧΉ™OΔ(?zQVηΙΝ9ۊΖωVŒ,Ι"ΕP€αΈΞΟΛ~h”Κi„?Oπ^G{ΡTΗηšρ/~ώbœ§NX&V›ΙθŠΦ―ΏΥ§43ΊΓψ΄A}„uΤκύόψ{pςρͺ#Ήf©WŽqάΖΈ£%Ž5–h¦ΧZ˜-όd @ΏˆάΦ†πB₯;9hΤ£}gΦΆ+8;ΈfΎ@(B-Ι„;Z9ο½ιvE¬yJνKΆIξ‘Θ³/Zα+Ο;§ΌHJœ€›Œ)€ϋ±@ΝΫ(/Rδ#‚Ή;`cAΈ<χߘ˜2Ξ‹ †ΐ €ΏΟο›9` y5,κG€‘m„BO r ˜ ŒΐQp+Χτy―γΛm7bkJ•ΨPš”ύΘͺŒ‚e&ώ±kσΙh@AϋΊJ_u©¬ο o:pU Ιρ?q>¦³Νώ.€Κ'‘g]™—υΛΛvT‡ρT·ών©ΞZ—™Οπω³ζ„G|ύŸLŠ)έίh‘?σ"]ύPιž'°3γJ!ΟL_˜Φ‚ :κ―Ω@ 4>!|E‹π€‘ͺΫuR‰o;#­my[’ φ·#΅d©5Η@fqC#`/ *5‰jL„b$31Žδ–+τž8»ςp6 ƒSκ|ΎΤa\"π"|² Υη%]ƒŽΉ”΄ΚΛϊ‘¬‰Sy€ΊeHαcΛβ8²…e&Ο©ί`H†=Τcβ—l…τLcE}΅”?pρ©}³^Ψ₯ΐεηέώςι©Ζ–χθ‡ΟxH³R”ΏF‘0iΚQ]ΔΟψΜ‚―ΏXΑjcΟž<ΉΚΒά α‚οΨ7c Jωϋ³ΐGs’\>Ϋ«|Ρ› °jη…@Όυ―+γ‘;ٌξt3zrωqγ±5׌†Κ ο©Τ+}Λ…υKΓBνΪ’U/vhx$ζ§FγούΨΩ8ΏΤΡ`ϊ$v ˆF(ŒœŽ Fρ3gbΙπ+Αc<’δ—LRάsε@VrψBυ™ŽaWΔ<Ά]F8|JzU^”φΏΰgmΐΑ3~ζω—‰ϊ#ζΑ#έ]@Yž³Δ»ΞΫΖΑηε?‹Wiϋύό©θ΅Χ-λθ7 ž~ψzΎ|δ[}τΟΫ–6ΘK-ʘΖS$P.lTΜʟϋ4cšΖ4½οΕwη δ αyΑΚZ»(υ$~«2—·Κ #Φ¨Vή¬ŠΧG(> 0; –]a!@γ`a(@YΏγŸδz<ΐ%‡]/|΅₯r… c$Ȑ€Ά:άΔ’ώ¨½ ₯σΏ=]ž 0`ϋr*γ]ΚR9of‘ιθn2Žb ΌΠ‡|3’Ϊ α₯Ώ Œ€’μr6”φ •tύzύ“z‘Ÿp@0VΒ`}Υυ‘dΦφ‡0„P)FDϘoˆβ€oŠOάΖ@ _ δΥϊ<§•Άμž…"kwŠΆw9W…ŒSλ€Χ$7„n>₯@ρ“o‘)Y±žεΝN'‘πΎr'ΠάζB©ΰ3Ο«}š6Γ+Νx3ίNεωωΒΑ+―0Qψ«g—ό“L€τGc³! 4 wcJ#¬ΎωuΎ€έoζ€N²Β ―φ™vLΜ°Απb†χθ.#l΄°"r΅ŸόΰxM“γ@”]Ύ Β3˜ŠΛwΑ‚S&«t―€•œh"L œ½}₯poͺέ…qΏ`€%Μ΅NŒψUΐjˆG΅βUGOg΅ΧΤAFυρζΑQ)ι™f|κ±vόΫ_ΎsSb˜–φΕ u†Α9μ|χ&™₯ $q4ˆόz9ΐ‚¦+ƒ¬/²°KPLsU“3ηˆ!ΙάΒγ‘ΐν|B9CΤUλs‘ nƒ³Δ%ς†π…J5’ŠΑm~xΠτx‰4\ΕνSΤ.WσΛ9MεμQΎ΄k·‹"!i’‹η"B‘Š~α=Υ+ΞJ~£ŒΒ6ΰ”ι”—ΐ³Yu­‰2ΞqΤʟ―ό5žΊƒΪΨΰη ³m7Κ_~‡»χ_νσŽε<δΑ2ŽΉwχδηe˜D{θ»·ό Ο-ωi(hόά/ 0²ofί΅ω”ρΥQηžœ8³j†IKΖ*,™qΛ„$'βλρ‰_ξΦ—!oλ"Σ[ZΤN/wχg₯Τ“νΐτUτ0lΗg`αχ Œ,·#–D°r£kͺCƏϊCA΄Y’­yAcl4φ.ŒΕχώΒΕψδ#1:­ψm'Ρ§Υ¬²Ε§u„K£ΐU)•8!π·Δ =2«οα ΄’΄ H­ŽϊΛ•&՞ΖμY̍˜΅ψδP²ςϋ Τ:„—,pφ“zΆΏ’π†ΣQP>ΚPι;½ψ—r&lω΅šn’#SΗK*@°83HΊνtmΉ’σ’πΕuJu ρΘΗΧΡΏDΆ£Y±qEL>&’<ΒΙηψJqΚi )\€ιϊY&ΉΈbœ\,pWύή£w*ρΚΒϊ…S~ξίύΧΰF›M·Ο%Χ쏫ή"#˜ϊΝypΆσλ}y­_8΅ ΈΚc,/ζ!gfŸVΧ₯lα ”Wδg΅ιL9θ 82zεZΌ‰³ΰs#Ÿφ{DxjΉr/1gt|?] t…ωx—Β/RZ«›ίπΧΕ•m΅ƒA‘4>ΜA{4›+|Υz榚ρ7~ΰ΄&Q9”½οΖ6q—S?¦#ΙXψδ)HeΰΨ’Ι6˜“Ό8u'y”Ρ9ι@n‚/ΰό£Ξ¬Ζ—:ϊ΅TŸq”|Ή<$Τ’†π’ΞvqΠΛnΕo…Œ―τ―i vΊςI2”Όg8pδΦΡ+œέρ 8X)ώBρBΔ2'|ψKΎ³TGzΑK9TκTΠuα>Ώ’'|*1zΕ₯α3nTΠrJιξ—œOΩ·sϋ½X<|eΏ’G;O<ρ _B₯ΌΛ`UΟ+}7?}ο_š‘\'ΊJ§œΣ*Ώύ#M)ύ]VvŸάsυ^dt-A31„ηZΝ›3Ν4"8β'sFX`“―όa•Fb{žχτ³]ί“’aΫΏ!ΕΖΝ€μ ΈUΜΜαšuςχ .ΎζΟͺ}R„. ;wΐrΣ O lM(s2}žD@ˆ‘ο>ͺ ΏmPΑ¦ •υ­ψ[?x*fi_=χ2ƒ;bŒjΨ(¨ΜnG:• θ£ώ…4„”‘xd93'§q ^ ’°όσό)¦δTΦ­Ό<e~δ2_NΒΐ`›5m/F¨g˜“KώσΙ― tθ₯(Δ#­Δ«s!ΪΒΥ΄έ(αZΦ]³φΝ)@<“ΑΠΖ’ψmZ‹?K$ωG–|Ra—L{ςΝ@8β…wUGξθ)"ώ&ΟέΕ@(εh Χ­ςξ‘2‰5Ύ”ζμήCN»’pφ±{%[›n3Ϋ–²§W‡d”•?η8γ9\Ζ6ͺΣ­q8mK |50c‘L½7 ΣjEkc-o~e6ψ({Ο6΅’̘Œ †`eN’fbΚ„ΑA1I!Λ€.Yι£ό·6΅:ίΰeB-ΧιΉ ΰΥ‘Β{¦] ˜žtΨ±*u>Μ ~ΜPuτ&ΤΖ O 4’!# Η‡‚ψŠΰΈ|v 06όSUJžήoz3~τ7–bfŒΆ`ͺTϊVόΔ-0δHƒ±T>…„~ζBωΞ§s…1υ³Όq1uΆ σC΄$ Fgqj’²=οšFcζ·sγ (% Ν΅υs\Ά_Ν^ΐ™,Ξ'œ/ΚΒ‡/9Ωψƒ Ό―ψKΜPӌ‹Γ7SΫt»πθCΏν*’y‘NϊΙΙ;Y7J% SU₯—ΈϊΑpΚϚ‰—΄,κ8{$«rŠZάΪ•—όžιΙ·)3ŒOZΩQΜΈ2A5ςρŸ½7έAG¬;έΝUχΖ‹υΩoλ³<δ&ΫϋΘ/p<ψŠΚ8|Ο§€ςž …•sςΕυΑŸη‚2mCψ|Πφvy(ΌΝ$Diƒ އ/C ΑwΒFψΆμyλ_Αυ§|Λ.7τ5ΦΪ1ΊŽ‘%αp}Ÿ|?'\*F‘KΡΗΈ cœ›Xωχ”F›[ΝFŒLΘ PœΧsω eӈϊ5σΣ£ρ―ώΫωψτγ-u-ζιZt“Αm!'ΓΫ0@π³P›…‹ζ(‰τΌk(ΑεεΣo%ΧtuΕImwμ@6°΄·V\ΓΫhi„$šηl'ΪnH¨'§j:€πq ϋ<γ0±—xΙίa ΰ„–…Ώ†nJ“Ÿα”ή'nάΆg \ιΤΜGšKRέ:N•ΒΙbζ6± ‰Ϋ|gγ—$σlΙΛ’r ”ςf8x‘²ͺ_nπςΩΡςΐΟw¬HΑvZ€(ξ₯‰βJ½’ιΆ[±tβΡiŒI|j x·CΏTήψω sH?λ‚&  qπ1,ΜX€«ρ°0ΣˌωRνΩΒsBWΦ―I‡ƒΫσά₯ξλΤ•‘±F™QKž(KΌ•οFY[1—|ΑΗΊ”»κ3C±EO#ZΡ8ӊζιυhœήˆΖٍεuΐλ*S,ρž Šό@Ίμ π3 ΤpoTx"xχ‰~Λƒe0šŠ}ηOž‰Φ&l‘1’πYύΛρμΊ ΚFI?΅Τ°‡a0c:ΞxԏϊOΏDJ―B¦C±κvƒς³ςβͺJ³S‚―(Aβƒ^\E+§kΫ α:‡“Υ?לw%ωδVŸ@=ρς‰Ϋ—OYψ€A_n )Σ·δΏΤσ Wλ§€όκ(Σ'6αxǁj‘#ε§όΚ7F##SH#‰aΰ9’ΰ±4Ζ,ƒœŽT…`3>d\”\Ώœ‘TFa·'Υ‘’&ϋςό£ω €Wβs‹³WΞ=vo4Ζ$ηά^Κ£œΊ―^”ΖΣΧHωŸJΗIή2.cHφ!£uΘ ξ `pΘΌΦϊZΜΌ­_ΧK’*‡π<Βρ#Δ0&©r %―ΉΙˆϊŒZ6“( ΑO+qδCC€W +Β„©ΌΕL*/ς•Ξo¬tbτ¬Œ3›Ρ8·MŒƒΥ–™Ό+`KŽ› Ή―ΐΧϊUŽΡ Ψ ‘―jΓέWS«/#ρψ‰Nόσv.¦Ηθ ŽβΟ{Ί$+}>&τhνγθ»qvΤΝ{CΐYΰ‘ Lϊ'―Ί,B²ά™Ί”ξJ,νmϋδ—0P«©Α§‰κ*ξ¨'²8΄ Œ!ΏΊͺά9‰Φ”%ήO—sΊ\-c ½BΙ3ž<œ£ ˜gςŸ ά¦|Κχqk…ΤU‚@ WY!γ:¨MβVΔ¨XΣuς– kεY’VΚΑO)}dΒVί…a%gy’˜Ό‚Μί…œο ύ‚<ߜμ°ΣUΤI…™F_¨‘„₯<^Ήν‹'ρ+ΉΑ/y–“ν­υupGcD‚ΖOψϊ*y2dXfYn!Η$™#αpω‚Ή˜έΣ‹Wο-†Χ rΓžRAg8€Y‘:ύŠɞ8ε‹‹Ί,ύ3*Ή£ƒ}ΥΕn€κC6pΣή”ˆ{Zμθ”tΉo`^i cΡ]”Vž“γUΐη₯ψO‹!NΚ8+Ηερdڐ‘0²‘ŽΘίΒm(Ža€ BY„Ѝ”ƒ.43“#ρσ\ώ‘U0Fq(όΚ8rΎ4@γwžj*Ξi AA АEΊ…‘gGύΠ 3ίM3iφͺ#χΜΤέPςͺ°ΐ€Ίa:‚?ΰœ?ΰ*0ƒρ!\EͺA„L*Ÿόμ²χ™"“XMΩNέ婞>xnK*­ΡέNύ2ϊ N~9W&όβ P;e ζϋΤQ8ΟΧΦm χΣ z΅|Ϊ΅ž>Ξήσπ]YZ^~&Ν” €) ρΑοdΔeF<ϋ0T Rq›s9d-F%l>-mde˜έ|x\Ι}ž¦\I«όκ²ϊσeΤ3ήεΌμ 6Ξ=ώΩ›œ²’¦M>ί °΅ίhŽυe¨ŸλΧ$0?9«’u¬²ΤGdF 2¬©2₯Γ=νΦ¦_ψΣγυ¨/=€Ϊ‡π<`œΥ9±₯b4//L@ΆˆqfΤί/γaΕOzblP–:1.DŒ(~W(λχ<ή·5!7ۈήάXtχςΐFtΗΩ!1°8[RάήϊgΫ_Υπ._ηš³¨ΌΫβ=›έθr#‘₯NθςͺΌυ4ψ?t6ZΒ£Ϋ}εo&Β—ƒ‘δ™ρͺC`XΌ8ξΆja7H’π/y@IVΣvN5.0˜ZΣ 0γαΛK9’pΐ•t|pq5mHΒe‚:ΡΕqNv8%sqUαγΜψΔ†(λͺž΄ͺτΑsRΙοŸΠ’—™$$ΤTš ήέ8/\a/QO^ΰ5$(Νot γδΆμ‰›ηΰ)α™«ΘΧΝ3ׁ°ΘA²’Β+Λ¨"a”ςŽ©ΔJNχt;Aέre3 >₯Œ»«ΓώΩΡψ–w/ΕkΚΟ“rYGώΝ¦y·“Οψ»οκ;«φσ€FOΊηErLe„ϋ\πΌkΰ²[±±±ξω`λŸzyχɞΓWοσΕΧΐCx0Α3φp„ƒΟϊiΕ7}fΡ¦v–\ςRE¬Vϊδs]!hμUΊκ ΧυΈΌb΄Rό]ϊ!ά­=Νhξ‘Ιθ1ΐ½ΚΫΒh˜αςΑxŒμ‘Ε;ΎDŠο Β–ΰ—©]χ†$tΉvLeΟ]μΕ·όΰ٘OΑ2hp#δξSι+­~$r:γpΔωΙΐπ1ψυ;}#@ΰ1+Ν3”Α‘ Ξq¬,V'―€ΙΟJβH―α>” œW|χe/ ˜³Ηόχ] νΎCυWω6~ ;_*uΚΦόjΨ‘Ξ ”2ΐξ$Ύ΅|Ε»"@;ϊΓΣ’%¨έ€EB₯AΣόƒg6›$Ÿ ’'„Œo§˜ 9<ΓόDš“0ΘOη:ελ |p³<νrHvq$Ž-Διskρ/ίυ)₯^X>υD΄ΧWϊν{‹_CGi£Μ= Θ rǝŸ4ͺΆ’9&ΓAτ  βϊk<μt²xkŽΩ¨`b7ΦVβΐ―s /UΨΝ5Cx˜@‘jΊ J˜¦*xSX’•ςŠ|₯ h­lΝΔΚ3Ίp…‡3qJϋϋΖΌDΩvΖƒ΄¦τϊΘ€˜š—Μ5£»8 ύ±Ε.ο`·‹€)9wΚUf»:r_."?w3z1=1Ÿz΄ίρ“ηcqZI―+ώ΄˜ϋ—[Pα΄dΪτ³χJΝιΤ'—=!/qπΜτ”Όg°θ”Ÿ±„Α°€lP³<ϊxWΣ(mδWpjE”­ε«TlΗ2ι% ƒ³!ηybΞp’=ό:vB«a+|DP “VΎσεj~-γόκ”•ΤΊδ»ΕΉΖΑhf6\-WΛ^NPύ} ,Y 6“ά2―΅y‘/ϋ`‘8 ₯ Ÿ)έ,“8VȞλΔ7e’„εΥΈͺ€oϋ»4ύ2>ιΒδeίίυς.}™Ρbγη?׍ى‘X˜›ˆτ#Νr_$pέωΤγκΧ¨uʜΌ–Ο“~£Ÿϊζ݁rI€ρ§‘ΐΡ?υe?^Άό•”χ πN”Ftd80¦…Γ·ΕΨδLβΌDŽΒσ€‹Sf χˆ,­oω">˜ΫK”gKf₯P!?ŸοOΆΜ* Žο (Χσu4³Ί$‘¬B„K!p©B‚„ΐwω«œςψ΄0/ώι̎E'%n!ΚnXS–0— Tα½eχώ"Δ-Ωm):;9Ώτ‘ψgu)¦”D;<€ ¨ŠΏΈv:υ §Ώ3ΰͺΙK!ί%Πψt^Ž—x6–ΝUYƒEάY·NŒύLI7D™[ žxβ8Ε‹Έ œ?ωeΎ3£„™ΛzT§f4sU‡«¬u½(apΒ.α<8ζO*o;‘ΤΥ=.Š_<―‡΄ZŸσ•^ΟGΏŽA >θ΅kύdEϊeq΄_ϋp6wνBΨjΗΕFZΐKŠΣ'e%ΏˆΔΰ&Ώ¨8a˜CεΘΧ>u9Ί]wB¦χσu0‡'ƒΉ^ϋNΟ:ϋ΄ι²./ 9•φOά›xκ3§tVrθ'>֊iTœ’…ιρψώŸϋ4|Αΐέωgύt4›“ͺSΠžϊξkόj—EΘȈd™dJύμ―Λ Η *GϊwφwΪΌg%ϋL˜7ώ1Nξ)ΰ«s‡―μΛ‹n€†π<`ίΌˆ¦q,Χ ,(dHͺJΏΗc†<%@ΊΈρOυΨbV»ήšTUΌΧ )b+ζgFγ‡}%ώγ{—cžαy@LΚ.[p(χάPžξ™Κ‘‘Ξ›‘s y#Ό~q†*\Εθ8Z@ CfW}μyt˜4Τ¨ΟOIΓ'j…€S9ςέ>ΩψrΥ―x5 ŸyτsΥεηd9‡ ڍtόٜ'MΑA§Ή«Ύ‰WQkœHΑsΈrΠ η^ΞυWӍSœσ€κƒ_ΛβάΡ]vΈZGuWάv0ΙWΜ§κ« β‘œόBAγΒ+δΓKdβSV΄††|JΊ>ΐεEΤBΙς]ΞόWλ¨αDqχΑ›ΰΉΑ‚ηxζg Ož°ΈDΉ¦ΰ΄b½Εφ|Žuf²ίχ³_œpϊ‘OψR$—G᳎92…#Gh‹/rNέOύΨ%π|Ό3 ώZ"sw€ϋx °w d<@›k+±η%τΊίηfkΟF‘)+@(Ι[ϊ5lrD=eά!³'΅#Β­7αΑX"fΏ*XaVσή–§( FfDΠΜκ”αςΥXΓ/ ‚QΙcϊIνyΕ¨sΎw@Lε7vΚu1!FγcD€+L;v0‘|ŒƒE1ϊwόrΌλ+!ϋGyXή'ΫrbDΆηϋ½#+tΒΒ!μ΄*„AφΩaϊαΙS ”©ΎŽςΥΖhYζΦσ—ε@Θόtΰ” ‘€Ω‘Α„αHRη ”κgd;ΏΊΜtτΔ;?*cΓ„ω§?––‰vνΐaŽopΌvužΘVΌο‡Άp5yMb9Γ€§„έx%άΟ―Žd@υRΖ}R°ί'œ;œ]ej[W ŽŽΙyπ‘Ί¦°Π’» nηφΑz\9θάΌ`(i”ΧΈ{αTIh‰g^²_Œkσ“œ[#¬#<$—†{¦ηξCςΎ―ςŒCž€~ΝOΖΉ·?ό‘MG$‡-«σxκόz–ΰό±ϋΤ§N4Ή_νr ΩΒGqvΏu`ά\ΟΗ‘Ψ‘\₯‰CΏσΓ?|Ρ―#ηυ‰n²xionΖάΑ[clbΚνΎΤΒσ„Ε™ 3 (ZJβWΖ°R©0ΎcΚc'`”Wρ*άeΫΎ-λTJ‚Ε’ε³Ύ[²€GVEΘRκωΜΎ¨¦£Όω)ί&hφd ήQ=ΒρJ_™]ω(sγ(Μ₯Šz•’ΗΨ Q•iΐMΤͺ¨΅gάβ8ΐLΘνύs£ρί΅ωƒμP>•}ό‡AΒ"]ΙλͺΧ„ιoIΓ―a3š₯Ω3΅|?± U‡qξ-βgœ”‘c]ž‘‡―Έƒ:@ύU9ας6ΜβΓr*ζ‚Ζ'¨CIσγVŽγΧ6ΙΌΝSπσ9Ζ„SΈίχ]ΞCΧWΣϊσOΌ€GΈΔݏ<%\βΞ Ža  nΗΠΐ ^ΛtύrW Τύ·8ύE§Ν4w‹HqΚ€C+o' €―\œ’ΌoξΦ«1y§Ζ%―lσ”2…°ΞzVYΚχ%σ+Υe6lqϊ)Υx3’;ο?ρwήߍ½’ =Ι”ΌάI~ΔΤx3~υΓOΊΪ«gΗΖ9Ι―¦•6ΎWφ’ΌΆckΏ+’Q›e\Μ§ά] ΖΈ”ά”ΡΞΨΨΈyL=|Ipξΰ•{mρpΪž'98Όb‡π 6ΘΠΒ³€ΩΚι…‰ <Άα₯h­πyτ„ΟυnJIR[^œ…V7FωΆF'Fyn_νPή’@υΣ¬oR<8ΊήΖ1 m¨ξ”εj«θ-n’±@')Γγ‚ΰ)Ϊ5ΧΈ›ˆW²Ά4Τ&ύt\Έ”—Ώov4ώΡ»–γmΙ—ϊF€,νΊυο°:™Β€€:ϊκt΅εp:Ύ%•ώŒ<ΣͺP+všO F#<”—».ΰπΨΥv9…)GQόœ4EδγœWœΖέwN+KΨΎά bt=Εwšςά―«εj{κφλY]£?†ώ8Ι(™ύωH«8Ζ'^<φ]ψž#όηFΒHSΌ?E]_Ωγ”_Ϋ«εvΈ« κ}vΣtO'‹nε° Y+"Ρ΅IYθΪιβσ‚*Ω锃·\/Ύ<„•?X†:Δ Vςύϊ’η²Œ|Ν›§φ>—Ίάe:*–Rάζ6ΫmΙ3)~ϊ–ŸοΝqπ iŽeŸ,<l_`ωβ29ο–z°ˆRΥΑυn;φέώZ§ !‘rγžάtpΦ[ΰJ6ύπ“Α•[€GΊBr~ 0 Ζ|T—ιWŽUϊξrΗ_ llΚggΐ ΰ²ύ£7‰‚SήΖ‚κΰFBA2+•œυt—­u錁3―Ζ©Gέ"—ΘWšύBξz7AJbί\#ΎοVβŸό—‹175’υ±Κ!@Ψn³ςOL&Μ<οXΨ$#獄TͺΈ­ι‚+ΡπΥ5…2nG”1Ÿρ"ΈŒPœ2ύŒp!oΖcc+#ιϊ„$+αZΞΚ¬Έšο2 £4kΨψ {Βp%ΝΩ:τλ½ΒΞέЁφ«RΏ”«ύΖνVΨ5έύWΌŽΙρ’Vρ*7ήlΔ‡>sЦžπ*ή3RΣ0SSVπΤΓ6>•²}Owr"Ω€ώδ 3<ΩgQGΎΜΔγυŒCτΘ£j,ζί£υe@C0pϊ†πxi@'@χ܎y^€žΔ₯›³„αOjΗ+ηί*β/ύ²”²ώM7,V|ρΉ_ΰ“œ‰₯5VμŸNέΡhŽOFSεΌX`ρ wι#,kMBVœq’Ψ­ά…'ηΈ)΅έχ”Ρ€PC’γΣσ1΅pΠiCΨ†ν³7„η„χSΏ?ύ ο‹©‰q^’ΜΜVbSˆCŸώtq² s "Q. p7Ώ™q  x†•½υ(oβ[’R_εέώμˆΨ©ΚΛQO‰Id,4OmDστFŒΫ΄σ·:E ˆy}c`q~<r\PΠΫώJgΫƒ€χ‹Λά†‡₯ςΉ# °7εΝLΖηοΔWγΣqογ›ε’€„‰ΚϊυΑ2ς…AΥΘ•?wπφPaάTώΩ_22ŽSŸ Έt 4ζ½ΰ:Σ‰rκ$ΜoιD">%/ηkTzF~Uv Gx‡B£Θ`Ό¦α >Ξ“$ίνΦzδΫ&½Ί‚›ϋΒ]ž°π`έr΅=―ώw»Ό:ͺάw+xœqKΉc¨Ž2€Β}W‘ΤηsP’|:t¨―π•λΧs‰ώ\w ~χι8Š―€k\€ *ΡιΌπˆ¦fΥ•4Ušξ‰S’ΣHRέ.Ηά™'²ΞZΌ’Γv©θύs‘šžiix$NΝS²Ϋž–\ψΝΗ#Ύυ}[±Ρ•‚‡<ΰόΖο8aη²Α`‡τ#χ6ΞsΑΙ>νΦF4€όΫ­Άε«|ˏ†@Ο²©+VΎeM.ΔΘq¦ψΪq‹8ž{ HšΏωNΒN¨œ<„η€_ώΝOΕϊ]? ³yCJaGPx"Εa9ί&θ’¬kPε¦ΌQ)b£ŸŸ ρ^χ‹Rε=•Ά·ϊύ! ΄ΐίό_7σΉ=oνS‡p„Ϋ8׊ΡeεύtΟc­wͺυŽQ@? ₯ρθ’wT&f“q +w,`’`&xmΆͺώ›³ρώμ…˜hnΕdS‚ΘΜ‰’—βG¨8^΄Q°|PΘB‰4Ϋ, ₯«½šΎ-”υ§%'a<|&‚Ιγΰ ’€₯Ζ‹_Σ¨@Ω’ζΙΒ‘Vκ¬uθoeˆ’td αgU_€Ϋ]ΟWΪ²H WgŎSΨiΟζTΔ@ Ί ͺ£¦υη§€ώ\+PΎ«3Β@}w=KΊσXέ]f~ΒIΗΉβO>4›T­ΈŽ}VTsΣλ!T&SRfG‰Y3©.Αu(Rκκσ¨\IΦe JŒ.es, Σώ ρΐ5ί;ρθΕΈŠ7y ˆjMλ Kf4$kFΚεGƒΞ+_8½°ΌY. gύLt7ΧbbjZΚΏ%r”όα^(U’§o„‘%Vδ4©σζέΌϊj_Η,Δ“$<•·‘"ΗΣ ·ήcά!<ΰΐ!<<ψΨΙψ†oωΎ8|p―ˆ¬Ά βKΕ ίT‡b$,²L$d-\("‡&‘[rz"ψ­Ιf4ΨGΧΚ…ΜujΚ‚ΰηύΉΩo΅c| X«|ο ¬Aθ"r Όό'&ѝη@ΒΗ§ uˆέƒΡ‹2Φ;y©A}€)ΌϋΐSϊs£{½4_€ΙPΦ0a~*‡Όkh€Ύτ‘HCυπHΠΟ}p=Ύα8ψάFΜΙ0o tT^‚Α;^υξ(|œη?γUˆ)bί?˜Όψ99ΰψŸa8όώD3HΒl ;OnPρπεΘs•:ΰ8!ΥιŸω·²Β΅ͺ\qnƒxuŠΧv^¨λ׏¬SŽv]Ε χΛα—j 5r)'όέJΊŽίsQ\?[άξΥ=ξ†Sφƒΐ Em¦;υ›ρ3>S |ΣZ†1X½–/τc₯JΪ¬Ξιd§Ÿ<š|yβΆΜΣΌYQ»p»ωΛ6ΐ·$\xΘ«y0ΐO—5ž]α-—B—S2%ƒώ³#ρνογ»ϊRκœ:Ι5Ÿ2ͺd—’ΕˆΚ4$_ϊ $“\‰^ Ξ{0.<ύD4ΖdV‘»τyΤΟ]U}€y'€Ύ ίς θι’Oδ6ˆ0Wx:fTΚΏ΅³xδο₯ύΆΏη¦tΟoύŸiΨ;—Ϋδέu1VnΗΓ-ύτ ΩAΏˆΒT‚―υV2~Ι6xΑ&EΚ‡{xΕ/ά ω#λRœRΚ½y1‡βμ4O¬GσδZ4.΄€ΈUVεΰ ίά·wRF€pΗeI«NοπΘ!;2B†„–φβ(ΥΙ=jާXύ3ΆFΑ4χ Θ ‘o˜ΎŒ Tγ«es¨#1«~/―Gό­:ίτύ§γΓχ·°IbbLκ\ξhεOXΪ¨FXU₯A@{™F+)0ΑO?γΞqύψ$lœκ‘>f‘*|$“Γ9†m ½ΰR1u؁'ηRŠ­*·ŠK}ƒΦWΛΔ/“Χw5wή₯pέ`9Υέ"Οε|ΥΡw»pϊcQ΄οgqύω)―μq*‡Ϋ]§έ —Η1ΰ<ΕK–™ƒόŠ}τJNΕ)tœΚ~P¦κΚiUΊΒφM“WTΈ„\|λtpΩE#ϊ2[.m§'TΏς]Ÿ+§\/&š½xκβhόήۍΆ;Α _eΌ—l±\‘ςg7―qQϊŸΉa“6.O‹sO=Ss{<>ξξok…ΎΉΎ.™ qΠύλϋͺXu²¨ΰΉοΐδΘϏύ䘐+δqέΏ95³ϋo—†œΕ!\Ύβ/―fhΤΫZ~-λ^nYA“Vς&FΤΝΆςNUšςv*}πDΛ’UΨΪβF—Αͺ4eXžΚθM5ύΑŸξ~,f)κσ­hœΨ°?Κε,p)”"ξ™ŠξΎ μ¨5)zα3Α+ΨΊ \>¬ΊΩΚσ[·ƒΛ8ύP€Αι ΜΤε&E3tύΒΣFmJ Μ«ύO?ڊoωώ§γΩ‰xΟο­„Μš˜›δ½Μ›ϊhΑ‘:‰‹Ι½ς‡±ρ-ΘpEΙ—τRΩέ@HTΑεx ”ΎxWF?’4ΝtWydβr><ϋty9ΩΈUΙ:Π•2δυqKΩκ¨ΛmΙν.χ Δ„KαƒψΥQwu—Κpξ“@]|VgTv8%φΗ¬vέsΆ#“‘J%Ο§’< §Kϊ―pνLt˜iSDl<+aA$έ¦’veφ˜jp-(G:ρŠ£2ι*_dœ2аπ2‰Cϊιΰ΅Z”u™4ˆπί©₯ΡψΏΦ‹†Ώ#†όκ‘!ωΔ’#€ώ4X P ƒ ωŠΆ/ X9w*ž~θ“"™†_ΖΓ7ψ;Ι#7­1JεŽa–΅\ϋ‰,oq‚*—(H~ʎν2ΌC οhΖήΫ^ετ!<;ΐ΅CΈόΐOΏ?~χγΗτ7ύ €ϊwΪ«&=@,dΖκ5ν+ΩΎ“aβΔ$½(|„ŠVα‚cΊΆς93ŠbyχfΖ’­>όρ*½ΕΝ2D€ΜG΅JonΘκUš·έ•ία£@γM~ό—)Μ=#²Ϊζ#ΩHPωeG5’f#μΘ1žκΕΰ‘"¬g‘―8έW¬ΐ•ύ‡\"˜™γ';ρ?|6ώπ?x"ώαOœ‰ΣΛΏD¨©ΞZΙ—] €Βΐ6$*CΧ];π,mΐΟ4―dδάqβΞΟσΐ‘œ )CΒ&GIh†ΚωάΦτΌKΒ ž\-[•$uΚΉOΥ I«Ž²ƒρηε¨γω:ϊ~q΅O΅Υ՝ώj^mΩAΈχ˜_`>Τ<τ·ϋ•ΖΌΤΙ%{’υOš$έόΐςΠf‘a+(œ˜F¬δΙηη²ιΧwwdΎ’1$8–|γT¨q˜‘ήΈ]9Β₯nσω₯?ξN 36ϊΗ₯»3+£ρ7ίΫ‹³+32Π‘-£’ Λ nδΝτ#g}}`ΈΜxq₯Ub +γΙO8ζζcfnNJ]‹υΣ[Rϊψ¬ϊUuθΚΎ†½—œΒ€ͺ1γο ©l°eΙ{Z±οŽασώΟvž½!Ξ\X‹oϋ—Ώϋχqν¨!‘Y―ν%Ή™Ηi"F|ECb%dE­βJ;ͺ£]†©C:QαlΛgm ILw§›Ρ: U>_όΣJΜΠ<5ΐS'7’)ΧΈ(Τjή/’•οqγ ¬xž eU―βnRρ| mͺΤ§0νyƒx{[pP¨[(}uFΆΠσ`FbLeΖ&Gcq^†H{$~αΓkρ•ίρdόΝϋtpξŽ"ι—ΝΡnυœ½κJš ·{©ζ gOγοO ΕοThPΤf%_„ˆr„φp’Yδ—bψͺZΧψi¬ϹŸzU yMŠMIμΒω'|Σ7φΓY†Έ=wΧΎϋ˜}qyΈ•r„•65qr©ίό?zqru4&ωvcQy vs71ϋηW˜Χ•?τMσ<Φ.VΆoloΗύΏϋk165)’kΔϊښ―ϋ³mο!‹77Φ΅ΰλ}άn¨&xYš?θC|2Zͺ?Ηδ•~‘γώ<¨_££Νθ΄6bαζWΔh3λΒsœ?„]πΥσΗbŒ΄!ϊF‰&±γ·7—ΕR¦%ΝD ε+ŠΌ5ΘwΉŒΔM„¬ ^ͺι΄™½ΤγLΌβ£X\ΛW”§ψξ?Ϋόƒ2Ȑ1ΐM„\.πV?ώŽ;t“y¨Ηυ«8‚ΐ»J§‹vBυ§‹eLΰΊRϊlYƒWθόF¨_lη#s°ΤqcτOŒΙC΄Ε‚|^ψΰ|3>pοFόΩώTόΥλ©ψ•―GksD‚Gγς΅PΥ'Fχ}r)œšΥHΘt+{;:NuΖΈτJ}₯_r}#!“=φμ9£ΙP™u„—¨sΐΉ V *ίJz0ύΩ8—rΟ†;wa0­†wγΌš@‘Θ71i< %έAω’-§'iL„ιΌ>m‚nΑ' zμ³;@”σNTΏ,½Σ–ΚP|·Qκ/αΪ&u%ί€›ν:­_^?βΰŠœ~-#'?½σΔΩΡψζ_οΖΩ 2ΈΗ 9ΐ–ΌhΖχ‘δA69‰ΧΉτΘ«Λ΅ q*(Μ’aM2`+ώ‘όΊ_π³Ή±ιGςxζyi)6ΦΧό[ό“S3±ΎΊk++±Ήžζ«€eg¨,r¦ žχ°pp"Ζ€Ϊή\_Ή·ΔΤό~γ ασƒfnƒπΓΏπ‰Έρ³1>>f₯f «ΒS„Φν,ΑÞΉ¬―9f6+0£  ωK"QƒΙ0ΤAΤόγΤςΘPΫnΆ#SƒG•a|)Uήΐ́έCSΡ>:mŒΆβΌ[ |ž*Πjί ―T„S;lι‘Ό΅Βr*³΅&±±)ΖW|K†AOa>dΙ€ϊόaζBc₯ΌfΨιξG‘+¬<_½ Νμ­ ά[8©z>ώΰfόΣwˆoύχΗβ‡e)?6"!΄c ζ`[V…οψ π*ρΔK‘g¨4>Σ¬žΣΈσκφZ8ςέΗύψ`β•Ϊ*νUšκLIο‡ŸΝ=<_Ό!$@8+4d)ΞΣG^ρΰGu­Uϊ!*zLZ$Οi ;θz]Πe§SΪa•Mwh–ζ+>9ƒtOΨt_hήι΄₯ σO!7`\ tχKΞ;”Ο<AŒiρέgOΔ_Υ^œoρœ?μ@Ζ°ΚηΪΏŒϊ-Ι χŠ.ΙkHvpΣ²ο (ΐ”Ή‚­²Mΰ‡~U ά/ϋ“"οrΣ_«‹ϋφω}ύΐκΚ’οτo4Ζb|b"Ζ''ΔπDΔ†Vύ ωŒΏκ/γπεŽƁητž½1;|Ο Ρ Τ4„ οόΧ±gRΣέ]mJΛ·Δ@-χˆ–ΙέΦFμ;ϊe"81 Z[(ηδ^δŠϊ±-7*5S‘¬1.cΒΒ_ΚSΚ;―Η‹ )Gy^ΔΉσvQ+ύ™ftPκΌ-S βΗχΠΆͺΠo\jEσι +κ67 ŸΗωF—;~»  ’ΌΩQΰrΐΨρυά2&FΫτSνͺf*Ս,π ŒϊSO(⣄ύ—ΰAγ1VυqW‘iΑC!;Ύ4!Ÿ’3Σ#qϋ‘fΌκζΉxΗΛηγ-―jΔΤTΔf'WE#‹Χ‰A΄ν€Ό.H<†ŠkŽΐ±!.Χ/›ιτΝ]Λα%œτUμUβ$½ζgλ"—=ΛYy+-Ε§ΒRJ:fΎiŸpͺQάψ.γΆt―¦ωΟOm&MJ›–«‘α6U?αmCG°ΰP¦*{—“Qο>)]ωπηŒό·ψGP²ψQBΆdˆΓ+~LΩ@ΫŒ…½dΔδγkΡ•κœ‰QΙ©qɞΌDΑ+IρδŽψ+―:+λωςž4ζ‘oS³36HšcM+wάτž=tήΫώΣ33ή`,π2Ορ3Mcγ|ˆM}€—Υ ΚPg>ή܌/{3ƒΒ €Ζw Jψ%λχόz|ξΡ31Υ+’ΝλzlŸ‰πΜΉπA+Ζ&φG³!KUi$Ϋ θΗaB±S• 2Ձ1 —Ι £Ό”²]HΨς³-•“βvY1J|3£Œg+A9bfΣρ‘Ρeυ›¦ζΖ’7Λ§4©SΈ{Δ±XνZŠ»WΒ Ύ%°ΑΨT·Gf₯΅{ KKΨ?7 s ·9„Wi›Je6ι6˜JZφ€Ϊ”ο±+K9„ω%‚“·ΓCxD‘p0ι"=%˜ ψ5ϊS"Κ†tqO%ˆΘ+ΩΜίvΚvU‘z\ϊ#ΟΎjq˜:όΟϊΔH Ϊˆ –EςƒE‚§‡“!_„€βnp9ςB[rHΚYς’Ω‘ΜA†”ξsCί-³βGxT™λπζ8χ‹:ΈA01)K_ρΩΉ9oηsέΏ!£ƒΑ8YΚqfœ{ά₯ϊ&BωωZΰ”?^φ&εΒ αŒ8wq#~δ—>³S²T™”εGΗ 9–΄vλΌ}˜†RΘωPPVΜ0%e ˆŒ€8ΤλWi’ζ²0ξ@ΪKŽ]ΆΤ·ΞΛπ8³ι—ω:=̞=υ±Λc:‹ZιFC«~Άσ;Ό4ΘΧς¨Wψ\"°΅_F a„ΡΔHte$lq]Oi~g>ΧτΕόy“`ι7γͺ7΄•>ŽŽsS|ϊ$aƒέQ9Šq/ŒκΗ •@1˜Λ}\« c'»ρώO_Œο•'γοώΘ#ρ}?w^ΖX7š’J²]4lΎ,€_Γ₯Ν’ˆOκׁ6覚3ag$Φπ^„POΆœ‰’+’δόο ˆβΓ’„Ι³r‡€D‘0!₯;•ͺθΠΌΨw@ω„H¬6ένRœς„i΄Ϊ²Ϊώ3•ϊƒν†sεŸaζ[z 5ϊέΏ;ϊΓ#Z€ς‡!ΩM΄ςWŸΌ‹ΗΖZ€aΥVί~? ΖΌoBvΥ ΕG9ΟJ·ŽŽE‡ν{έS·Π'§gbzv.šγγ–(ύ•εeoι³#01Α‚©ΌΖΜ°‚g %­ΫNΉ†μΐΐionΔβm―Q9υs/†;ώϊΏ|ovΔε%,€ζfW`jφEΘSbςBŸ '•t’-OJΐΥ°Ί&ζΈUh_τ«δ:<χΉπŸΚΪX˜`‘Cσ₯<ŸνWΏ…Β+:Ωͺγ&Έί3@9VλOO ΄y¬ό2ζΥΔM:ŠU/Mna@σΎw5Lϊ1nš)HΏ•IΝx»_ψ0*cςe‰"Ώ©—ΑέlmΕ‰3έψψ#«ρkŸΈΏχπΊ׈£ϋΖcΟȊZ@Φ*”ΐπ·‡πRΐιΌσ@Ώ/‰q χρ‡pγBD‚Β5j_緞nŸ{ΏœΒ†ξΝ¨?QvζΓ·RVŠƒ_uZ1+~m.«εG›0ŠjEI}ƒνΪm+πάΎO—ιΟιό3œΈΚ/―o4τbͺΡ‹σ«#ρχώGΔ{ŸlČdEƒ:ΔwΎαΡ Ξψϊ?Όͺ0}γ₯b°rA{Όw€#Ω2Υ΄ bΧ±Ρ’τ8(ρο™Œ—ονΖz«c Ιwω₯ 7ΦΕΛj“ϋΈ«}cέ}œ˜œT²V¬­ΖδΤ”ψ•§6’%#‚•>χπρφ˜»±χΦ{brnΡyCxα Σ>„σΛρξί~8¦&Yj’(ŠβBω±Χ₯lκiDgσB2,š1Iί@43’BπΩάπGœtβπ€‘#Ÿ*3ٟ ΞD‰hRZΚά_T:ΟόsίJšΧ.ΎώΦόxtfeas9ΰB; ˜Fyψ<ηo ž†Δά¬ϊ{³cε>Υ‰Π`άΒθ©~n”ό ’μ£2ψˆ—T§λWPFƒv0Θ1.Έ1ώΧϋΌbr©t%r=C€ΗΈapcm+>ς™ΥψίτΙψγδαψ?ήu*ξΌγκ»[ͺK©SsΧίκ€Ν¬έϋG­’δ™(^ž!άXΰ'qͺŽσ £Ψ’Ζ βγ* ”³¦–ƒk5|~Ig”)4†3Να„–ρiRΤζtp©-Σ)KŸΐ³\!“:;ρ}ƒ+υΒ‹ƒΥ~†©d—ˍ‡2΄/Ή’εΣG IWΗ§ŽEό΅_؊Ÿ=£RΪΰ‰ΗΈΦί+Ζ=ΖΏ ~Ν›Šζ’@²†§ƒ0θαUKŒ‚l~Κ<³σΉZυΚηΏτ“-ύρ©Ι˜šž6ήΕσηό ¦Œnd•Ο#€ΘˆIά@10ZŸο›σΖΔT\—WxDwsE–ρωVŒπΜΏ¬ςF―mζμN7’» …•τΨΣy9€9Ίζϋ0,ΔπώR  ΏW€­>υ£‚Υ>od˞'zlϋω5‘AΛΝB^Q{lΌ1Ε_ޜΘkΔXM(ŌIΝΰδ“C˜Ί2ΙΒ aΰέΥ!ωΰ‘g4ξΥΥ^όΜož?σύoϊžGγ—>ΌrJΌ P •³Αψσ6,ŒI5΄°’χ a¨s£+ωΙΙ)εϋF¦4<ΤW_"t_Fό^n€Œkjf:Ϊ›μ αTL8 ‡o‹=‡†wό± s¬~‰Γμ7όϋΨ?3a’ίκ±ΕΥ3±zή”βΫ!σΜι¦u…"Ρi­ΔτΒΛbfζ6ρΧ€”!z…€ΉŽ7βωΊ:i’}Έ‡YξΟ΄*R–J)AČ’dυL ψ0£σQά<š‡ΐφΘHY–uL](^ϊ£ˆ-wσk%βf‡ ωΤz4/Κb–1°yh*F΄ͺφ ]EhζεΤsMU>―;§1+­§ΆΈw AΫKJS7F'1‚Sa7 σΣ/I5―twν3’t~―·πΰgOύFΑS“3^r:όϊϊ‘:σέ|eŒέΊ>’ό­ΨΠξoΖΧ½e1ώδ;φΖˏNJΐΘ†α α€@αρ!5’Jύ>‡QΕi€ž |άqI‚C&Β5ŸP|0)5­Oέ6μ@#³€ΩΣ‘ŸL ΖSٚŽΝ<Π’G,"wωΈͺ M>(fhJΔo4ύ°?jx(KΊ3JΌ:7ιͺCόR•Ή<Θφ³lΕwžy$Σδ&eŸ8ρέοmΕ‡Ÿ‰™YɈ θ_ψβ¦^πέώβ-ϊJ_r:ιH•?±έΏΪφ<υ&Η,§ΖXˆ(έ—BΥήUςΣίΌ'¦-ΙΒQΛNb^­FΠΟ©™ΙΡ\Ρs™žγRϊι€ΝMίωΟ=άXΨRœέίSМˆΓ―x“{:„/,“_Κπς½"Ό‰T 0°ˆ΅Ώκ‡pν”'Θλύ(δϊI…EŒfΛ±AJΆIžPrζZχ`BΆΘ³( ,'₯Ζ―$₯GZˆ;λΔ¨½ωq):6λχη„UΎ!Eέ<ΉS›<ήΗΦΎΎ?),E^―‹»ΚΑττRαn~gFF„ϊαΟ Ίs¨nOsαϋδj•χ} πϊXFμρΊΛΜ~ΣΙNγμρΨ‘„γTO5―΄&tπ./|ί7 ‚ςΤ/ 8ϊ&|ž ˜“Α³Όά‹Ÿ|Οιψίuό…ο~(ώϋِA7žο1χh% -8ZE`κ ‡Ÿž΄IœΞ81„«Μυ +σοsQά%WωΰVG Ζω.[σεS¦žZNωβ€’MΥ§B­ΈΫJ:*aκU}Vώ*αΛg4SκRN gˆΊ(·F\eœNΩLχŽYΕUΆ¨Ϊm‘V|σ…ΫOr@XSβ_ϋl/ώ—w΅βcGΜMj²dΠΓƒlρ3wΘσ―ό\νΛlvC΄ιΙ₯Ηn?m(sΰph{¦"¦GΦU\ (|ί3R7wώ³ΐ€«KΛVκάό7&Γ Ω”Aαqδ\π©`v'F›Θ€nLNO»―ρΙ‘ςΏŒ sž§σ₯ ―ϊ–Ÿ‰5)Ό’ˆ™ ς^WV«ίWέRΈ%ε‹Ώ©Ι’υ 9·7€ΔVcί­_#ΠrU3Θ51σ•κ€.±Q ‹’ƒŸ@ͺ€Λ‡’΄2ƒϊZψΚδγ=ΌY‹›φLϊζ17ξl¨Bγf>κgεύ{Τ?ΪQX–Ή?(΄"Ϊ7‰3Ή‡ώ§Mω|“' Όέ¦?»ΝΞο-Κ€˜‹FGΜΛΧ/vΜΰ )^^‚πρΓd+πŽvJ{ˆ·;Aτxs|D/ΑBsbΓ_BdΪdΏWψΜBF. κΠόδ³uξ²/-nμ›oΖW½i1ώτ:oΈ{.Ζ&d»¨T£mDωKbœω6Œ«=|°ά ™Ένα‹Oκ.(i»³Μ»ΐIƒˆ„+ή`=8FΡ‘¦?#.GΠiz>˜α]H‹ˆ’‘·,“†€³%ϊίQΦi‚ΣντΣI!-γ}gΕ7/m&ν‘–₯Σ0‘/ΎΝ<βRšZυŸΌρ}οߌί|h„'zS¦¨SέΎ βή₯¬‘CΟ'Ιs( μ7œ<§&F6%‡Z]θΌ˜)[ο‹ο˜vζήxg#ΎηONϊuΐ¬ϊ^8%ί²υΏΩ`‘Rχ€4Ύ@άiΘ?΅Ι»ΐγ>‚.χh|γ“ΣqΣ«@vo—^ΐg?oύφ_ŒΓ2[{λlχ‹aDbΘθΙπ‹€:δ΅Εcm—Γ0θnœ‹ωΓ ΖΖDœ*Η―πŒ™Θ<…°gŽρd됂8ΧΰI#žΜ¨ƒ–έξ°%‡7ςω }”Ϋ;½99VδΚσυ7ε5VΪZ½σψ^#­ω₯-Ɉ‘ΐ‡|ZΩ·o›‰Ž:΄²U:—Fajό2x½'’>Β₯ƒύ“VΆ£›9―±σR!‘zINίΉD‘ΊΔ›b\Ιcα€0t/ Λgh£t™έ―Fw%ŽΛ*ĜP€:δ{Ε…₯†02Έ /q/a|_Χ8Ve 0εχά>ίψŽΓρΗί±?n9 s,)ΦV==Y?Uω[ΰψδ(L WΏ? ·ύ!< ”ωκC‰οN8‰—'_ͺžŠ―πŽμ‡°K4ύΨ­τ υӈˆΎφ©­ω’'ˆ;_ΕUγF%̟|zWŒΤDq»ΤSst³”sΘx;”ΏΓΰeZSΜυd~ξ DΑιi½ 6•φί?ݎϊP/–7G‚ϋ™©ˆ1±“ΘύCyΝ_τM–|>π“1ΐΪ/^Ž]ΈΘ•Ζ2 #εσh±~ υm”׏ΣolΙ‘ΏψeρW@3Z[c~±§˜mώ©ιΗσέώπ}ς7-ςX ςΖœ6όa>ξ—ζΆ/ωrz6„Λ/iΰoώΫߎŸΠ1;5[νθmŠΐa.˜°«ΈώHGχ`Θ9Šχ:Ρέ\Šρ™#±gοk£'βύΩ,Žήxg ₯'Ed%δλqb&…~¬άαGΌώ]ϋͺΐΫίscΡ^œΘ-{‘pΗ=Β€ύF—ΤO™ό-ξPΉ†β§e…sŸκέ?­ε/¦eIμΥΏ΅‰ΠJ&&_­ζ%}Z>sΉ4 άtˆΰΗι#²€u(₯δa_Ζ‡0 ,t„¦d ύN*ψφ2\•~Ό‘™Œoγ§_ΓQω΅­>N©Η?έ8φwd?1Kβ—φίΖ‘/¬ΑΉ΅η³'ΊρύΏΥŽOžΨŠidΟΪ8f1A] «ΗΧν}€όώ\*—)3_’κ4ΕΗ€ˆ‘;Θ Œ}ΎΒB‘ξPŠxu05μό«Ώ΄oΉ΅§Ο^ΘWϋŽMΔς? Θ}9S“­yΗUi\€ρI«σΜφΏΗ*βΦ7|™ŒY,CΈ¬π’6ΣOΔ„ˆͺ)βƒˆ·Ψ@y•ν¨ε2ΐ–”ΌγbBnόΫj―+ފ½·~΅σ˜EΫŞM«q_Λ* ‹cψŽΖ„₯μΑAη%κ(&΅…Μ Ή<ŽΗž^gοdΎXhΌ€]+sYηΘ^ΔV?}=΅c2:ZφŽΑ΄6R‘ZyOκΐκŸ•κ€©=b|vx’ΐ_ώ’~iZο œήΜέ5λ{ Έ1. ru€RΨ; )…ύqœ―4ζ›i Ο «f§βΤηz(KΊζ¦”Μt—!OM(Y&ž»ςϊ؊0bNe»ΥuΨ·§_ωϊ…ψŸήq$ήϊκ=13«qJθ`8d[ξ¨λΑΟϊε ηagzρΧΠίKυ«ŒγYAωΟ…Ο{–1;yw5Μd˜K€³ϊΥ€»¬θΑ'‹τνk΅νšάO+)ξσ+’βό§€+g:•cΫZ…*‘W¦$ ΰeΐE«ίWτN"mΐΗHΣa+ϊντΆSΚ‘EίΟ8y.βG?²ΏςιWχ~—ΏΐOχ`;ζ:ί—πd·οΡ“Αœΐ¬*ο»αy ήίβΡb°Ωqΰ'΄Qδ‡ψG""&'GβΗΚDΜiA΅ΊΊγ“’UΤ/DχEa―ξ%Ϋx΄/ί"š7±;pY€ΊccΎ| `}υ—•‚—¬π‘ϋŸŽw~ׯƁiY¦LB„].`ΐhύΛRΎ @άοxgύl,άόΞk`ՊK ]U•Δž|ΔJΛΫκE †’Ηη3΅΄i|,rSŒ%]e|˜…|VΠζu(ρ½Ρ™3D^>§ό5΅/EΡΰ·ώ±4ΰQΐΗVc„—wΠΥʈΰΑ|+ΝIΫJΦΈ0R2n 0T΅ο'@0¨s£ͺkτ”ζ―vFζy!ˆπθ£ώ<*δΊΨ%`χAaίυοzP―GξUΠmxPŽ~+φ§ ΦΓ€;mG-UF2G͏x€ϋ Ζ4~γ ηΡ,όόeΚγwε65ž.ΈεΰD|Ε—μ?ρŽCρϊ»g%ˆxoΉ†Β(K› œ~銠PϊIΑ]‘Kυ|>œgƒ‡Ksψ|aκηιK?ϋRυ—±8kW~?Z΅žšcφΆΓ©ΌvζΩσ9.i₯l­Βΰρ£Œ* JQP”Q²QΔN5έn+^Q&ΎΛ”4b’•z)A%‹ŸqκJ―ΰς'ά7$hƒϊJέΕυρjϋόEΚρέώ5­=~ώc›ρŸ~ΏΛ#1)Γή² Μ‘ομΧΟQυ―‡A―CΛnc{ŒUΪ…-Τ―QΆΗΨ!„―™δ ˆw\-xŠςiqςe_Ηˏ6βώΤH4gχϊ%>]ί žV0²ΩΉ#ΞΞΐΪκŠΫ›œΞwp_N}"εΟ#„tκŽ7½S²ƒλCΈπ’5ώξNόδϋ‹Y^w+"Gqp@―ΥΙ•¨wΨzη²;¬²• pgγ|LξΉ+ζζοK AΘ(wO)ŠL Ηʞλΐ萼ψEόd?ε‹™0Bz|ƒ@Έͺ]œ•Έ6ψ± ^τΣ•OK^½f±s³B—RνςŠN7rFώ[χ|`F«]•gχ PΏžF}ΆP ]aβͺ#οτUέΜ‘|6Aψ”p“χ¬φ’A{Σ0ͺƒλ„hP:ꏿ((!‚0εέξ—€ͺ»(aυW³›·=θΐΗ„lπwWΤ.Bƒ„”;cœ!G±{:ͺ¬₯φθO+0ηΜΐSt γ…zιχP/>[–mMγ;nšŠ―~Σώψ£o;―Ύs.&¦t”ίΥΙς͍enςŸ>0ξάα %Ηy.iGaΑ³܍w ¨(φyu aκσh§βμFu\‡K5 μΕw%etϋ%l+yNW:­%-NwΐΠ§!η€Ošϊ‰6Œ«²u«έtζ8HΠL⺐QΰΈΓ%Ν8ώίxΤ•αέNγΪPυ?ΣνΤϊ9>Ϊ‹ΝΝ‘ψ΅ΟlƏh#žΓkN2λAδdfd $q[—UΎ’Τ+Υz·;ΜΦƒ)yqJfUcωq αΑμ0€˜½7)GYʝیρGΧbk‘­›¦SY«­Qnψγρ@΅Υ]Λ{Κ²»“3/κCF΅‡``«\m7΅Ίη‘CΆϊωxPp’|+[ϊΑ_xΌΘ―0φ€(_J™6{Κλ0&αaΘά’ ΰ³ *§2jI©E‰+δ€p]^}t £Θ­Ζ™(FΣ흉RWž‡RΨQJ:7uxg@)wέ2_σΖύρΥo9―Ί}6¦§%D…ξΥGIrYaδυϊm¨M_ixFΣΟ·apύΩΚ)½  Zz:οφjL'…P¦«Gϊg.Σ'a;ο™@5_%ϊ —@?‹ΐ`΅UΡƒ‚―€Nxί μ"'†ΐwΨG»Z§γgNŸ§*NϊȘ Χ< ¨U~\LΌ)‘τ›χ΅βΗ?Έœής›2π=ψπ¬ˆέΚ_Af‹ν”Ύ·όVβTŠœ©AΡ;_ Ν°Ϊ‚πAA―σu›RΠ“Ÿ^rZϋ–i?ΫοΗ–;~oΧωΪ‡§@«ZZsύ„ε# |ƒ ͺƒ9l j¬γη[Ρ»¨Ή^zΥH——c ώ¦7ͺ 7?6Ψι’oφδA†ά„ASd›Σλ4Ήm˜:Gˆ#PiOaVχ~ν0I:Έ ηΤΨΰρS ΓIqϊ@»œχQŸ;Œ Υ£Ί6ΥgΒ·™Œ/ΓΎψš7ˆΧί5{ζdšϊbΌψΌΉ³^ZΙ„A―D€` ΅——vTϋ<ΪΘ”@zύ$…˜³~^…Š ωΪ•#P §Χrƒεελ_gΝεΙ yϋ!(LΤηΑ΅dΤ± ¨ξl’δ–Ά²Έ~πiΞΧΉ%ž![y˜˜FN'Κt°j9ΧQ|ϊuϊŸΡ>Ϋ¨C£Ÿ7`­EΌοsρSΏ»žθΖΤ„xƒKΊίSy!ωΐͺ?­4 ψ_ΥΥ„i“Ίαd<­°/ΊYQ-xu ²>Ώω2 ,Pΰo₯₯‘ŸŒDKαϋ¦­8΄¨Υ?kψΌ έ‘L•bGΠρͺ_oυϋύ’eΚΛ7ζ]μΌόν(αΚjΐKψ©ί| ΎυΗ?'΅jwCΘiΡΓ oŠXeZ‘Λmρ.?>—Ό  xoσBLμΉ#ζφά-βε&Α¬¦0ͺ ¬ζΊv¨ilΫ±eNΊ·½¬εPR@b6”arΚƒΙχ*ͺ©FŒrα ŊB“ΦάRΊΏΰ‡"–2‡wQ'V£Ή‰φΝ2φ›ΡΉF?ͺ|γάfΔlΣFΐ֌κ ίήώΫTν#όΤ<ο ?*Ϋδ† .ΐσ.21BU·τ£/Θ1ες„_)¬8Ι ϊMuθj,^i¨ί\ΛOE¦²ε{ވ¨k–m̑΍Wβΰ«,ΫυRαYŽ~:0ͺ…Κί/AώhZΈCύYŸΫTš·@51>OΚ  vZκ[[αƒ{Ηβ½f1Ύζ­β­―\ˆC|Mq”έp)Fϋ*―?‡τΚ «Χd퐼šυ…€«λΧ]΅ΞTρœ–αš" 8P^³X" 5kGΐΡΑ4 3vfe;ŽΏEZϊΫi‚’–sRpjΓ:GefœWηšƒΐε)š_§'³Ζνι @ §".q|OD¦d·ωgοΟ£mOϋ>¬ξ=ηo~έ―ηF=± 8‰$‘ D$Lˆ’(1ΡhIKqd―eY^Φ²€8ΆΌ;ΛIώHVώql%±γxI€IA)Ε™  š  »ΡθιuΏρNgΈω|Ύ»κάΫ @’‡yξ:ηχ«iΧ]»vν]UΏ)Έ8\Βνα7œ_’–0GŠϊ3#Θ{?ηΫ@HŸΏΊhδwφΫί΅λνKΟ3ά·ΦΪF杴Αfθυ±“m~wοπγΘsχ/ oΊu9^&(ο”ϋŒ|‰Ό0€δπΒ_αΝΦΑhΩ…Θ—y2ηφ³kνΏωΛ'Ϋ•_μΧAΧσ’Ώπη«}]ωK·ορŸnle<{I`Žžu’ΰή7πΆ?ψ‘”ΥύώΈWδΰ/ώ_>Ϊ>ς;_i'4΄ ΆF*ά•#&Δ€™+ό~3 /₯蓀櫁υ}ž•΄Εά§fνΒλώ0†q―[RW~­&ΣΠašlwP2ͺbΰ-η V™aΏ…5œ›‘/_λ¨Η8RΤGρ½ϋί;p„ΝΛ‚άβχzΏοφV#Άy‘Aχ₯λmŽ‘ŸένwΈ™™Σ^/ά|l‡φaθnίΚ£ΰ;΄~0²}Ξ7“,¨Fά‰‰Χξ&<¨Χ BΘφ€-0ˆ>50V¨₯”Λδ_wUœΘgΪ·ξ2Η†ξv’—¨΅­»Υ(Θ“M‚„‡ΥEΑ›Η 'l>υx΅Kυέ©<&&s™€ς–³{Ρθ:w²½Ξo 0χH€Β}]νœ?³ž ½›okο}ϋMν~&W›(jλΟ»lˆˆSR‰[|ΜΆOwNW5ύ«9atδ­Μ#”„ΈƒΑτB«²pqDΊλ°αΗp„αkέ«bτ¨¦ b+<«bζXόȍπKλ^qqΕ„σ«:L/vΡP0Ζ~Mj§3κ/ΡJ‹“Ÿψ΅SUXάΚχGΆώHΟ‘tŒ΄T–pό .ΞlL‘9”ΐž™΅Ÿό]Œn{ώ:sj&σω:)‡cέZΞvΊP7νvά»P•œ2nUΒ;φl‚ν0[Ίτ3vs(ŽΡ9„-κ 9VOx“χηYa έRc½jϊGjνoώΡsmΦN΄.>ƒο+}½φο[ώζzι’άd:iϋnωCpΖΡ·aό3‘ΥύΎΉWδΰ-ΦίkΧXΑ:FŒΜˆ nύεFΎ―ω»*u«ΜKu` Δ’y ΐ›}LpΎs±½η;ΫΦϊ©L$κN²ΐγͺ27ΕQo>©›‘cΔΓΥj#ψγ=έc0. d†/0tΊk-8ΓΪΦ΄Άύ€:³ξ\g·<ž«ρSLάΦ?Ιlόβ~ΫόNφω θz]γO[h~t€«ς€‘H0ο Hή’7ž*r‘™l:„Ί€!m1κb°Θ£>Gΰ‘ˆCό΅³Ÿ-“qΛΌ£ ‹ΗI†πQΟεΓφψ‘Νφ{Ρmš•όι3g3πΎ^λχ‘ΐ½΄ίrnωΫ›»;ΧΪΙ3ηΫΏύϋ‚Uχϋλ^qo@Ήπώλvα4+aZƒaαΝ`«ΗΌγ #ζΝ€nι•ΑwεΡwλ?αΊ`yp­MO\hηnyG;œ1Qΰ—'? ‘ˆ;υy'/£ŠjΜpΖνΚ6O8Q«]γjhLXΓο¬ίηύ5Όΰπ΅ΐΉ©ΟAλ ΣX;θφΩ||7““ƒϋO·…oΜ ¦ν;σ|50:³…<Ξ‰BHτ©“ΠRtϋH`^|M^PΛ崬ΠιςΩ †ν Γη”ΜI%κn‚qη”kžs2αΘ³ΒLŒŒ§cΊlώUT oΞ&ΧͺIšŒΰΜr;_άξ€ν¦sD~θsΙ zZ5ώ&†·ψ1’ψκΠH›~ώΜ΄½ωήSν½oΏΉ}Ηƒχœl§N U— ¬_~Ε‰?唀J3nψΈΧ#Γ„SΚφ¨0½hΉc‘{| "jŠ,ŽˆΞπKέKRΒλͺτ*qΧ‰νE˜zp€U~?UΥqIŎ₯wι­ευ_εΌB.Ύς—ΒO„yZaSk±\α£tƒ†5ꕦl'oΔύ₯lΙ~ΚΗ18o²Ϊ_Ξ'νKΟΞΪ?ϊΨ^ϋ©ίΉΎςΓyΛO\+λΠηΈ_Ι‡xρ{™CZ‡p†νwϊΐ~‹¬Π₯έ™οι¨Ξΐ 7xΥΣ\χ°.8δί±DAVΈž1Ό+¦ˆΤΰqq½]GΝ³―FyI„?ΏD‹¦R‰^XxМΜI82ŠlE.•±JΉCρu₯οBΰ™ΛΛφΡ‡χΫO}|§}ς1VϋΘΉοβΪ81Ν"aAX)Ÿœ³ωTΈψ8•±&aΣηD~F¨$BΓkC:Κΰ{xΒ9ΦΧθ,6Μo&}μy“oξΥq¬3­#μŽNTΒσΫCxίυΪυφΏύ‘E»6«χψEΏπ@XΫ?έΜ³s/ ΜσšσΟ<ΣNžΏ₯έχΠ·΄“go*τ―ΊίWχŠ›όμαφοόΏ?ήΞm³ΪUˆp$ŒγNizt™8Ψσ©ΰ> ˆρΟΐπϋΉπδ…·΅§οt9˜Ί2ψ²x\ ЁQ "} []vŒͺ9ΒΞΜ…Ν@€(3wε³ϊΟ‡ΑΐŽΟzζŽ'Ύ°‡•yξΐ·J»YwŸhσΫ·8kΐ{Cΰζ“»ΩQ˜έw²v$2…£¬JH#Ύ˜lcΌ<βΝ‚N^8h~‡ χ @οΒ‰‡Χω‘KTΑoέΪχΊz7ΏΚ¦Χ‘³Ύ#ΐΜ¬Ό- _"/³£`»;ύκ=i© œdb"χRUP―θ‹b.”‰–ŒL,)nB’9…ϊΑNBϊ'΅ˆ(=LžΖΰ0ί^§υ)RΕ Ÿ ‘κ·IήΜuη-›ννχmίώ–σν=o>έξΏγD5~Nίͺ‡«MAς΅]*¨"ƒΏεͺήrΗ‘ΤΫψΥξΕ©£ξ΄σkΉj ,"†#μZαHHοiΗ\J›η—ց!aŠΣǜΡ{χγπF}#=jo€η_γD€Αΰ^tSŸ°Δ+Μ½`š°u(o^Ξc¬<{ι°ύΚgχΫΟ}r§ύ6FClΛ!‘μAoŒxΖvΙn¨Pΰݘ€Λ7]Vψψ–θtΔQlπ$τ‘+Μ¦Nρzγ38Ί3ΐΈΑζΒ%χω  ²€‡Β„o`€°&—τΛ;ΛφΏώcgΪχΌρ°]½Ž΄>Zα«…ΫάάZΡ·Ϊ ΰ΅ΛWΪΥΛ/€htιΙ³7·ΧΎωνξίΨWέο{ΕMώνΏϋ+νΏώ•ΗΪI·ΡβŒ+Xΰ Φ°%WΨsύ_#ŠŸ§4ψώάΠίpθΝ€k“vσ]ίIάΗ)Ο)ΓZ<PTΫΗΡQ€ŒάΠc’U« 4L’¨ΡS!πΧwμ-D(ύΔ5όupΔh»u§΅c&ήΤ·―ΩΒπ1X`pςΆΐt}­ΦΞΫ†^Ÿ·ΕmΫmyΫV&`KέEkΡθΙ‰’UiŒXδ₯?Χψ—χΫϊ₯>:Ώ‘Χ—Β LΞεΓC‘€ _«'~Σ ’·Ψ‡nπXή•C}ρ¨b Ταͺ)J™h& A`C9‚WΓJπ υ ' AHνϋΒC|ώGŽˆωΠR)΄ˆ1Έω‡Šžš@Tλ$a„“―¦₯}’@vΝ Ιϋ nΏ°ΩzνιφξΟΆχΌρt{ύ'Ϊω³Σ6Ω/|VeO][.|!μ˜—ŒQ‰ ­`‡#ΈτΗ*l’Geγ’Ui•έΣυBγ 僻ηKoΖλΙΑΝΟΰΧrΣ‹\`GF/t9UΒΐ9ϊ<))Γ)D^.Xψάύό s$E?‡=HΌΛ”G] "ό Έv|v°ΦžΈΈhΏώΉ}Vϋ;νwΏtΠ.3.Ά6&Lώ'²Φ—Š 1cίΚΐαΩA0i·ΏξΑvοCίΒbκ\ΚΌκώη^q€οϋώIϋέ§΅M…[‹ΐPΜ*]‘Ξl!ΗΟV4ƒΠAsθμ˜ψWοΈύοΐΗΗ;Ύ›Ω=kLTP«ΐpŽλlη9©Kξg¬w%ζ9JΖΑA$Χ²MΣPFΙδ%dz£œΫxyο7ŠΒΛk‰;ΐΒ¨³·Q:{‹6yΞΟcxngΐ tαφQ-š0ΟΤreήΆί ]‹»OΆΕyV ^‰”²ψ@ͺx #7 ’ζ Ξ½lο)°ΛπϊΦ{]±{£)ο»b1ΝνPqχό…[‘ZJ’J’Λ ΒQTlπGΎ‘@τΌ$žΎ&ΰΤDήMrR‘§ μB_x9œ„eRF‚ω‘Ÿt} R4Οέ›ŸΙ#αͺ’EΌϊΈ”O0φoΗ畦εσζAβ7Ÿ΄ϋο:Ρήqο™φξןioyέ‰vη-[ντ Κ0‰'ZWXΘΡQ½Δ­¨x±KC €# G4–‹/GΞth„ΗΩf=ω_nJΊώ¨§—{±χ"'κτE–λβUK― g.ΤΔO˜ΣͺΜ ‘ϋ9WBχb’“V}h:e(§žΖaϋ*’ή€μ—gνWYιϊ£ΧΫ#O.ΪξΘύ–·ΟH’<·Ο­B<„ΕC―ά”‡OšΥe{€jόUήΒ €cgϋBΰŠrΚB –ΓOyε_α2ς^ζΦ’Ž\ΊΫUΟ B:sœ*kκ†PN8Έ«ήΠ|°ίΪΫ^wΨώ?2mΟ_νΖϋξΧ΄ž{v2ΠI–ˍ€~ϋ:m—ž6ζϋΰΚ€–ΊΛ”‹-ο±:}σmνuoύ–vαξ{{Ξ«ξjGvιz…Έ7ύ›―ν2ΪTά«ςΘ|μ/³]]_Θη γfώΡ.€°N8|Mπ|ep½ml_hg/Ό=ΫZ)ο©γg$UARCΔΊTΔΐZ:ΐ³\Œ>yρA01m=Χ MΣH-8œŒ›τRVcŒaυ¦ Ώ˜»ϊgLbNOσˆΰβ˜©αΝ@§m“ΗχΪΖ³΄εά΄Νξ=]oχ£ϊ νΊΖ ’)+ί Υ‰”ϋάkWΐΑ€θ:ό’ε…ϊΪΧ„ΙΑαξ<ŠA^Xwž,!§Sx‘Ν„D₯ΔΐOΆξ+ ¬ŠΚ:Cy€ώ|ΘΘ›₯7†YTδΩd’σ¨Α¨'ιπB}pι8‹")kΤΈujξέeΙγςšL?ξTFΏh—žA™]ΫQA—ΘσϊcΣ\1ϊΔ‰ΙΉήk*,γ yw \9ή~aΪήpΟΙφΠλN·oΊοT{ύ'Ϋν7mΆS§ ™o*μΥNAΧ³‚ ΄c―λΒ_Λ ΠUΔ– W-­Π‘[Aΰ­% r§βε­\β/N|)ΘΏΤQΐώ °?VX’η©S ?ΰz<0 *<ΆωΑΧΰ; ½χχς΅ΦΎτΜ~ϋνGχΫΗ>½}κρƒvι2γ €ϊ&…C”vφρβ»rλ±oΓΗΘbz¦Αγ{’ܐ‘Α‘€ŠΘiωψ—c¦ΓgWzWψΙιν(έΦ}ƒΒ[HΩ=ǘD²σͺšψΙοƒβƒχδ’%Δf’k•”‘₯Α Χv–νoόΰaϋCoέlϋσ΅άυ_w33 ό©ΣgςD@n „Ÿz2†fš’^$SOdΧρ*‘RJ|>k›'˜Ώώ‘vΧΎ©ς^u“ΉWάΰΦΏπj§Oϊ„γ0Γ;3x08¦η…; $ς–-2—iσbWώNϋΰΠΛξμ]n·άϋ}”ΕψE°»@Ξ@=&Ώ+Φ‹Ώ |ΆόΖ¨.J'οˆρ§ŒωSςQ(ΉH9•ΓΗσ¬GΕ΁Δx€†³υΑλ7’89β°~Ώτ·ρ…λyΡΟΑ½'λrΐ -Ϊ)ΕS “ 1τg—Γφ¨„Ό§`ΆhλLΌ7ΐ―.OLr£`>"$ϊ G£’³)/Šl‰Ž0''σ'M'$(. 7ΉG I^«πλγβ4Υ»m—/…ΎΠ―v§ύ?*$§Θ uΑΆRΦΆ₯ΟhΟ_Q7ψŽώ 2Μ0€Ωt§oΡν}Κ_ΜΰŸέ2ΒΚ³;βcΌ†.«GψJ9eΗβΉΌGXΚM‹ρ·HaNΊc`<Ήg\ΑC^γͺa©+€!CKŸψ)y‘— ΛcΊŽ-λ ήͺ·Β’νιύDΔRΉdŒaδύžƒ=ŸΝ1Ϊυ4_μώv^ΆΕςԚ¦W„“<0˜ΛωωΰΠbΈk^σ@»ϋΝοΙ=―Ίοέ+j°»?k·Υk·lΦΦΆB—-ώΑόΔLŒ§v5»&žI€J4[T~$θθI€εŒUσ|7ž8w_;yζ~ΙGzUκΩΊxΔc€&±˜ΆΟCV”*€Ν6ίΰ΅ϋ5ί@|†Ο … ;)¨ 9/ί±­ΰμc;κT–©Ζ=J‘HΘ έ|vΏmτω€‚ŠKƒ˜Υ=t9!˝€gE―όΜ·’§ržϊψ£ν ¬ž+eιχ†Χj¬Ge+Ξμφ@cmέW{u™XΘ#Γ–±•ςΒΛ›ij;S»Ηώ4b₯fZ]Υ[|δD]™˜P™ ”δ“μnDpztbl‡²bΝβΠ‰^˜γN™Μ!<ωʐmπ^Uο6?EŸέvΛf»χΆνΑלhoΈσd»οφνvη…I;‡Rήfb0XOΡγΔ »WZVcnΔ^BΜΏΜO•~ϊ4ιΠέ*t”tΜ™hƒ+ΆbΠ1wTώ(/!β£Mα₯ωψYΡG6πIφ} ;{ Γ>o_Bώο±ύφ»ν΅Ο>ΉΧΎrqΦφ\ΤθdqCΩu|Ε‰¬HΚͺHM+++3ψš +γڎ³ύBfG ’"χ€$›ŽHYΗ€γ•²&δ ŸΤQ~lek’KΎ»pΘ€©ΞŠS¨‚Œ|ιžφωζO'δ‘s'ρξzΉΝAΰ{9€ΟύΞ,`¬Ϊmι{­}η[ϋ[?8ɍ€Ως§‘ςΪϋp|ُουπ~©Ωήnsπ€Ο‘+uΗ·š΄ΪDS2&k™ΐ‹©Ϋ*„έpQuξΆ{Ϊkής&§-τͺϋι^Q€/΅Ϋž|n֞»²ΐΈΝ>όΚ€8νΧAŽtΫ—+™6;Dζ*©ά~r’!₯#iζγ― ?ιβŠ.°¨—κϊΣ3ᑍ€†μψN¨ŠReΦάΒπŽ„£|οϊήK^§ΟyίχAπΉ9ΨΙ΅ΕLλ ¬Ip…GšHΐWw[ϋ?ύθ²½ρΞ όrΠΒ+p»βώ„Ξƒt!'qφ3u€½‰[§κΉœτξΧΨλ›ο=ήL(.Βόάπι w΄{ήϊmmϋΤ™^κUχ?Δ½’&Ώτ{_iϊ?ʎ €K&W° ƒ ? œC™τζ³ά#{b%Hd{—kξzGW—˜xΰoσλ/·“·Ό£mΌ“t―ySΦ₯ΰρΛα ·+ ¨™‘Cͺ(c€ŸkΞ€ϊ{ιΙΝ8}χΎŠΒxΎ! αOœΐ9Xίe’‚!Ξψφ1ώ^ΘΝ„h8_τ3ρ©ΫJ›ηΎ—ΰΖeUqπΊ“y*ΐ/NΏΌΣ&(’Τ#₯”©wΐKW΄₯V+Τo}πΜΧ ―a€Ό‘qqΗV½`X/¬]:Θ; Φ|6Š•}VΚ°5―I’εNFn|₯NΕlο™ι ‚SVcEεN„ωQΎΐD!©A,» OΔ6;a|πϋΖ@[&;’¨Ηα猧(τ…εtz†ύΗ;J7䀏ړΰΤ₯“‹ΗO€Ι)όΦeΊG₯z^ΞίμߞH€?VX‚/c£_fef?UΫFNl―·[ΞMΫ-g·Ϊmη·Ϊ]·n΄[Οn·Ϋo^o·žΫlηOMΪY[Ϋ“ΆΉ?8άΖNύU=Ϊ‰ο*φ8«°Q‰©—/žΘ~ψ€#}π(iςμγYU~Π6οΐχ“Ή{Λύvωjk/\?hO=ΏlO>ОΐΈkΰ/^ΩmΟ\ZΆλ¬Z}=΄υΛW™Š¬’œz52dgδD†V†ZΪ”΅NdΡ ¬U?»_–;qžξI>aӌ“ŸK{I“o©WδDμζ%όΝη|OWPεdΈcΦWvgό…rωPPv ρ5ώ.θKρ‰?»x.„H.ŽΘH“ŸΈˆπνΣήNfΟ>lεŸƒ¨Ύ΅uZLy΄Ή΅‰Κ›cόw% q†2ςφeΞ6μ‘`εYgς g& Φo*Ιh t OΗέ8sαΞφš·~;:χՁ!ξ5ψ§ΏύXϋ“ω―·›OφΗ`φ½ŽΟΰΖΓ UaγΘέιJ;ιDkG@εi w&ύ>€~ΰπΰ:ώ5±βΊΫΟοχUi˜έ+*o8qe|” γ;(tY)’‘96)'‚ΐ1 Z–2ή ΰγ{y̏tΫ2ΉŽEeξθΙjΐΙMΙ γ›‰αš0€0Δ‡·2 ΈŒΈ:Λ;ώ½ϋŸfκ/> ΆF]"©{έG ½ΖOxy“/šD) αΝENNt~§ —0ΰ£ίNς5Ζ΄7YŸebΞ4:’Π°Δ~|ΘO!Χ GώΠΒs˜‘"ΙγUyΈš‚–LΊX‹pzmnhτΔEE–¬”G*7vάιΡHͺͺd«?δΛΛQCh‡ω"¨k£U§ψ _^β d’Θ°Qƒ&Ut…C‘!"]•ΣΟ*Εc8-Ÿ0Ή‘Γ4Ϋ[₯pΘ‰m B=\α’#Κ1ΙπS:rΙ|9\r7α±ί:ΨήZogιΫσ'ΦΪ©ι‰vϊτz;wj­cyεζδZ;Να₯o΅ΩXΫH8iπŽ?z3Άί‹Žˆb){'Ίφ…ί\ΨGv0TΛ+χΦv0tWwΫŽkϋνς΅CŽe»²‹ΡΗΐ]e’zm―Ύζθ‹vΔgІο|Π·nΉU+ήΑ Ϋn[ ¦œPΚΧ> yΦ(§ΐΘ―r".+W…#Ξ:όΫΆžfRήλΛ€UzΒoxBάOh/α•c!y£ΣtV6’(N/₯ΡfΗqξ­wήˆοδ|Β ί1¦H2Ι έ2άΥΏ:Mh£žm o[tG² ^i±νΧφΦΪΏώήeϋαo]oΧά „·ψu‹ω}ΗͺίF–@OxN<―C—Ÿ” ›κ”ϊβL’°#.xr‹| Ÿ9(·Α,ԝ‘έm8sΛέힷ}ϋ«ŸώWt―¨ ΐύκ£ν/ώ?~«έtmE φ°²@Aμ¬Pωyγ_kixH‹BPπ\‘FcR-•]_4ΫΝe€μάϊΞΆ΅qΤί2k(DμπΘ5;鱓ψΠδͺc™ASB †BιFΦ<ίκ%=ΆEδ |Α€τ ρψ`ΰfΎΰXΉΖΞwH‡ ΖށΌƒ~έλμ¦Σ~· η―=Ω–~W@ϊ:72’#D™t?ωv·wΫ›V…€x{™ަRsrΚNF΅κΠ·ρUs9Λ ίtFFΖbpr=οqAΕiυΞYΫΆQG.‘‰‹‚βZν H?9‹Μ¬;ΡαΌeόέΐσ“‹τGdΙ a†ίξ8<Εt—IΧ‘;a€;NΊ&W˜œ3.aΨΊχ‰ŠŽ\χ³½†…ηwUgυ v1U‰ΡŸQό―2φ³UηL…|Ϊ4ΫGχρK+ƒ—S|ψD~p"Œ£Έ“ΣόΜγž@α)g’ς‘Ι0|pΌš‡δ&_η$`#RαίΩΫοmχΌυ[©G©{Υ}=G_§«^ξΏόΉίkύΗ>ΥΞbΘ2$Β\ @ug •r†bsπδ9tΞ΄’7ͺ53Ϋ:τŸ πώ€ύ+IΏιοJΊΚ\F§U)ͺ2F@C‘‚#¬^]·RS²ηζ>³;Œτηύ φlΓƒ,/οόβb£‡Jαη·`˜½©OcKv;νrΖ­!^»ΈίΦΜΫα-m‘ΑΗ°{Η°Ÿϊυ#CVήΥBθM?+…gχΫΦ;ΩπΣΓ‡ω¬§*8ΜDžAŸχ4x@ΪF—˜°‚σ>wόΠQ ς(JYΉŒ~ΰπή“§\1yPΕψ¬j₯“S&pΰςͺF)ae©J[Ύ.½Δ)Μ©|Ϋ¦‚Τε†K³€Mgΰ&l±ΐ ΓΚ‹:j—’@Σ―xQy„…Š—Κ Α€Mί‘$‡“hψQ‚Έžψ €§βTuύ¦!K@pι Λyκ°ΓΔΣp½\M‚)giyDΨ,ι“Χ$‘n†…«^έ £ΐσα«Κ\ς½Νb™Ο±ΎjkΫdzχΉγh™_2R0‹XNp–("λ€ »β«;… .FB”‘ΥΗΈ±$"8/ Θ“ΔS&XC?΅ΑίΒ-ZΣ‚ΫΈ΄τhΖG•'a©Ž|xΉλ„mΓUε¬JœZπnέπ˜θ―žΦΡF?–/†2Άό\xvί¨$Š\»ΗΡΞθ7‚«GZ;ΰ‘Οf€nhάέ;lο{ΫZϋw?pΨμφ»όg>=½‚yNΌ²ΣHθ₯”1εΟpΙ‘™†SΰψΩEΓ…„:ΕW7gxDH(οΣ”σ]SwWέd_Έχ­νφ^}»ΰΧs5R^!.ΧwŠGΞ* ΅B“Y³‚ŸΌ—C(ΣδV”€π ¨pΞ0#θ;/Š_cVΌΎy*7ξν=›|«΅h ‡ ―ΛnΙ»Ξ@"•˜­e š΄Μ^!΅FπσW0Ή Δ`u ¦h\΅ΟΆ–³ γ._…(/ŠΗχ‚/X=ψIΰε…-4xέ>=!χλ0ώ(ΐ£’Y„ςΞ€6Μ³ΜB˜4xc_ν,”’²ͺl[sΈcaΩ(y”QΆ:)³πλyΠ°vI KϊΣdXaέρ`’―z™b›ΊNΧαvgήAΰξJoΆ;os9tβczί9¨IT?qE‰‡qz‰d‹|%Ό‘φ9ΔR΄& ι“j‹²ώpR`ϋψηιΰI ―R_ο°(hSπUΕ8›taΛL?¬#πψΐWz• d`8Q^tρ9Σ’£Cό9Θ‘Xΐ…L;*ž4 ΔWv”«γ4€¦€q²05Ÿψυΐ™RρΛ …T™/?†5ϊPd9xεε‰N^PΛ-ˆΓz’G|—°»φ‡V=4qUXΖ0 $πξFˆ8Ύπ’€PΫ¬qev u?$p˜oΐΤή₯5Vœt°bχ¦Q‘+-Υήρ>w|BΗwuΨONδ›84’‘I",‘‘]Θ  ΔCg`+Ώτε”Kχ&‹Γ•‘Κ‘sηK#ξxΟ.&ιV#21–'ʟsΒΉ·$ό PpΘJ&¨oΉΔŸxΟ²νΝΧΪζΦv;π³Ύ~Νb9δˆτΪΗβ‘§%9ΐYT*©6ΒοLκL¬~[Ζt?; όt>’=gvΏ`ΡεkΏtΔ!zψΉ/~Ίύή/όwνΚ3ξUχb§HΎb\ΔZ"0Ζ’κ^o “ŠiX)l6XΣ2}ΧκanβQ κΊοΏvpb”&›m²}ν^|˜0qͺΙ@JύŠ‘—(x‰ρΠu―^%LfŒ>ηPk‘2zμ| σs‡†ΫU±Ϋ~0―χ&§ŒZΒKW5 xΏΘ |ŠΑ•y ν΄Κ ΗΰQΪδ“| ΩmΛ«u'ZWΌΏB.χ*l“3Θέn―‹Ž ϋ½τ•i§ϋ$ΐΥ «ϊΫoƒΦzcTžvΰH{Ό[Ϊ'ΞRž‰„ŠΝ/ Ί« πΐbθ³ϊa‚"μΊ/'r’βDbπf“>₯4 ₯¨”@νD!֜’Qn*ν‘”»ŸšπbΤ€ƒΫΎr₯·)ΥoaΉ‡q9•Γ0Βd• gςμ ˜qΜISωKŠΉi‡y9ΔΥ~yωU§ΡάψUΌ  )m‡uv1Ίτ«u•B&κΔnΚΘ›‡<cf6Nτ©ώhϋ₯ΐr1θ•eε XŸ"Δ|Œ‰‚r‹αε ΚΎϊΖψ‚ίΓyžeΊ΄KΩ1‚Ÿ1,MV*LϊGO©„zΔ›zιουυ¬σ‰k WΚαjς֍#ιγΎ€θs)+aΣΐ₯œXFAΊ;t6 “e+”ΠVΌ@eLϊ”{nλcτ•cΗ"ϊ*;Vœί  Mδ¦ΒκŽΤO^žΎιω^Ζ . „₯pv(ν/3ΙρΈΫ;ο=loaQ°·wΠv\Vδ[€K68<₯ɜb°σsΡ$OLχώ)‡Ύ«ψ ‡Ϋωυϊ`|'κ«|Γζ'‹-xT} q6σUΔ{ω‘_~]2ˆ—Μrδ/·Gγ§ΪloG†ΏκΊ£{^9D΄~Κv ­rŠ΄j”Vξ,9%J’S3φϋ*HGΉ«ώΜz5φ–Ι.ΐ„r―νο~…ΑξˆΚRŒ *Β+Ϊ,€«’#L΄‡qk~μ:ΧΌ9ό΄1H’―’Z`θζΉHJ&ΤaΉΤ <°Λ£~"Ψgώ%=T€jλλ ΕΓ’ψ~GΓΈ8SΟO]Εο`ΘU¦”©/υ b3y‚sž&PžXΟύ Ύ¦ΨUψϊ₯Y›0 ΏΌρž'(Φgέ%ίΔαDε4νσ&C'nΚĈcθΧΌφ©οεό\@ΖθΧ]…/ΖΛ>ζPš6*£¬±‘―όνCλMε‡ΣMφ ρ*n›ΑMΔc)»J4i—%;ĈZu>4™–>7©―‚‰Ιγ2Φzά•-ΪCcε›p‡[ύμ£γΏΠυŸτΠHcr€/O¬π•Ώrβ—@‹βτ2)(‰ΔΫΣ½ΐ‰œ8hZΩφ Ζ— `cΜυ9δ§2Z;.β²c’žXΗpŽQkΙ΄ ‡oΕΜd9ŒO~mg _Ύh˜½™5θ@žΛA"οeχAΖGβI(oπΦ>‹μΫλχ-ιI€Α5A·ΌaΛ •XςbΔΧΩύκpς-: ωζ*_\^Ξ$ΑΟ%AΘCΌ–η°¨­ n~u£feξRφΟΎwž~ϊΉΪς·‡…ΣNNa―m'ΎZ½‡eܝΰΤω)>0έΐϋ}ͺΗ‰ΑΖ„ΕΘHο“Λ89ŸΘDCόαεΊ}&Ύ‰π`€€iΫΏ~΅}ξ—?žϊάoCδ«Ngw½b\Άδ=B±Ξ5e Ž/i²4‘ϋξhPα•πž0 'EU!h>a BΑΨL7Ϋdλ¦ΆsρΣL0ŒήsΣ£߁ε€I}X²μ«ΥU=j‘”£|Αz€ζrΐz1;Ϋ{ΩΦ#ΩΟo+Ζ±ϊ°œI΅’"LJVbβ/Q_Όv}C>ήυ 3±P9‚‰rN4’όC&uΈ ΰkFYΕoϊŠF¦ oy)Paθ“–€kΒ>9pώ1‰πΊώαΣϋΤmC€± š;΅εuΪ"201" [u¦Y f¨|κε(Γ$\†dlϊJώβ›Θ‰$K'W£=˜<]έY%ϋ…³Œ6Ψδ‰2£/±ΘΝ0θŠΣ8b”β…ΨAš‡r§ˆa›ΙS .Š—”πΖΧηΪόj4R~LΈ‚†L»*³ΒφŠ^yίψΫE©Γ|¬{ΥΧλp˜¦/‰CCέOγA:~ψΏθΡλύ§LLΘsΌΛ£ŒWκ˜C™[ύ’L††Θ1ωΤ•ώ#- dΙΛ™{t6+ΠC_δ3ΫξΊyH³πΆE~P}δ6| ύ•ηχ‘k'΅cy'Μ“= sήΆI9&οΩώ—ώτ/εΥwΙN{ρˆ΄ΤΓA\&”<Ϊλ₯½ΘΙ··φΦ[―·ςΓύιSΰ„ΙΞO€ΉγχΠθΓ»ώυz–ΏΒ¦ΧΈcΨWy9\ύΧ$a:έH^vΐXΛ[Xq»0nάΨ ‰½Ϋβ› ―οξΆ=&Ž“ηLϋά/ύdΫΉό,”Ύ²]ψŠqΚ»²ACŽ"¨HId ½Tΐly»½lAΞj…Γ‘bΑgqςISψ™Τ.FΜΔυ ŒiΒ̞w#κ>21•ΊeκD'¨€ w“‰ž―‹’ΘOl8ιε@$?q·χΌιGd„ΕŸηνU όΜκ5ˆ T¦;$,«‚ œΙ>ΒηV~.+\>ΘWΒ¬ «'ΐ4DQΞ„p‘iπ#—ΐ=yz/―V Υ›Κ MΓciΕ@ΔοΝ‹³›Ή‘0όΎ‚B“nY₯m_”U)z5«8Λ‘Xέιp›Ώ’/ΞΊ ‡–mymΦ}|\β²bτ€λQiηΉkŽ(ίΘawΟπR‚…΅Ή4"Ÿ>•―/1=:Φ°°΄Σ_d+€΄£/΄=U¦3% Χ>H]‚&L@ϊ3<`‚½ΰ*‘α¬;ˆΘυO»ωgbηΉ mŽ14KΟzBœHυ-‹‡—I«£r{64‰_ΌR˜·Bƒ2FJ&Žv`Υp''M8ΣB‘$Vφέ¦φiΨ^ΫLš·£Ωtλ –‘ΎΘ§2DeWβόI€όΘ5|pδ;ΦE–όˆ$,΅βuR ΜHo&ΦΚ“0™Π’δ.—ς(ny•ςΰZε“θΈΘγ³ήχΒ‘Ηp›gΒϊρEbΩ•pBgχ1φ™(Q―γsΒ8]{nΏM^˜΅ΙΥy^Τε €Ή6bεΠ.ρ‘#4 žhΩdζρΥκ볡φ§Ώ}ΦφsΏ΄ε₯ 8γ}1iSOwΖ£ξϋpεΛ»0ΚκŠnάΛpwγOϋ’œΖ=ΓϊtlωΒΦ$`•η$!~•uΗ‘πΤD#ΰ+~@Ή+ΰγ₯ϋνKΏω³νΙ‡Ν½bέδ?ΐυπ7ΌϋόSWΪ?ϊΔWX€FZ#τ‚ β £XΓ…+Nε­•Θs0(Œ+τ$·ήƒΟ]ΚEH!•ΑΑ•ΗΫφ…jϋm lςk…ξ€1Εz’³R:‘±2IΒZHμιu ‘”­dΣ;Λ γΐ4“UAV{„«hpι―v 0~Ύ6t‚brŜΙΠ†Κ ”‘ΐΕΚDβ@εeC£"ruιJd.οF^Ϋ— εUxπΚv©<ΕCι5Ξ“ άΚ\BΓ³5ΊΖΌΓ…ο†ι+£’o;Uΰ–Knoš‚ iXͺ4½J{bΠ- žΊ΄ ZϋΑ4Œ4&™S ―‡U« ˆΣ<Ϋ’’΄<ΈT6ƒΆτα“‚€Σ3ρό₯xΑB—„U}FkB„QΠ†n*kυ©ρΚ/<ͺ䴇 ύˆ~ηƒΚ:ΰ ¨β€Ι€§v‘ρ½τ¨‘TΈB“aŽΎΗΐe=Iκ~B½}žV“§~DCk₯φ›MR―›ά€‹―»ΐp€\—₯Q(yΠy™ΖΟAF SΓ…Ώ`8€Ο€‘ϊς Η ΡΠh™ΰ&ξB֝ˆ¬»2Η8ϋ6Μν AU#'(ν$Vά½’š »ksdΈOτ…ŸψTΛ.x1VΧNπ”rd²o£ΤGzΑg22›zΙo©£KdMΈMηg|ΎΦξΌyΩώΚϋζm—°„ DΡ tΌcSώhΤεqλΗ@s`Wqς–Κ:β'ΞaYy˜tͺaύδwšZ>ςͺ\{Ή‚Iς8fmδώυKν—?ΧNž»΅mlŸ¬B― χŠš<φάnϋρ=ΦΆϋJ\)ΞέΐF,0jήe!a@!T¦Η)<ŽιĈσWδvO©6Καv=ΚηΑΛ½ΆqζfεFΚRTx‰'œ\¬Έ‘™m«V†_P³T$¦C!ͺ.xKξD'Γa›—‚μB—Ϋ…ΰk$¨Λ;ί”Βˆ2ΝRΑ\ΖhλŠ@ύτi@θδ_ΌKb ζΞ y{`ήΰJΗνMVααlaξ€ΤΙ Me•›ŽΔβFaŠΝ—y/χ&¬mOλšg.5φϊCTη‹ Fm~β*ήm `Θά Ytεδδ—bΊR΅Όν‰c‚ ήrX}oΰ+?β‘f•J7 !’£X•!=Ζ£χ₯œHκΣοay6“7Αƒ“ζƒj%]όβ"IΕ¦’eK>΄…VRU’Ά·κ+ζΈ‚ο;PΏΖί:Ci–ް«;Λεθ8Jy?’&o(–²Ζύκ^^ΰl@εάp_0Τ ήBP…τΠυΈ<{¬>™«IώκG ΣΎžώˆ§GSή ’rR[ύ Κπ"ΟΊμσŒ œΖ[ρOYy™$8!΅ŽƒτMΗ!ΎΒΕα%4οgq,&dψΈ²c\ϊάβgΜL·μ@‘ήC”οL-€ζzx&pφƒυp ̏m‰}΅m;(LˆP CDΣMc’½δ`r€'_PW΅$`2ƒU )>€QΒC§Τ!h—o›7έΣ&THεϋ7 e v™6%0#ίΚ£άFρ8‰$˜3‡mp°Hˆ„Λ€²Δ½όQŸΚ%ίΙ+ί–ƒwΰΣΦ±M©ςΚ‹˜$Δ€;a°,εό‚Xψε\IτώŒΔAΧg@]D!φGS―Ζ#<ΉΞδάn}NΝΰ«%ΖH«’U¨LXάΞτ)ŸHο₯mΤf_:ΛCvR‰'l GΩζd‚ώS“e%&mζSΑ”·5Ε`λΖ4ŽšP€–βZ€τ½ŠTΘ^w2a•šeΔ’“(•“#O$τLϋ0[#mT_¨«/:lώβν2–dΞ‚Xΐ[’°QΆUF’LL9iΔ­–a@θsohΥx‡ΔεSΰϋΚ_Xq*ΗΡNpχiιΝΘ“ŒΣΣJ+‚Ϋ-_—²ΎΉΣΫ„oΦ\xί‹νa’@¨hρFZot• ³MO½‹Ω…\«7eϊο}+›‡ ’ˆ«ϋo_ΆΏτήy½φΧe«φ¨ώ†lΡO5ς1ώ¦Λ}yWό³Μ0ώ1π’κx ρ£WeU  žς.S+ί“^δΐŽE_‰½X’Χψy9αϊεgΫ•§ΏΤΞέρ:τϋ p―˜ ΐηퟢΗύ\ϋ§OΝ[l•’˜g`‰w1‰Λ/wΙ“_[γΙξeρdϊ%-”ς VΚWrGœσΑυgΪΦ…ϋΫ:³Ν8ƒ2pˆ ½dΔ0 ΅#ίϊƒ‹ϊ­·ΘŒΙ@B=ΑWy©|T*,Ϊ—wυ3h¬Τ­Ηo[H\ΉΑΘ|_Ω‹βρε!ΉΎθ ƒPΉkΕ”f–&ιŠΙ‚ߏ -<|Βΐ›ύΌΡηΆ₯“„©Χ-½άΰ€Δ ‰ε4ψφ‘τΈSAc–§7κY}Eς0"Ά3s0ϋŒ|Ύ6Ψ'9y}1tz?€FZ%1΅o%^ΔΰA³LŸΓ'Πξ8•7λw\€ϋ±δ€Ά!ηδSwdΣΊ‘Υ]‰΅ŸlDο$΅$ŽΙΖ₯τγ§Ο”3Žά Jόʞ_ό;hgN0Dƒ£† ΘδΨXΨ’qWή\ρΞ꿎¬φ{^υ‡όΐχ§Ÿ4cΠ"£’¦w”ΉιυJ―iqέ“/HŸU˜ώ\ς+Fۊ'ΆΫ€ωύ€σϋLΫ>sΎm:›΄od‡lD:Ύaέ|―}βηœžέoΧfΣφαŸ:έΞm!PN†WVЇF|H‹'$$B˜π™4Έ 1A δϊ΄oΤ8ξω&ΐ”Ξ,[₯σ{Kκ_\oσ«O΅wΎ­moίΪwY;()‘κTΎ %<² yŒƒΩΒθ鑘“ ΙΑœžά€Fqhˆ2KX‡n‰›¬2ς†Ήάδθ`£P^Ϊσά†Ÿ¨JkΣ²„·}€ΑJώδiθwβ€αΞ`Ε`η±"Ήsuώ₯o#σωzΓ*"4†…Ν'w³ͺ™ίu’-θ Χ»σΧ5όόΙ%οΠ₯=ή½"Λ QΎΰΔΎAζE>(6?JδΞ€“y­bHYΨύI»Κ ΛΛκH; Œ<²-ƒŸ·Κ3σ¬ ΎdΒΑα6―Ο_Λύτ V1ͺψ@οv“¦uβQΚΡ μ7Κi ΥΖ⁔α$7n$†PS=ϋ·X:Ÿ04ΔΰP·7ͺΛΛ@xŠΛ…Μ)›­cӍφ“Ε”₯ t –!b©]α™:4Vήτ‘Ρ6ΚF“r?EC“\‘¨:ΘՈ˜•ΛnαpZ@Β€.Ν• ΓΏΰI%όkUOP0\ωœΒyƒ ΗZ²ΑVG^d^D]J+e"sπΕGόΖ$ ‡ιΔ­«όΒcG“λ•NŽ˜"\ΥeΘΆ„έw²š¦ΔW πλm6Ο']’ώw?yΓοe […ž'ι]Cβοxΰ‘vΧίMβ7ϋ†ž\yφΙφπ/ύγ6ΩΨΜLT%ώύ9ΥNυ»s‘½Zύΰ²Ϊ@˜5'Όε–{Ψf θŒ{π2lΝo8Σ6_!d’―ξiύ’‰eϋ _Μ}WΫbοωvφΎχbΨΐ‘Ρ†#Χγ²”ν4υϊJ2q€­-δJ@Ά 1*Π…EΨE4ιٍw” ΖΩ @ξTW‘iYao\ΘΧ`yW²ΖΚ­{pN/1‰a₯<Ώc;/ jWgmϊΔ.Δ©±‡GΰreογwK&Ys”™ΚiγK;ααβφmvΟv›―mA(mX›1ιΐΈ_ž·Ν/^ζ°Νξf_ύ±muχαΠGm0er—Ώχ+Θ RδžmΚ;θασ:Ώσ_π8ΐsΐgώ-cΫ…Ε—/>% |.o’G΄0ϊE+α§ΈΗP‰±Ζει '‡π*ΟU“­ς˜Επλ―;« rR!ΉŒhΘ·l„ƒg’uΔ7=Ε=W΄ΧΏrΦ%h/“ ‘ § Ω*εŸβU{ωέ³¬‘ž 02ΚN5£ψ8ώƒ―,Ÿ-C’@N˜$"ΰŽΒΐψΊ++SD&¬mΙε₯§―DB4B¬ίq Θ?G¨»πFΑKι61»Н-φ―hB·ι€šBgq]OλΥ“\ SΎκΦΧh†›1M|yΖwWP―«tθ\jό.‘ήޞΠc*oΰ^GˆGΈ…―ι/Ρrg,u"yW…u+‹β6OވœΉ9Π €40v€-FŸΌκ|£O(†τYxYΩα{τ "9νWύ_ά¬ήb2ηw$,h9 )^ΐJWV1όLj5θ1ϊΚ…h7lΏ§Lf€θδ‘υΤ©Ri#ηΘ”xιŸψΦm@ϊΰΎγιEq}ΪγO‚l¦ΰwE.ΩZΧͺ\₯n]ᘡs·άΩήψμ©ίxŽ>·είxξ©G?ΥΎπρ_n›Ϋ'μηιιΓφώίb2€‘R’U”Ξkg^ί][ΞΫr›Aέ•J$ίY6>ΟΑΐ²<Ϊ¦±ƁM<ΒKοΤu68ΕX”@ϊ½_UΉϋB[ŸnΆΣ·=Τ–ΧχΜ–ΔΘ{|K@Ÿ7υ(˜– ³nfβO†….yv'π1Š*!‡p*‹ŠΛ4 ‘Š΄}Κyß«ύεi”y‰οCΏν‹AηνΫmqΟΙΐo~ζjΆΥ—·m’ΔΰΧρΑ½Ž§‘\ΈΥΟ€ΑG“䗏Ξ8Υf'PtΞ>²οVόA›>z½MŸ;h Vψ³»ODiTCϊ³ϋmύb=Žh[ΖΦj”‘aΣάΖ§L”%ΌΟ*Lμ(E―νJ;gR€΅?EΜΙ>―—@²B.Έ» N΅XŠr’OςD<©ΟE“οΡ[ΏYR7 ‚WΕ”‚^—υρAΌl΅•ŽB2,)0ΒϊG™ε eEˆ0ο8°?‹-•"x|ασ]ωΈυδΡ6ΚŽ!ΰ8= {„m”ύή$˜IŽm”(NΪ',|βXδȊ+γ%<&I9 juήyςM&vςͺΛ²ς^†Ρ°ψ€œnΓ³’v5žΥ$Ζδ[Fτ†©$°I/ΙΟύΫ―lάtω)oΜ#,Κν}ΖΚxύ΅²c>™Ή4LJ[ΜL^ϊšθϋΤ#νrΜy―Λ’ @&«Π=E_ψMƒμ1!5._½?FΑQžτ…-δo£!Œ[/yΎ SΩθd$-α”“ιςLXXNu¬΅ƒΦώό\΄?ϊΝσv}8@ReΚ7ώγ.ώρψ]mϋkθε“€Φ«έ€ΠbzœγΗ©&©v4_*·4ΪΠ‘Ά½,ƒΏjόRg–ΏΘΔ5Ζ>>eΔG8eƒ§trk" ‘o››'ΪΫήχα–7Ύ~ƒΉoΘ{Ύψ‰_iO<ό[mσδɈW]?:l~πŸ|qΚ‚ 5Ν^N! ’žkΐ mreVƒžwGQ 1ž‚‰Ο4%#€i[ FΎ1©„Ε»Ρ;ΫΪΦ©ΆΎibπ€@@KEαšP&‹“’P-K8Š )|ό zΒ&JΫS‡ΝΚ’xά’χ:ζς¦­\cΜ$H#‹!QΆΛMw΅αυI Ÿ— ζ·’ΌΞ“NΉ…— Trό³=I]ΉωΠλ€”ϋΪ_pe¦ϊ<ο£L^ZA‘Ί5ŸλφU§x”l&$Ίνυ6σ’ƒΧS™XΈ›ϋ πs=Φ²”ΙjήI 6“mKϊM%/¬δ^‡nHγdΊύ>Υ!/e`VΪr/iNΘ―­ό‚‰xΩ7†QΨάπB9²4aϋ΄Nα2ηιx½9¨bΤΤ+¬Έ‘7«=σm―€lŒ ΑŸ&Σ5rΠ#ΉμαΔAά;&’q ΫnxFΛe² OΝg& ’₯ΉΑLγΌ8σ8˜π²8c €nj M½6ς‹ϊH N{ˌ°φŸψΔ«Q–ΐ§MςV+#ʟ8ΤΩΑezΨϊμ”–ŽλlλλμΰΞ₯,|Ηΰ˜(›ηd7ϊ€ @ݏŒt§­U—˜³J'εœ$d΅.₯z½δžΕ)dΩ6Z/΄€Ώ(7ι“E'yʌ|N7rδRλύ΄)˜­†I‡Υ§¬pœ’+DβΘ'U„οΌ©΅Ώφύ³Ά/ί-ΐ_b7*ίςλ:Ώ“ϊw¬ώϋ!/A’†*»ΊVŸ~ ,ˆΨ%ήΈ|λ}δΟΌ‚«ΆΓ|*εξdœ¬ΣHeΰ› o2α4LΙ4I β“ΙΎ„ȏΊ=ύΕO·›οΊ―M7ά½όΖqίp€G~γηΪ³}Άmdε―€šj'kOΦΪ――™ν>ΛJηΪ©¦E™β«σ¬-.Š( †bH8Π<ΛsΫy&],f¨x£p4>(lc(ύ$­―θŒ 30"mψω`Πτtžjώχ΄φP ©D[Β_qc0$Ϊi±]έWRӞA§w―Ε'ZXχF)g›ΨΓΆZŒΔΠ*ŒΜQΩ©ψŒ³ΟuJ ++z/ ή²Υ–·°Ϊ†ΦLH4πL|΄nκSΰπnι“›%«™lωϋEΑ;·Ϋα]¬Φo₯ΌΟJκj $σΰΆΤΓδΜ-w6- wε>?7m󻁁žΠ|ι οξπK堘Κ_ΪέZ΅·YζnioΈ²^»rΖ6ΆŸ<^=ͺΨ'B ΌyMΩgιLΆ >¬Ύ«ΆμZhΞn΄u'NLš&€ ?¦|ΩœΒ­{Χ7}΄ΆΥ> ž₯7j4ΐi³–±³fλ‹#1!ƒγ’Žι`By7:-έδωId―wF1Γwε7΄‡ΔΕa½”ΙΨm½.α₯ΕΊ:q~}­nz΅†*«,:Išc Ό%FΩs ?;->|[e}ς1υBCξM1.?ΰ½υfǁ#[ε1ΖIŸ°•\b–ύΑ™8'onυ•άξ(Ι{&b _MΫ₯ΓG^ ΔHK£}iŸ…χό¨oέO‡»C¦ΑF€ΉϊF^Hg‚¦Kxbμi‹wυΛ EΖπ„6ηκ¬m<»ΧΦ•}ίζη“+LάAKY/ΐqWίŠ\Bu€nϋAΎ™FΚda i7Έ$ΞΎbΠgη₯‹Shj•?Ω“«mz Ζ»Κ²mVoŸΩvΝνL0Xαi”Ό907YΉBnœ0‰%«me,ŠA/=ι2a•#w˜pδ  ΤTY ‘FΣ"Ν΅Σ›υΞƒ¬lα%ΌΚJ™ςKŽ|ΛΐΎI‚Σόήoς&tΣDEΊ¬όxό²ςvΧΰΔιΔ²[›ΦΔΧιΛ8°ΡςNšΰsn@γπ«‹΅σΡγλπ]ίΌ;@ΚfΎ Ώ¬< 贈ŽCx½π2χ—ˆίUΏ2f?Bvš—>ζP¦`£ryθe!Ϋb˜#c—2‘A_p&jŽ©L`wdCΓΰΏBCmσ9/]υ2ΒΨΎLzψ+τ]ξ#!ίΆ{/Qε@%‘ϋΗΆ:!’‰β (ήΓ’χiX:Αρ#NJ•†:ΰςl« κc4‰ }ΐύεXΔ·{<Βcι ώKνω'Ώ˜•ΏBb'Φ©whο8}υΙ½ηΧΪv TΑ90μτ’7Rά€•e'…•Ατκ~›^9hϋΨOσ•˜>%€1:‰ΑΊύ4†λЁUΑ3-*y·Οmep:Hs‚¬T'ΣvΠX3₯λ?ωƒΆƒ±΅j•Ah3\Α Ϋα8q·!Κ©Snvœmυ'œŠqāIœΊ&ŒμΉƒ_…@3Pΰ‘k—fmC£Ν¬fκ„λα rΐf|Z-gΈξζ%M΄6ΞvΨaΰΙΨτQ@Œ‚νPiSSΚeλΦ›16Yi]Ai=?‹‚Τ0 ŸνA£όΔx-Pμ ·nUΊ΄mΚΔΑKHBΔIΐLή|Y‘ έ›¬ά9 Mήτ¨‘ZδΩυ‘gΤ-¬,“&C£žΙΠ|Œ™[ΛυjYρs)eΘ†ΖΕΎ`ML˜Zh}©\RŸ χ‡ˆSRΒgΓβ²+Ισ7©Χ›)έφΖkκς^Π1ΙK0€Ά)FΣ̏uY™r`)ώΒi,ƒ[Ία³q/-dG€>Κ«˜θΐϋuϊΐΙΨ:†vέΎπ½¦Ϋ' ΜD•>ΝrΤΊthΏγΛ#Θ›'Q1ό!'A-mΉNy—ώ‘“‰\GgΥMWΨ^›"ρvYVήpyωΙ‰_&4O“³ς>x‰@#›wυ›Ι!_jœα§ύ‘/³©7»ƒNŠ‘!'«ΉΙθΐ8‘w—ΚΒΩ…‚™`ϊ£c•lQφr›έr*ξΉΥο1’ψIΝ @ώΉ‘œΉcήών}zωπŸ}ο²½ζf·Ί»A*.@d―žλM*‚“SŠ ƒ?mqμwkVς,Ÿ’ΈU—|ι΄~~Φ8'όLu.?Β‘‰Γ6¬ŒwEχ1£.Œ?ϋ±J‹ BiΗz>*΄±QŸ%N9~Ϊ›O~τ'Ϊή΅Λ@Ύ|έΛϊ&ΐ'ωDϋΚg?ή¦¬Ζ«ίθšΞNρ#°twεώO>·lΧηh‹^FW8W7Ϊq8°smPιr ©4δMό(Np3°™ ,NlΆ³B·*©-΄EAo¨‘`²+’σ$ω$;θφηλνώΝSόΓvαΤa»νΜ²ύΜg¦ν$¨Š.ρ8ήSŠC‘―Ph&¬¨*9‰―’9γ(cϋ₯Η4“ρk«’v°'ΒΥξδxΪv£˜άΞ„ ^₯‘Ky(CiΩf·vs}ύΊ|‚/Χ}–Ω:Ε!ε‘„ͺ ²j‹8Ήƒμ †)—'aΤ‡±ΛŠΧrύ]*ΩlŠΓΩΊvC#αTΠγ 'ΫηLb4nι†M0ΰ¨`5Ύ1πΥDIΔ΄£ζ€)o%΄<4iŒ²b„qΎ“ΐz½A+ίV@ΑϋN€Ό>XιΝnΚhΓ κq5l9²:S0ΰ]v_č©Σt₯ j@Εc\ώΫiΩ!,M¦§QςΟ|ŽΘŠ>‡τ*Ϋ1μΆeζΔΓ])J­κeŽ8ρΩ¦uάex81 €#“-ιπo›€‘H 1υ(ηϊΩr§ι™,φS­™,ήΎ§<΅mό­"m†θͺ.λΧxiόμgύTγΔΘ‰+4½BρŒ ωc_οα o¬™hΜ£Χ0μήxκM¬Τλk©τ’SvΓγQ»άΰκ˜Gς)“ϋ€·_Όά2eRαε„τ©|’y§υgL]`’«<€―.O€ qϋ‡ ;gΆ]£³šhΩΆΘuQ2δ^~€, Šˆi„H,]‘ω­4Ϋ h;€ΜΧήvΨώϊζmgŸΚXΆ&Πϊ౨πy”Oz<ά £cZΤ΅AI~¦[•xD"O+Ο„όR6)ρkςb>­Jz‡ο.pόl³~ςόΫxq¦SM>'γ&jzw£½Ι ΏΐΏηڞ4 ΈήΦ ΗS~Ίέq[‘?…ύεη^Ά€ΛO?ή>›­;2x:γψ¬Τ0§ψ㚏ιΫ(Ÿί}fΩΉ2Ν7H\ b‹Ψ±™ε«PHˆ’t΅¦1Ÿ+Rφt}ήζ[Μ Y•88ΎάFΨΎzŽ‚§ΈP Ή< £4©νFεGήα;³ΧΪŠαmw,Ϊ³Χ[ϋτΣ“Ψ Λ ˜«FΓO»p/6œ:βΒYΔ4ΫΥκ₯fA}•#”ΖWThω€Sιεšͺ+ Π¨―eαΞ6&+eyδKΉΦ‹r ΊΞ^;φήGuš59wΰε_^RΓ3ŽΌ0JW¨ƒΜ¨d^ηΡ=ϋD£$~θu²‘Χp'ŸώJΌΎ9ͺΔ‹"ΝMŽ*V]*0 %•¬qˆΛ΅t”ΉFΐϋ"μ €†(,ςr‚χLXΜ•΅+Κ w*PξΩΩ‘ŸΟdΔI…tpθ;±ΘJά?uN(—•"ε€!ΦXž4ͺ,Hςjβ@ZŒ€~˜JqVvδ«eΜ8‡hAβA<Ό―9γ>ω‘!βHΔA8χ @@n΄½Β"¬φ)ΤCZ|ρnΛ™'‹*_φ1²/ύ’X…ίϋ'œ˜Ρώμ*8 :mdλ1σŸ0ΉŸ—Ϋ„ *ΛΦ,―©Οkε`Ζw.tsaΥ¨»γΥίΉ‰¬¦|X?4ω&JeYJ2 0šθεΩ)²?Υδgu <\žς©˜-&vΆC~8ΩΡ·ύFρΘ¨t›˜§τΥ ‘!σ*“Ÿy„lΏtˆΗ$²ε‹iω gΧe‘kLŽώχ?2o'Pa%~β΅0*lΫς¨Ύojυ;-£ldΜFŽ*” r\Τ“yFS¦ϋz½ΜKΝ²ΰŠ>Ϋ—`0β6ΡͺφΠ‘#μŒ3"NΣρΝ2P|‘ώ0Ά=ϋΨηڝΌ-E^nξe98ΨΫiŸϊο’?κ§@υNαxiΈf¦†ρ`ΒWφΫGΏΌήΆŽ›~·£ύ₯c+ž›βδ(”¬„Q¦Μ^ΧυζΈ( ΓX©Sι>›GΏ°ζMRn±η‡ƒΠ³ψώΧΪΉkmΞΘσCο{`Φ>φ₯υφΜΥυθˆ+yTσ—^Σω«τs(‚υi3―γ("hqςJgn T4* θŒ6=0΄!J(ΑΰσζΚaDσα·RY%eεγŠ₯«φ«>4 +οΤ€yγY‘―:ς1!πδΚ¬ŒbŒ!‡eΡΪΣ'ω‹[«κˆχ՚d{•~~€Θ>!°Ξή·ΩmΠh Μ2ŒŸ^=¨Λ €KgGσgY:'+R$»Πΰ»ςidk΄ίw"Δ(:I^fΩ_kιπKˆ1, ι Uƒ%œC³> ˆGZiΌ+ΑΤ“"œμSΚΘοLbB£™ Qwѝ~#9 0¬ήώpΒ«‘΅~ΫζDΕ°ΖfKίνi|WπαcAߝ os»<ιΠ(φΤ@‚uGδπ$έ>°ν>=’I΄€)}ΌvvωΙ%‘ΰ~Ζυη6y!ΞπΑCόεǐ¦>Κ*7\αœΨ-™ΌΧ½%3ΩQp’ζ[ψΔ mι yF» 2qε.Ώΰ“4G―(Cώ΅9†kאj8θΧuψ7χ²‚}IšάπqΨ5d'Fώλ‹ίͺΐ˜.oέUšςψœ€%–x›‰oίKˆ΄ežH!›a0μgώž’ ΛΛ;­ύιο^ΆχΎiΩvgΆΕJΔ[αΪΚ' ωΘB ήζ…?†©ΰh`}h+J=AG^~Ιg‡ =Έ4­ς«€p–Ž―r\½!βΒώγqό…¨»^‘Ό¨ΜξκpxΆ\‹šžzˆUΝΓ0 »p°Ϋ\|ΊέzΟ‚βεδ^–€Oώ܏Ρ9φŽB§W‚gΗGΈ fπ‰¬ΚάΌ΅Φώο»ΐ°νDšε@2xŒxŒλ’Tˆ3ΠΌ>Ξ½J‡E’;z½³ό(O‰DΌή·Ζδΐ°Ί\C•΄oΉϋ°½ξfWXVr˜ΛΎοΣ¬ύτΓmwŸ ‹ …2Yq…&γ=œ ΛιL½ωΔ\•ΉΚ œq+΄uΔΓΐ[ά6 ωώΕ›΅ Π TΆΗ,S.`ε:N§ΫφΎvyΣF^Μ––Ο; pνͺ%κ«…cΈ-§βΥο$ˆ/Έδυ•y)j BŒB²₯^ψΉ£œ“Œ(eπψ’«°(m ΗΒw£ ύΌΟΐmXιbR˜kό„'βςUΘ^»·¬‹:םΉ]λ#‘ϊέΕ3Φ›ι†2ΞδCγκΔΘIEڎ1Ξzδ”R1°΄ΑΆπ8mΏ>PX”Sε<;Kδϋ‚›τmβ€χr&Rn “,3Ό6škΪΰ‰LC—εΜ—κLΰuhνύέ―np €}£­ζc|ΌY0“xκjέ»ΫC7Ω½šψ5@«ŽΥΰΒ lΗSvά0όΎDJƒ™­9ΫB™ΓήΤ„‹:lgθ -ύ¨h3?ω!νΠΨ91uΗ!c‚lΙΠ†N4Π>ώι£››?·…ύK»2A_Ψ /ΣδbNάΐη%™άΚG'rφ΅Q`ν w }ζΈ"ŸWɌ !?κ#έ1rβ˜ ηΡW|'ΏΣGVΟ!ές+ΖίΌŒωU΄ΰ’ξΙΒπί β;IBΧΜΧΪξ;%F: Oy-ί¦§f’·>aβε|ΜΚrm€\νqqφ [σRt‰[ΌbΑΞ*³Α$ω›^ #”ˆGΐ—λ—ŸΗŸΆ³ξ(D/χ²›|αγΌ]αYδcj7eGΨθ”aμ#HΗ|'#Ÿ„vΫι΅φίώ.+6Βθ–‚KχV:#Ё¬D«LVs3;ŊEε uΞΪΫ¦[ƒ t,ΈΧό£€|!ε€Α;œ•ϊ(ΌΈPΦήΪ»ξšΤ3·όr©€γ{οπ†Žr«@œm(Ÿ€a½°uΖΛE‘όT—H„?ΧΙ‹A5…θcH+LY­s˜'―z):Ϊ©r‡7ήџ7σ©4ŠG‘ΧΚ}=pcu―’sΕ#(\vZθ|!ŠŠΟ-υ<Ξ—*9IΈ}ΣWWΉ€aŠAΙz#’Z>ΧωΟLΫό<ΖΥX_jΔ½φ›>qεe=–‘mq,1L™(ψφΓ>iˆρAΖ²ςΕξΝ‰Κ“†FήeBθA{#ΊšU;ν_[CΛΒݍšυT»π’vzSbdμ4› 0y‡€i–³˜τhˆ²ƒ Zž΄zή_UίκΌ―EΠ”Qήγ“oΏ‚Λ>N]ϊ’2]™Πψ δδK~ŠΧrτQ].<)§ό€Xήoΰc³ς„~q¬Ε ϋΗs[yΩN.dPΪDhΘ6m₯ΫŸv’bΫ­GβψKέˆUδšέΡIΫδ%e5΄n&dWΙΎUf¬#; NˆHs‚θ%oυέ‹“Ug&vN`ψhΰΞΌ&Ž™U@/xσY_ϊΩɟΟωKΰu9‘ ]δ͌γΟ\Ζ‘Ο^ΆR~l‹9φlθŒμΘOλβ/«΄ρb?8]m°²°«2ΆΒaΌΛΠδμOA°κλ‰pXYpEvΔθCz8†ρ}Β$nβ(|‘ΓΆ₯β$ε{ΉT–άͺt€Ϋ&w&ΨFƒΙ¬ΎO‰΄½Ηӎ.Έ*Z0ε ™xEΛυΊΝ΅œ2šέ+G ςs”%έ— y3ϊ-―}CΫπž΄—‰{YM>χ•φΨ§~­ω};‘„ %DO­„²‡Λ7ŸήQr^Λ½XŸxfΡΎpΩλοA…3―:5Κ­ "HΜΔ@8gή#”Γ‚e˜/‰‚ΨΪhσΝMΑWΪΊΊ•LΗ@€γ€Υ‚+?2DύyNηCίσϊυΆηj[Ό€y9`›εδw=°ί>ς‰­’IZβC’ ¨K`Œ΅…H?ξbdFbhΞΒ‰›Γ‰4ΨW9Βΰ™―AEQηυ΅έx€ς‹aVΪͺ*#•©ρΛ ₯9ξπΎΧΖs£˜8]£ότ³»ΰΆ½«΄ QxηΚ €8}Y³ϊΡ* R…n;²Κ_ ΎO`ΐOQΊν[œ?Α€…οΡωΔ«ΜSˆ«ˆUΌχΕ'κMˆΎ?šΈ†ΜφXŸ΅¦Nttoxλ–½+ψΌώƒ0ΧPŽ6*7έΨη~mk‹‡#ΌU±€Όφ_&ŠΚ…8A&+iA=Ω¬§J0UΚΉτΘS'cHΰάABΪPmM$τ„ΤS―,&HΩΥ›‹ς)7aj-ΚμΔdXΟM[ιΣά“a’Ό₯ΒΗ$ύ1ϊΥώWιweŽz”ίζpwx`Ÿg|Qovͺΐ%|ŒΞt`- ­l―ΟΦΪζ.Ϊ}·ψΒρ˜i»;¬GAwμ•θTϊ'χτkΦ=tnιΧ^–£.€"ώΐKqΔυΘrψ 3βΡΫ&}UΰΕΞδžUžό€WΖFΊν€|Pυv “m|ySP9£ς,Š^GΈ-rΒ©ςΦ›‘Ÿψ|»λ oOϊΛΑ½¬&Ώχ‹Ν žΆASpTΝNνθžήΓΤ„{ŸY‚>` ύμcLΗ–ΪΑόβπο)η#ˆπ›žλΑH… ³[ /σ‚ΰ―2²έξ·¬ŠΧwl ](ϋs”Γ%Vq €~δ‘Iσ;CΘ^­Λ€3ςs›‹φ­χξ·Θ$@}Fs2θ,! }Π’€'΅œp nύžžΖ+Δͺ—‰’ŽiGΪbDΩ'†—κ4H*/€ΓtΌŠγ[Nž[Φ―+*•dξ|―I@­Ίi«=ωƒ‘Μ Ι{ϊWΠbΈΔgi “o°/|_nc”‘7—^`άτωύΌ’8†FPμy]0Š>tRΞ΄ϊ†A8œœCsξωPωρzo&*|W’|œ+† |Ω†—_φaˆ¬Έ•ϋXW°ξ&x―TtΎπ§οέκvA~h$=ΔΩα"o©š)‚ˆ 1ύ―Q²>]‡·°τΗY^žc€Ζ³ϋφe.=Π‡υ€‡[ΫFͺύJΡΉν‡0”σˆρ‡†ά¬HΠI\ή„gΜ9ό¬Ω4tXŸx€΅Ο|WύϊŽuƒΓ Gΐ„/άΩiΞUΉυ Ε]“N@Ε—ƒ½•Št`€ϋŠ+μμώηd‰Ά‡_φόž;ιR0pΤe ˜ ˜„Nylkΰ…χ΅€{“Bšu’ž:ΛkψΕΌ\#.O)—K_ΰ΍―ςΜOΙσUΩ^>¬1G»iϋΤE“"Ϊ‘DΪEΰX³nψ•1+oπŽZωΤ‡ ωγδΏγ—nwd,cYxkνκήZϋΛί3oίϋζeΫa"φK]"ή2ώIΕ+ύΉ2ώΖ{8ςHΚΰΘκ§*[¨ͺŽό _™!RV0ΆeΐTΏ³1=[OΑ΄–ΰ°†8+ Œω›˜³nχμ˜8ΤiΤCωΕΣJΣ‰Zzg{mΉX΄σ·έέsnlχ²™<ρι_o—/>ΐ0 "œ8;7©0ΪY €§ΠΪA¦w ΌζΜaϋΏύφa;α]ΰΒ¦χ«σkδG QV:‚ͺ¨ΌΖͺŠπ«½ tΓΐδΙ€Mάz›Ί-νΐVρQ,(>Ϊ…»ΦžΎ²hξ΅Hυ«Uƒπ₯pαΔΌ½ϋ΅ϋνζT-h¨ žy•6ώ ž?’ˆη@ζ'έ¦U8ˆΙ3¦O-ζ₯`αSˆ³½ˆςY ˆ²Εhά‚j•™νŽς§ N†-/²βqωΩώΦh’„εC₯¬VS*_ †ΟFη•§ ΘύWλΩι oƒ?Ύ”΅ΰηκmJuςτ^^Όv%j•(`yΤJΓ ‘hΌd°―4Ξ*~ό€ Ν4Aεε|r@eμ$ΐU»“oF΄ο΄‚Ϗ” ΰG0NY‘%Δ "ΰΐΛδN~™(…KBρ%†!yΚ/iς˜l·šS·qε5΅₯ϊIΛΪY˜ˆFΌcb#ˆ\&ςbς}IRςIr{έΧWg²$Θ›ΫυKy'_„§0trPO^3lΊρΑOWόήG‘qUŽœhΨηφ ΖΨUr¬—Δ+cΘWϊPƒθ œŒ―“όΠd‘3ΚYz mB=ήc’ κΙ‹Ÿœliψ™δδ…@d9vͺ<>£LΌόD8/”BήΌWgΑXHπ–Sž­/Ν£|ι|x3abγδ4χv(Χc‹_όΠ–ΛŒ™L„3!₯œeα‰Ζ_Ή΄ν™°Ϊ'G²<©G5‘NσΠ{žY„W:G[κΒOϋ 7ϊΠvφ6§ U\έ[o?ψžeϋσί΅hΧφi—ΈjˆW›‘Ι8mΧ‘ρ?Ίξ―Ž»e©Š„T}EΨ 60Ζ5 ?`…19ώΧrAά}½0?„N)q™o€’f‘‚θ…{x€VrŎC―θ4/u>i>jΏOG\~ϊ‰vΗύoyY|=πe1˜νοΆΟμηΪtΊž{¨ Ό!§Ά 8 Μ!”Ι£°>‚‘x΄©ƒΉ΅›O¬΅Ÿ}tΡ.8³‘eͺΊ„3π*ρ„κΘ›ΎTΖ( ŸB«p‹7΁££Wͺ*γ «Τ5Ÿ°z/φ»=-<ΗΞ|Ύϋ5³Π“›η₯όVO; ΈεδΌ}ΫλvΪOφ4e …f`uV₯5ΐΤ \›Α"\ώqa…~9Β#¨ΟνCΫ+Zώ•.Ž„Bs΅nω(²¬—l dr ;442r'{h ]8|Ι‰bξΚΟ;£ΟΑhš¬˜¨\Α“λ’!ΚεQ?·TΝsBBz UΪ.ίΉθ·ΔIέNF4,i«°Ε:Σ~ !bΫm΄ΘIWοCολΠζ&.σ11+‡­I»tΝͺšΈΧ^σ8ŒOΆUώβ m7Ώ•<‹όƒ‹ΚXœΎ2I[²΅―1Ώ“€l•“—ϋ!(‡™Hχ–³Œύ`Z ‰τά•\jΠΘ@ŽF,7”I8ΌτΏδƒΎΌΦ2ΈœΘΪVψ“mfλr²DVy ―r9Ερβκϋ<ύρΟ–Ή/"ΐ*ςΘέι—ύŸΙ€όΧ Ϊh>ν―ψ͍‘4_>ΘDΜλϋϊΫ Ζέi0ŽϋΘ¨εμs A&nςψάH*O1β1Ψ΄+Όu²%Ώ€W^έ|€ʁƒz7žΫoLB²hΧ8±YpδΝ‡$δE@)e]΄%O’ Ky12₯α·}β¦ήwJ¨'4YeυV<§-ς&ν XΛ gžτXΉκ§kθΊοzΣ’ύ;XΤ«~)η.&%Β§ΘOŠΑ§ΤIΫρΗ£_eπ=,Bzκ$b~q•gκ Ψs₯£«ΎΤ* [ΐ<Ϋd=EΜΧw)ΣωΖ/ΓAVNT©GΤFDΩY93γχyΩ‘Hb0WϋVρͺ+Υ§ƒ„ηΜ ςγ_{αΉvλkoό§^€Η~ηΫΑήn:!Μη(AΧ€@妄M—±3±_«suΎψόo<΅Ž>κπQΗθz£ΉyΝ—ί:’ΉΓ<υ£\80ƒ$BΥ(F }vςΎ;Œn”g₯+μ*’΅vχΙe{ϋνΪ([¦αFŊ`:ΐœί^΄?τΐΥφΟΎpL»AZ"¬…Gίς†ž κ΄Yά•.™&8O‰'@~dϊτΘH[5hCΚΗ•««SΙ‹6d ΩϊœQδ1Z”ΟΞό³ œΌΓWFίΈ8_/TŠR¦ήμ d"aώnΩjb$Θ“ΧβΓ Œέ†άιοjίΧ £ss`Ÿ@΄“€{)b ~ΙH#=ΐ#αv³ύ RζˆAfEhΨ\mΪ/Ω^ο ‘"™€ι{²ύxΚ€w‚ηϊ³†Οti ύ ΛκŸ Ϊα‚`Ρ§žΌ0ΗvΛ ΊFŠš|…rήξ(|16|±ξš Rά> ΄tΒWκ̎„tλ›'=Ά™ΆEαΡΏ™xˆ>ζ:yl½–Ÿ™,ιΛ/‘ΠέΎ/?ν£Or…†ίK/N’ΗβπςiK vv¬WΊ Ν}Β”63ž†Ρ‹Ό₯]΄ΓW:“/mΩΪGnΨtυμvδΞ}‹φ2³Žyσδ«“#ΛJ›4dEΉiov8p–!iσ¬­ϋ!ͺYΑΩ'ιΘpπmΌύΟ')«#£o=Κ―2)ί)ώ§<π]ŸŠ ΫϊΉYO™7šF T—‘0ΈΘ-¬zδΪώzϋζ–νokKίό\’Χωa™—uyΚ/Ί•ϊυξ~h“‚±ήqˆ(ι ‡#’‹3jzΉj₯M,ή₯ e{ΈB_Γ™&υζΛ;~β~ςzΤXjΦ~~;₯Τ£ΊΆ|<+<Β[ψ ‡ΖJπ_X¨qή—Άwυb»ιΞϋκQυΨέπ€ύkν‰OZ›l Hf·KKΑ• *ˆGƒ& Μ^‰b―ήJχd$ΥxΝιΓφw?ΉΦNlšη`Œ³e{§F‘›™Uiς\―#]Uœu‚5³tŽάΘ£ρθω ϊΊoǏψ9BnΖ€ΰ_Np`Φσ€©/­₯ΝN$a{zΨ>πΰΥφΡΗΞ0ƒ_9Uiχ_Š€γζΑ–ώΞ£1dΑlžΩζχ°©Ζ₯₯(o†‚‹ΑЈi˜½„ δ«ΔφΑρy›'JOe©Ρ4e―2Άι–LZ€B„Wή3 αφ&)―‘SO& Λ¨d‘ΑΛ΅B‰JΓ4Ώ…‰ΓMΫe€L#Ο›¬²uQ1PΠ*m^‡·^z/T„nyν 9+e'–AAΗΨK‹4ΈΥj{)―1ΜΆ·-‘ŽπΔΏβ@zMΚΘΧ']Ω\mΕK †+TΉφΆπ[ήUΩJNHH›Δa»‘fq˜QU8RΠ₯e)Λ_ΌφνΠπΈ“bpΡ—,tzΐ³zK‘}ξ\!lKd™2ʁνφθτΖθΉ+ _μΓΰpW§f5.nVϊαx­ΧŠs⃇yΒν{…›δπά.εZΉ†Σ6€Mn½/²»β{#άΚN’».–ƒn ³υ- £/ΐ\ˆ!‘v°ˆΪσι—©rCό8U&£ςΩΆ 9υrΠΊίͺpl ƒάε~'vΚ ύ1֝¬\JRf­Γφ‹Θ1‚³:0B„W“Uτ+OΒgΒ‰œdŽG8τTΦ™ΌUhε2±ώκξZ{Χ}Λφw~ΈΎπgςκ:Ώ^ΥBΐψ£K‘‹όάk₯ώ"νψ$  Υ” –φˆηρqςXeΎ.υPg‰[ΖvYΟΧtf¦ "Μ΅υO’|2Γtaq½Zœr° ¦|Œ|„’<Χ/iδ›*ήŽί‹Džτ9‚—ΈgΣ·ΠοΣj·Ύξfή°ξ†Ÿ<ύ™ίl»ΧΫbΑŠΊΊ#ΒΑρPΠdΔ'ΒΑ«ξ*•ν%ρ¨%ΔΊλάZϋΘgζ؞i%"Αr²3ISΙYƒ_ΐψε†!+—R†… ₯Qρ» ‹kκΎ›p*π«€9+dŸx~ΩώΜ;΄•V¨ΑwκS„μ©ΔπΪ/΅_ϋΚ©φάυ©»©\NΒ…"Ωό²Ε'ύ‚Δ‘;–ϊAΤyl΄ζK«t'1Eς²κ‚ξάανΚIŽδΩ/(κ\§Υ H%C₯+Β¬hœH@F^Ι*~*nκΙΆqŸĐ;Qb’ΰϋϊηήXΕ‘ηΡ½Ζͺ±F‡Q0#;GR2aWδyώ?DHιEJoρܜF3\Ε?NCκύ )ƒΡΙPϊt('Φcž—Kˆ;ΜNˆ0Κ…«>Ϋ!m2&Nv ;—rδ·nτ ρ΄Aίς₯Q„€e²j΅KΌςάδδrM7h γ…¬οΟ}+έςδF›s8αρ1ΆLψ4~φ›uΪONX$Λ:Σ?E8JOϊΜ΄'DŠ•cEš} Ξ‰ŠpζIΡ“&ZΦr™,€Wμsω m$δ~ώ™dΠΎΤ#ΌΘ쟳[†"5-$QΞ;μ«N&&@ŸΫ–μ2ΘίZyuΦ¦ςΥ?νΚ˜FŸ7½ΆhΣηφΪδ"γϊζ·n·Γ›ΰ™—•œtΐ³άΘ¨Μ# %ίΤ•N‘ς©·'.ό™;π 0ˆ…πœvΘ B ‡)δ9 HίΖγ,L1*ν-#k€Žθ `ΌΙο{ί±lσ}˟ύGέΆ Q‡χοˆΣΕ†ώ˜$¬ρ^σœ{GLW±$oΐZq…MOT—δ§ΌΩIοψ‘A?τ›ƒ'έ6„U~±3)βς·:½Ξ ς&΄χέWίΠeλΏΧPvI¬&qΪ[LΪ_5ώΖWN΄Mοk ?¨βch aKημ€7ά³ͺLw_ΨξΈ%1€D#0xHςWκΔcTIӐŘΣ~KG±y€Ο―.5˜ Θ b֐ΌŽ3ό™TΈ Ԙ’|³;œψ€Ηr1œ΄o’x;_œ„¨π ϋ›y3Φ,—f|'όΤkΧ¬³Ε«qw’§wΝy 8‡pΈεγζJΫU$~ ‹|΄L§+†ΐ£ΣCe16kw\G>€Ÿ2βΥΨAΘΝ—1d€uΓ•Id%Ežrν}ŸΊβπ ί:ΕM½Ήξ.Oπ•Ηϊ:"τ«Kύi8HKŸJ†7ςΡσ©7ρ°Ϊ°φ+‡ΡȜΈ¬?tPYOIΓp /›BaS_pQVPΫ£ό(πΐφ8‰Θ€@Xσε‡C'Xπ%—5δΉγ _CΉ2€S3‰1κψ³.pζρF ΏQ†ρ_^˜_Ψ΍+]γν‚ς$|„vρٟΉΡ΅rWC8:ι‚©Šώ²-ςΪ|Ž*cnͺIZBΐς„cίδ›Ξ$*PoXφΪl½ύθXΆγ{mΧώΊ1 FλKŒΆτΧαρ1ˆξψ]ϊI‡žςΔSϊΈ`Hξυ.ŸrRH0†rΗ‡ΖμΦ&-ΎΆ#+ΪΆ{ψ χ|Ϋ&8a s΅Vα|¬šθ£ξͺ\§ƒt°ι¦₯LωiαU ‘:Β—έ«Ο·»|§nHwCOžψέ_Ν3•vd„C*k]W‚Ϊ…+p€˜n‡%–^κ¦4½rvόί~z­ύΒνωΩ]ii ―%…«Ξ-…_ .ΧΎά–wΥ©""έ―bς[1φxŒΪή,†#J zκ^jΐ›:ψϋN‚;_ΌΪŸϋZΏFW‚\„βι+νzώDO›}†χ]wν΅7ή:kύΒ©ˆ΄Ί½VAǁ@’lΧ:ΪgqΟνŽtΛ₯²^]βΙΚ!<)œΝ'$‰Œs%šU€Ϊαkq5`Ί|o^p·JΓ© .ζ „ΚήνΧC/%ψ|Ί ΡΤ‘z]*IŸΪΥΨϊZ^ΣzΡύ$Ψ/Kτ΄ΞΙH>1«‘χ>›νG_4φ¬Πσ>¬T©_Zc8 UŒ ί;ΌC»•;—yάBIs₯zŠCγ‘!ΑΟ£oΚ UG€quιλŠ}JΑWΕj€3ypη0kυ ˆ|ΈΘέymy™ŽτΙ/ΩAΉ@۞Lπm7ό(£O>όΞ*ωδFΆω³bu²Ζδ3“)yΦ'ιoeέΞ1,1£­αIm«’¦gHƒjΛV«ΰ‚ΟsΌ@gn†ΜΔ8tH'e";=!uŒ„q;±“+J|dtγŸΓϊH άY;7ϊτu&φ=ν²)αaλ]ι‹`§ƒ#0ό¬Χ―ϊyŸt{sαόμ&“6Œ"8#ωƒ?ߜ¨€Θ^Ϊj½½IΊΠ,# i‡Ι‘9` A“tϋέΒ–± =«2νGθY`Ρφiά_ϋΰ’ύπ{–ΉΫNT;G{=σOΆΧ±¬o²:Υ<*7mόω@Ψ )! œ‰©cψar*)ψψΥ6yF ό Ψ¦ωJA:ΡΩs· J«“ Γߊf¬½ΕΗ֞ί[oοΉ}Ωξ<ΓͺΜz9€ΕmTnZ’ς ‹σ`9iwžž΅½εZϋ/ŸlO]ζf}›[/*Ξ²ƒ~ι0,~Ϋh€#,ή(pσ+ΤΣ;ƒΆ¬rCgΑˆΓ‰ΐ‚φΓ‘ΐŽκsύΫk΄7ΧΝ#ƒ”Π‘Υ†°0'L*½`ΎύnX#νŠ=λ%w5μxhΜγNJ4^ώ»‘wΗ(ΟΈΣίi°Z+ƒΠ$οcά<4*t£{S₯«[ } qo‡ΖΘ hή½_="_dPφMΊ–ςΚrŒͺΕ9­&eΚΎiβ₯nΑW―o―΄τ‡Π, Μ.2Η_L]ϊ1«}/©x ΕY±pΆ›rN$μŸ0ΘβχΠhuωΘ$ΟΙ§a±ƒΒI]Ύo!Mς¬·PΩ³-™X*WŽK&vye±“.Q8τΥΦτg 7eκ«Χρzˆg;δ™oœx―7Έ—IC‡OςΔΆiΌƒWΎΟS rHŸ8νg sΚUVπ…Ομ³κ?q²΅έ_Άo½‘Ηώtp%uX§:ΣώΛΈw|‰#Δ”_qΪHΎcgυΦ?)HΎ0–³ψͺx/OdU—~e–Ύ#“¬ΰ+ω¨•Ά ΄ΖŽμk9@"‹a†<WI+|1φ¦σ _€Αt"N0ΚΙ«ΤVψπ†Χ₯WύSΑ[:UsͺΊrJ:E¨ΊθKξ(‡^˜΄ ―y 2n0wΓN>σX»vρ©pu>w oPaCHB;α©Q`|%†αC…‰«ώμΒ“Pόη ž7ίΊΦώξΗ™ D,°°L œm‚ΚUό Δ!p`*Κ4/r°*(‘(έΘ ‚ ΐ+dΡ½ϊ² j¬a ΪΌyZOTͺ£K•T7Ε)θ‹C膦½ωz&ΏώψVxΚζ eωαˆK2δ–ΰ1™`iAε\!€Q–V7―xδΓ'fu’bΜEgΛΘΠ0kθUΜι(ά*§m•₯dHχ/]”ΝχPΌΎ»ήjxp™Θcπf›[Γι όŒκIύ`qΐΫ@Ϋ.nβŽΥΊMX8―Υ ‘Τ]½η5½nσϋ2—σ}ΏΩΊ―Ύ"˜²„λπ’¦qqβ’-uqΞK{τ;_•‹<ΎhΥπΠGΨB“emΟρΓ:4˜N²b'΄jHMw§%“ˆ"+§~λNΫ K>ΚOσ‘Χ₯;–—χφ‘ύ£lR€,XΡY?ϋXΒβJ<τ›λμβšΕ>ϋξξŒι—άάh[FβDΘa(H(G²βχz»/^rR$N'0”΅­cηΔWL{χΏc+7-Š[žˆbLE~Ξξ€cόO*—δ[Θbp&4δsΑξ9i‹0W{Ηξžu,ΰk½QœςXxx#ό©ΖίI§»3ΒJSΪK}΄«tˆθΑΑ!)Σ0 β`ͺϊ οψ ‚ΚN»ΌήοDδΚNkέλg}—νŽσ‡ω²_κ‰°ΙΛ–ΌΪρvZL;–^:΅όͺΣτj{₯5DMP …ΗαΜΝβΜtόλΤ›νgŠΰΎ¦<¬AD§Y‘rϋήφI_z/•yϋ7θλoΨ ΐΕΗ>Σφ―_ΖΨΠ10³ΛaŠh!˜v%xd0O-†w!Νaΰκχή‰)gΐ<‘’_Xά=uiή>}i=eΧSςV~π₯βS£ZκA‹ΆI~(` Κ/ƒ†j9ΗEPΤ*‘υ”e>χΜ’ύιwmP˜Ίž'Δvl₯X„Χ‰6>“ίιύΞ»Ϊϋή°Χ}a£}ρ…iΪ3ιν¬“xδ‘eε“Ό(ž™–&œ W©ΧRV.W<~ΐDz=ΠώˆIίvκsΔΪg"@iΈΕ₯ Ο±ξkͺgέE@Ÿj¬„†ΰ—"eδ+EہςGv_πͺΙ9Q†νΙ6ΏΚόKoβΊσD[ά²΄δ‘/ŒΘ!ί—,4ϊ"ŒΰYgΘ¨Έ Χ0₯ΒrΉieο½ΩώΥΠ©δ•Gΐs9ΒΈmaBJρM\=Ί:t2Jω°œ«\WςΈΌj˜zs”όg"'O5‚i°iΰΞC7BYΑCs&WΦ#ι8=csrpΧα[_’y9“b£VT‘gΫγ„Μ6Ρ?™€Έβƒ—δIΖ­ψ­·|qΈςž\άΟk5’™,hψ=μ[p»›α{Ό|1&δα­8:mΉ†/―ΰ—¦€w9Jίηδ!;΄ & =ίƒ²i³γ±₯Ε‰ 'ςt œ‰ωΎIΠΆdΧΒΙ&ω™ΛC―hΣg‘Yρ€wŒ1ίΒHŒκB@&ϊ™ΐ @ztΙi7eg,ό’ίŸyί²ύ΅’a3τ€ε6‰“υ.sEΔΩϊρcψΥeΚD`‰ gZF:ΏΔΛ.ϋ|R';κΐUΌ"«ŠšΈiKΆΩ;œΏ―ιDNVxΣˈgθΗ­ςƒŸυΩυ η«ΰq―|Š―ΜπΧϊHΣ·³lχpUq‘γIκIτ¨ΎωΑA`Λ’7”»a'Ο|ώwΪ\ιΟ9ω· (08BaνΎ0‰*eΊ}«#9:¬‚†½σώυη—νΏϊΤzΫvΥfΗχrΦ£  !ΙNC€’φDqΉCXe§β“>'βΙͺG<ΐ¨0πΞΰ‘~·aχ°–ΆνχlB 4ζŸ#ŽbΆΣShKI[oϋ”c‘Τ>πΖ½φΐ-‹φ©g6Ϊ »υΞα2Uˆβˆ±yΦΓ\Φ£“Π^sX– eπA!ωΕΫΘQ­<KΎαH ˆΌ¨z;œˆ@2ψς¬L€_^₯')ΙN_\ςΪ΄†ψώ0ί:Σ(KT[cDμjθηΕm§Hgb’qΌ4‹’žά¦ŒΉ΄JA@zΪη /xqΐdΓž<ΫΟdΠχΓgβα‘BTώΰσ©?‘£Νͺ:―γ΅*'5β–'”Κ t*·ΉΣΆ»:3~Vΐπ!OI0q±¬Fε1ψCͺτi œτ{ ¬'Ϋ6jΘ’΅Ύ3·Αε όͺ—ύH Φnίpg±1’'―ΔKR ²νSφ½Οƒž>λω‘K'?|όξ©½\’ΙdΗOσκ;y°­ςB| –Ρ…&‹s’傐.―ιWeώ[‡ΧρΗγ—Κbv<άΥρQSεϊ…ww$£Ύ€ldhžύˆŸΙΟξž4ˆΧ~χζKοUWβpΣ2xŠ6eΕ²ŽΏAός26œKαά/B~ œΌy™ΘZ7ΩΚωΞώZ»ιΜ²ύΗ’΅χ?ΔP@]ͺW$Ως!_XΓ„jόW\β"!8σΘ\M„Y³Ο’e<#žΖŠEΠyX=G”N‹ri©€+°9LTΘ΅ΣV=R‘τ(!#βŠ0‘πξ<ΣΪǞœ·§w§θUς¬Gpαΐ#ϊ1©«ΠΤφnΑω}π(bƒ΄¨¨P€ΦRƒ»πZŸτUJD(‘ όα‹‹φί΅ E`ύ!όYEX“u‡„Κ‹ ΎxΰtuηΝ„kν΅Lj>ψ¦ύvζΔZϋάΕi»ΆυρYΫd«I„h˜ΧΩ’@Ρ'_ Ί„Ί.~ Ψ(τq– NΞi[kTα[―ν±œuΰ kι/1@An£ό[ Qq8ΆR'°YQkΠ―Α;λՐ†-Β"IΕn‚½ή7―ϊ Ό[όβJ cn€^oΜΛjΥΆ‚7τQO^θ’’VΙk5€ή ηdE#0 “ωˆg  ‡4(€EQJ4L|΅0 Κη†}Ÿ37WYMR)΄εΙƒa¨νω*ί€:i΄$Βςΐ2}Βΰ„#ΰ’ mΑr• ž5ΐ#_ζΉcΰ₯ yδα–80pω¬­τΙρΙΙD€‡0V6υrδ1C&0 VδίάDkΫ•mΫο㊬ψ§>WοdΗgψπΐ³¬t‘³ΆΞΕOY¦Ί‘Ω1VΜαΙΆBί†» ΟξΣ?σ2κΠζΞΐB~RGψe™ŒΡe›J‹“y(/Dg{ Θwε„ψ‚#²αWF]ρg§ Θ?Š{D/Hwh$z6ΐ}bείσ½/&qι$ΟK_‘=Ϋ"š.nh Œ«ώχΣ’ύG?άϊ–Γ£π*‹ΦL:ΐE°ΐ1τ‡αδ0&UNΉιΖί$\Κη0˜(œιž’.ˆi½Žΰ†|³½\I~Q€j0ελ:¦…‘SΎ£|·Ύω½N…Έvz½žΙ“q¦ >:`$ΌΟεƒžjjba1υ2¨Pl‡υvί‚E“< iωWy_dwΣ―%tcΉr0ΫΫm—žόBSšΙ·γ₯βHš7¦ΰW€" C9Θ³ϋ«[–@|ŽtXΉ‘6§Γξ9΅h?φٍ,ΈΡ;ΆϊX|%Υ©ΦCΨβ$’²P Π©€«|-/ Ψ £τ?.p”Ÿψœ4ώS‹φφ σvοΝ¬RB3ΚΚ•`ŽΠ@ό(·Σ”μ5ͺ.>ΌυφyϋΎ7ΞΪŠυ‘ηΦ³rp’“‚6Njπ$έ1ΆŠΔƒτQŸgΉ[‰κF7VjhνΔhΰuαO+{ͺJ£­6ƒŒ^¦{α•χE^‘vώY.w}`σVun·uΖ܌©R1ύ’]™¬Ϋxμ“δ¦ΗϊέφYΉw£›{H_ηΘ{ά}LΠӜ,\ξaWσΠWm¦,¨r ΖΛ7Œ~LvŒˆΥ ν ΆγŸӘ§!qr!4Πλή΅zΞ?|²έ‘sε>ΔΈ™‘?ˆc„νΏ\ο‡i™ #jψ–/Ι \=^H†’γ iI?pj9-ψcθ%ΘΊ4όV) ςά-R>μFžγ`‡ϊdΕ?ιO`ψ ύάηpŒžLπζΏΛύž ΚΪ°1AŒL%„K[ΰ½ ‡Ϋ0ΕψOΌ”`s…/nκΘwύ©#m±ύN¬‡p&oβ3ίΎς°-ό3‰„‡œϋ€γ°M1ώή΄š›ψΫGΉΡΡ ™ε Ρ/B.δ)t8Ι ½8ι>pBI†dΪφ|Θ ’C7ω6+}+~ΪwΙΖωS‡νί‘ΓφΗΏΕsS`h\Υΰ¨CdI%Œ„ι#†Ϊ€ )£)[αΘι–MωqτŸifƒ0Έ‘ƒΓ³mρ§μZ―Ɵz3qEύ‹\τ€‚WΪΓΓϊeΰƒ 9<* εMzΚΰSJ<…Λ$k৬§iΓΩw«r‚‚7θo K©yBŠF7e1{α5――Θ δnΘ ΐώΥηΫε§Ύ}1Nšπž ³Ά`:Α°\‡Ξ½oθϋΤΧF&'=“μrΔtθλ/¬·Ÿ}dήΞϊφ‘0>ΈΆu,K<…:€uŠέ‡Δ“κxΣ.ό¬(Υ$& e„'²Bܚ²Z_΄?σΞ­ά h[-¨ΐe†g½U–Rǜ°o—s" .{ϋέ³φύoDAbg}~=/ ΩHP ½€g'\iρ‡mΐ\AŸ‘ΙΣaV’oσΜΪœά…dέΞ €$bδ €ΡπΡA ,CD+PoΠZ’t]ρΗΰŸΈ½ƒEQ Ά;«1ς€'ιw =0Ϊe€1<0””uPo‹Έ}Ο;υ1y7€Fω4 Φ㎏[άvUϊΞ)Χ£OohcXroψjUk{ γk5r–s+ίζpYω k}dϊζBEΑά!㔆χ*¬Ξk•mι=Ϋοε erS’E‘ΗϊΕI£C8ΪfC£+Χ0Bι/ፅŸl/|‹rMλρ²†wθKmt§dκ»σ1κ~=ΟΗ.}ΊaΉ‰Qφ© Σ¬Ξ7βs`˜~&o(Μ·ώε£Ξ6¦ ϊΤΩ‚ΐO0qΈJ[˜4„&hτi Ώ1±8UγΆεZ½»ηΰ%”c‹ Κ72±&H²ΙΓ„N)γ„0oόSvμμΆΐ κtΌ΅Ε٘ϋ["·EΏ4ABε)傝t δ¦B/k7›ϋιπΓφοnν?ψπa»ϋζZυƒ­j±ΌYΪpχk‡΄“—jΑj₯“΄|sυmC…kgU8q!γϊζΩΈ8ρ—Ÿ³όJ]&8Π[ӏύ^δ i$ΙεΔεƒψΒΐ]ZΓ„&³Ls $ςα‘D =ΩY²P0TYCuΰβπσΡ4”#ι)Πλ#-ν §ΠQΡ eLέ~ί[ΜΌ‘ά 9Ψ»v©]}ζ‰˜» ‚bm §!„ύΥ,³KPνδbΊ§ΜΊu½cLOΗ’nR%[€AHΪέ§ν<2mθŒΈγΈ„YY20έPό`(a$ƒNJn$œ‘¨(€—zς=·UT⚨π0>φθΞFϋ{Ϋν>κ$λ&J£βΆ7~§Α”ͺ/΄v'9&(΄wή3oίχζe;ΉέΪη/η]α–8Bε Ɲψ‹MΏψkθ ŸAήγ€Ζ¨B―jΨρ£c¦mΟͺ'€­)#,z>΄ΐo8 ˜'ΚϋέχΥj£ΰ}‡ΧYAΤΰkά5Έ™ΈΒF9ηQ. ’]d?I«x\νΛSο£_§.ΐΑŸOK(FBc(ύΠ Ž|h(+ΚΝΪΎg…Α·―$dςαδ@~hΔέ%Π(αoŒ•―iΆ ”i· VŽsWΝۜշτ{Wπή΄#TEβ (cφ9즴۝w<œHC{ͺ0y–U°•Eψšzν‡Θ)'i4ΰΤ |vŒχΌRμ:ΚR$ΌΓ-M^8ΘΔ*—E¨;ΥΙC Ύ―ΉŽΡμmΛͺ]>χν8y%/Ώς•ΎΆJdΓώΙ#ƒΦ#Νφ΅|4ψR~PΑΔGsxΩ֌'[§Ι³2|LL"ΝvzŒ‰‚νΚW ;aP~ΘΐŸΒ‚OΉœh’ŒΌ‡α=Gύ7–˜:ܞ^@ΏΫύoΊgΩώΓnνϋήαM~ΗWύΐ;ΐŒŠΓ i1κΑX0΅ΉΔωW΅UΆμy3’W{™Θ|₯Ώαœ₯9I…CXβMY39Rm’^ϋΆ λχ"ώ”_Έ˜°}b€£‡Œ±X„5¨o–#l~—²†Iλ°$Ευξΐΰίr=Ελϋjz«ρ›'>ρwˆKN*!mΉhwΌώ‘δάH^[n$χΒ—i_ωΜo!Δ¬$-!ΡΏCΆ¬ό †“΄κLό’ζt‚Iy9 ,”ΗX±ΤgpΩ{β¬-λiνμΖ²ύπίkνβl«ω>Ώ²Άp`ˆ_l§§¬ •Šσςά€ςw±}tοBήΠζ ό4p"M…••(JΔ­•±#Εέ‚ϋΫ›νυ·Ά§Ϋ³Χ}μnήf³ƒΆ0kσω,ίEΘeΫ…_α¨[ϊ2Ψ@IM5ΐ+”ςˆτ JK2gσIϋ™ΟnΆόΞzϋΚ₯υ< η\e(Χ<η |Y^Κ$Βα#υϋ~rJ!T…“₯qϊ¦σ]YŠΞUOL Ό΄š²p…hές cΰλ]4άzqπ±θ ώ<'QρώΌΏήIΗσ‘ ΰϊ•ύΆώœ_‘[ΆωgΪΒQηΔΗM―-Ϊ†ΧŽχζusΏέ Κ{=Ÿ΄ΊιθνΘͺγ#ξ‰W^ϋ2b0Δ•φRΘ6hε…M΅/™ΙΧ 5ΰL<&¬šΧ˜08YΜ³ο~ݐŠάAK&Έξs¦HrώμX§FOη΅ρ³ pΨ¬"ψΏωφyϋΰ[ν~&O^]c" 0†υ€qΆ¨8ˆΞXŸ#τƒσh«A€§΄νμn°”IΊν nΫ,Ό„FO# œOZ¬³^ΗH%]$NΒΰ§ρPEyΩκΞ6·[ωφ…ύ#^―έkX½ω—φ‚LY›°bwVο€!7θωάΉ«Q Ž«>ΪXΌPγQζπ_£ιN…ν2 =ιoςσ˜š _Δ„Δ7Με…CΔRΈΈΠ*ΫμjΥΙ’ΪLΛv³Υ‘zΡΡΣ»œΕ97%έΥ}ΎW !εo~h΄-ΔoΪjs&Dε“ ^ΟΞλ{α·ΖvΌ¦i0¬ΉΔ n&[Φ‘N”Ώœ+ Λx_…+sκρΖΖ(ws{i 9+ζΞίδΑ阏~6Η]']ςΩ‰†‚π{|‹’υ•a…α,ηxV#kΦ•ώ!SΕ-ηc°εs/»jAcΑΗoΐΗ0‰‡2Αcb›§XJίͺdΪk[έsχΘώπ«;»ͺ…ΓφgήΫΪΏχƒkνΎΫ—ω©Jζo₯ΖδGΛ«¬ό­˜™ƒD=€txx’G|[ΠΛU^αZ…‘Ω ƒΨ•)B2qΫxβζΫ?€J»“ΧBGvρ±χE珈CΏy† ΄ςΣN#&φ²©Ux‘@‡τ™§-ιΕ,“ιυXƒxF±L B—°$€)…7Нƒa­Ϊv §Τ―ΏρήpCNv1xΑrΐΥnu¨lTjάZ£%CXσ(Τ™«Ž΄§HK‡₯Λθ(:UΈDI%_ςΰΝ­ύβ—–νβώFΫp4+VjS§}ZԐŽ0d[Sά¦Š`“UŽ0[TEBρ₯»)I"η(&ρωl·†:,γx›’ σΙEϋ οήj;³’XWΒiΌp gUεFεB‹ί‘u©Z‡3¦Νhχ\X΄χΏqΡή}οa»²ΏΦ{a½ν $(Lυv΅P/iςP| ~κšΒ§:ΥΔΪΧΰ΄ΎZQ…­’’Γ‚³ŒN=jq§%πΜψ†)7ZaŒ}ί½xπ.zΚfE+ν•«ε¨ΧO½zMΩa”.8κf=ΰBΏ°ƒΑυz΅ΚlnΎυΩnP†T`Tp‘”I³Κ‚†N|Κ}žΧΖ5ͺyΥh^μŽ‚“θr‰œ‰‡φΙ$”κr€2βξΓh£ψ_κΘJΤϊλ.t―§³B¦νγ²Fv-Θ‹ςcάžg"‚‘υξύ¬Πν?κˆoϋ…‘επΔ―ά‡>΄…§Ι+ωςMϊ5ΚX2ÚbWνVSˆΘOΫ©C'/5Π+~‡Χȍw Β―’)Ÿ’gVώ1d΄!od΄+ΔcΎu0 ΘeΫeYqΕOzήλΏ’ΕQd½υΪ‰AέΐGŠe EθHwšΗ˜°yΙΗ3]Ή/ΓίγΐKΟϊό(’Ω>όmkνoΡ΅φξϋύπMλ ϋ‘Œ­Aυ„Tu?醃ΪΤ>† ¦Ωnυ[FVχXNΏΒεWω±°*όΘμ/q>1ΫΫkRε– R+ν_βκQ?εzΓ#h ΓKΒΆΓΓώΙΦΌπφ…U*7όΒ_aΕ'Π#Ž  ‰ΔΥ_fHŸ«ύκΣH¨`9zšυYΖHη°¨€‘ƒ” BN‡OΒΐ›;Έρ.ܐ€ωώn»ςτc0B±<«L›ηKμ"€•&³#Ψόν€ΐQΊw›‰½σό—αJT ƒsρ‘[gνΏyx£@ΙXgŒy\Υ-¨ƒ"Q?αoιU‘G³α,‡‹rPi— *[z o¬4hΰ{όϊz{ύΩe{π֍ΠTœ pXƒΕ:†ΠΰΟDσΊ$r8WPUp8ω$J|ίπη›/œ^Ά?ψ†e{ί›ζy/Βγ—Z»-Φ”‰ΒlXΕzΣ’Ί¬Βώ0―κ#lYBφΡΨJM)ι„Lγβρšaε—₯KY_ƒJ0όbΫ›ΰZΩ+ωkŸ‚Λ—ͺ ƒ••³ε]IŽUcξ Π0“–χ7›65΄}ζα΅ωt₯°β _Όι?@’ΠΝOš§@d€#;[2ς cοτΧθΗΈ›Hž+| €Ύ8!±:Κa\Ε‚7/Ό‘0ePH*|N•'}Ϊα'mDxEn@db‘ϊvυ–•Ών$-ψ4”ζΛœ|Μ€Α°uΛKΒ”HΊν­‰WΉοΐ…—ΛTG ™Œψδάδ'Βt5ƒΧƒ<²ό―sΝ\yδֱ⎌x4νχ†(/ζΥS€Λ[Η „ΐΗ|I1tQƒA³:/œT-;VτψΘ„4ε-˜ œά‡_]ξΒ+}Ž!#‘Qγ=?| μW™hZέΌ§΅ΏυCkν»ί/)p°°fΩ89Α$΅Gβ¦ΫWό~c?ΊηχύΛ° l4ξΞ“ωϊζ•“Χě[ΆpBJ˜y©'ϊδ‘*ΐΏΠe‡—κΠ2Θ=,O­'-ρD\Qk«I„εΜa™ΚΧΩΦLD«’ΒUP/’.ψDΦΓ•K[txYIG~*ΔK01λ*D 7·ΫmχΎ9`7’£ ƒE7ŽΫΏvΉ}ρc?.`ΕΔ2 b0Vy”ΛΖ/Β«RΐfdD u™MΖηδ@GΆqœΫΡΑιLΊΘΞ_ΆϊΩΒzyΡώνΆΦ~γινΆ½! ΚTŠ΅Š,°ήΥd%q•ξ ΞΖ4*…2υζ©ο'X0ΖΑ›’L£‚&ΛyΪ υωΌύκ_9Ê\½hsΰη€ΝΠ ΒkzΎ•`;!ΎYΩ>qΩfbA‘hV ‘+œΜΐ2~αοZΫΪπα„IϋνΗ'ν}b~χqxΜ–zUpωi]*OΘ\·l}P€£μNή:ΐh^υ+ΞΑ-Ξ"³+( ΆΛ7šζˆPsς¦@ –έ ^`r†β—"°4bΌςˆ™ύBy\φΓCγ]βΛ™ΐM|EοΥYVΚλΧ¨ΛkΧ7cpΣθj£.Χ §N¨¬oΈ„¦ρά7Γgε‹AZ\p»38ΟαSŸtJc&6Χ BρλΑ/ΈμMzΐω­ϊεMυƒ@ZF`ΌγбqείWήY){P,xΘ—T_₯λ'nΧN2ζθόLdm_ gρ»KΩ\ (]ψιSλΒεΚnHo_”G…Ή;Š*γΉDy™ϋ,δ_wDi‹Οοω{Σfž$πιθ*™Qvΐθ$Ζ‰@Π1C˜68$˜Π@zo†υΖψχ½A˜ιμ¦0αμιŒΧІ”ƒΦN g>ψΞ΅φaŒΏΧϋύ¨χDIδo)ΘkΛώ7<ωuκγ[‡G—O―¦ύΥ.ΐНGGαγΧακ?|Λ‹ ―«hΚD‰Ž&”ΙύϊVώ6ΈέGν\’η)ώ”΅―Ɉ.².ρY$ΌΆΈIH’gqqΏπΥϋVΤMΦlgςGΎ¬c,D ¦κ© €@` νF’cEΚ|˞ΎιΆφ†o}κΎ‘ά 9Ιά‹‚ί&¬’’ωόT"ΈΜ`¨¨b4ΜVΐ3γμPqQήN³cˆΧvRu’eΔ­ g·šΥ'0;σφ?v’eΛcx’Όš>g‚:h³XH#1χ=H“Ξ~ΘNWE%3―Π΅_u:2 θtflb½D’crΰ«΅kR%ŸEZηΡ–Zυ΄-ϊώϋΣNbςζ>Ϊγε?9ν£€‡ςΘtw¬7B 6ΛK ­Ξ¨6&,rιG€…h l΄b€ς&T±Π&ζκή=Xk7Ÿ]ΆxΧZϋΰ;Z;j‘o>-œ‘R&΄.–Hσ rϊ$ΎιI Μp₯ΗJυϊfΆΓ»β_Ε㒈αi…sρ H(‰ο/ω•]e¬Πtυ€Ύuo_δ ,žτ*/5ώΩ‚τˆ–AŽaό½/*γΑ·JΏγ ƒCMηKNx·>Αμc“’‰€ΉβΜβƘγΑόŽdΨ ΓC>jGΉΗω;FRŽΥΗMwήΫξϋ¦ο²π εnΘ €ξ³Ώψœι<:ΗUo:&#φ&ΖfB˜y5[u+ oΈU§ρ#BXQGΡy—]€’Χ;.μΘ}ΎtΡώσί:lΟϜj§§€«όΝοŠ”gζηΨQΞ)ΌZΎρ΅ΐR^$ΉλξhΠ Me@Ώ―‘XSOΪ)]N ?³ΧΪΟόΙ­vηΉ «q…Y*«~WώΦ A c„Φ4Ϋ`{POŽαqΒ…g_ψHxΈU_=‰M`2°ήf³υφΫOLΫΟ?ŒΕΦCγ6<Ϊκ«tΜ—waYΫΊ[7‚'BόΡl8 έΓfΛΘάιŸΞD€KΨΰ =ƒ’4WΤN2Π‚²Ϊ ’0\€{­™4·ˆ5–#Ό^’γl—m·ΧΊ½¬ Ά/ΑΉ²Φ j\εU/“•υΆF•I“‡f’ά!h—0šyρDΑ:aΚ#nΤ£ ĈRŸΖ=wς gjH ͺΔa1ψ€wπ€ƒ34$δΧ!嫊>πΧ½Ύwτ|ΨI¦€š²J?5m3ί' έΈ”―ΦP8zΦm]I―†~ιύςZt>π„yc$υζESφ©m +}εΐΎMœ~NT•+’&[OέPυšfΨ@x&Ÿ°*uμiDη€{Γ­ΟρίΗaϋA {ί|ΨNn/›ευ.4]‚ΓσΔτ ‹Ζ?‘Κ·.ΚIΣ cυΥw+—Œ Fͺ•o‡#* ΒΞ\dΦ’΄3YΥUΚ3&-i<Έ«œIΊθQ蓇΄TxL0ΝSt©υΡΡ£β–§Φ |‘¦FδYVž₯ΆžΕΑA{ν;Ύ£έzΟƒ¦ήP<ρΙ_lΧ^xF:€Ό#2;©ή¨~p «Λ¬Q²ώJ*Ι”U&«ςt^T;,ΖΥ™€*‰ΐYΖδ%†Υ:O3 ψ#³ΝΪf Œ³ žƒI±πΪΎ/Χ—τΕ"”Bε€²SΨ³zQΡ¨π†2FωˆYΚΰςL:‡½ΝvΣφΌύόβt»ΔΒΤgƒΘ1 Θ„ΐ Š/[ζN l α\Π—ύ½-ΘL5-‘„poE²4eπτpΌΦWixyΐΧ›^ٝ΄_~d½}τα΅φΩ§10¬d¦(Ξ2 ψ”q₯žβoœ<«mΟ 1•wϊ₯ΰμgγφΦ½aΑά}™RΏ΅L ΗνhP rΑΧ•‰²bάΈΒrD,„ΕЍgμηΉ)Κ‰Ϋ_>Γξc}Œmv€w‡Γ‰œΧ’ε{ŒtŠRΔkέΦλN€Ζ…xαΣο‘'L@²³!m%Ϋn9‹zΤ©;ΫΪϋΣjΗšσJγ]δΕmw‹€qd;”Ο“΄Oϊ8ΈνœŠl/ δ±TΪX;€I GhÈ:rb,‰žΑ˜R§“ςΆ‹κriΔΆΫaςΧ2Πα5τ¬Δι3eΌŸΊ€— €—ΌL"νΉ—ΐΙ‡ϊδ8)²†Γ;λ4Žo?6]ίxΖΌχ0©qΌδFQw1˜Θ8 XΆ\ ’Λr3%‘6˜^<ͺΥ(Nž™N$Ε<‘΅ w¨’²œ{μΣ6_ΌυξϋΦΪ‡Ύω°}Σ먓ŸO Σ[)—ZΦ†—1‘”_²τΥi«1$3ε ^Ζ·α^Ι8rpΠ'ŽΕάΜG^­ώ Tα*PeR[ύΙN+»3‡ Δg΄ηUύ4D”ď—+*m70eΛCuB»Hͺ†ΰϊΉΰ#0O_ˆrΝφ;ωž«8 -4‘š²§PdΞ^]©nιιRΧ"ύFΗ#ΟκSΫ*Δΐސ9&#²d0;Ψk½χ‡Ϊ‰³7Yβ†rπ4-½αάσO|Ά=ϋωOMt],@ F” Υϊ Ά~ξBl“Aψ£CΣ‘„ιύˆŠι °©ΓN4Oβs ¬ΊςSO-ΪΏρσgΪ…“ΐ _€>»Ϋ•Qzœ!;UVΙˆKštJ„ƒZόβ\M Ό6‹*Pi©4ϋMK"ΚO―μEy^œlΆΏτΆEϋwΏσt{Ε­ΗΥ>p^X ύτ“¦1…'ίΫOš?žδN2ϋΠ«ς΅ΒΊjŠ€•6V[?§όμ•υφ˟Ÿ΄_ϊμzϋό3kmZέ ρBVυb§l^Eά±VυPG`ΪkΈb’2OrΕ%?§v˜M=dΒɟο4°}ιϋbλρ>)ΠeΞO@–:sˆƒ*+ j¬-—λΡ? ύε*YNCΦO–1‹Αƈ&qYw― G¬r€ΗφΊς•&Κεe4>²Ζ 7uzƒžΧ¨Yf›…•zt'υ(ψيΆ.荞³F +ϋ©„<ΌάηλΜ»¬γg_& Γ“ŽΧ1Eάf΅m~«@̑–έφ‰­|©Ο―)Bΰ₯βoέΟΝ‚Ταϋ3²‡yθΈ°νK/7(σ’|‰{§«πΌ ЁgησO;% ψH'“-£ΙαW}„.ΰ€g²AέWχc.rWτiΜνΓ(y@d·“ωgcXSTYΒ@€œΈ3L—γ¦sΆ} ΅w'ෟ?lο{ΛzϋώoZ΄ΫΞΡDκρq)KKK°φΟo­_ώΒχ ύŒ ™n(δΩ’WτeΧ,‡ε½ΩοΕ8ξ~₯-ί:uŽ1]Α$ˆ#l&ψ·£ύ²γ©x²*·3#‹–·#ν„/κS[_)΅ςOB₯Q6Χήν+J9VYνT[OΔΒθI@ώΖJ^Δeeδ₯ˍΞ"€D«.ΊTZ Φš₯±|λΜX2Ψe{χ΄Eo8+7˜σQΐG~υ2+GΡΘDΩJν  αζ—νM|gΆ Iΐ²ήΡιT;ΤΞ¨Έ=κΨ)!2±Ί―`\Yγ›GψάΦaϋ[?ΏΦ~αΩΣνΔ”Ž8'κΝυgλ΄th0Αΐxˆ/9*·B)JΣε³°(­©—8PD‘₯O RΪtŸ ΒΌΧf{f­}δΓΣφ†[7YΨ€CiΗΩ…Ι€“–μ¬vΜ« €ρ\ϋJλQβ(Dεγ€:₯ ΥΔOΨ yDόV·Ϊl½³g­·_}d½ύόΓkν‘ghφy¬μΜw²]ΌB¦[ζͺΗBγ@¦ Υϊ–8iυi}·Ο]ΕxΟAξ L₯y bΠ“—f+―d€eη σ-+J%BmI·^p${‰Ι7τΑΒL¬Œη£CΤkίyŸΖJζ¬'' Z8hΣ‹‘τΔM§LΆο]Iƒ2uil™δ΅ο HžπdRWnž#o…ŸΆx'zΚсΟjDνTΨ">Ά,r«U>έN·`MδQ!‘?k £οΉχΛ…se³γ–‘σS`”FlhΆώm½δ‰;π’­bβΰ„Η­wwRΌ6„s¬Ιeΐ§3²κΟ.qϋMΒwΩ›†?ζΥγ„eόcΘ₯“m¦5›@ΛθΣΠΠ"σ(k–} ^ωηΔ#r‹qίήd•okx'ώk‘ΟΙa«w}YˆΓΎK9ށ#?³M7Pυ††Ά¬^ ­ΤHΎ Ρm†Mλιδ[¦Άϋ«-Hqx]xGϋ:N1βψψ'ο˜ψ*“XΕSo?@`q Oπp”yJ~τ£„φpτž™¦¨p§-©„ΤόJ$ΙΐwΛ(’ ]GŠtŒ„S‚HRLO½’οxΥ)ρ„μωθς“’ΊSgbΆ£pΗΖΰλl·fzŸΦΙσ·΄·όYςn4wΓNtόG°•Œ,;«τKI;°e³BΧo4ή…==‘&JΩy%žbόM#O?"~%”ήwε‰O8[μΐ+DΎ’χ‡~βJj# 9Ϋva^7φΎ;ά:#lΩΚ—Bq-QκΓχΝ€ΛνΝΠj{²€ο›Ξ|±JήAnIΡAήϊυύlΕξmL™,ΜΪ―όωΣme“mhZ{ΒEψέ₯ΤΖ$ OτΛD@^VεŽœξ‘’1<…FΫcΈ289πMοΏ|9β%“-”ϊ:“η­΅}q½ύσ‡[ϋΜWΦXθNς"Έδtή πΔ4ω Y΅γʘ‹œZlπ¦ΩχΦ`βico†ΰΈ3yΰΦrΪQΡ–¬6‹°†q†ο ΨΌΤgϋ]!jΌ³Ψq‚eš–Α@Šd©Ep‚`ϋΌ€ό¬VΏ‡—/ό0$Ž4oRkξ»ζ yL6f £Ν)/N"}}Œ5yΩ1άiXζf>Rm ইGΦ•Š,›hڐxέ_.ΧEΓHšˆl§„3„Σ4αΓ#}=ΠΈΪΟvΌ8­ΓDΩάS°γhbZΗγκY…―|Jf'H|¦U˜Γ‰Q&Γ$’¦W N4Α’ ΥΏ\2 ¨dΏβ²`c­ό¦-œΖjY =U_α;aP>\ν»κ}πΦώπ[[ϋŽ7.Ϋω“δCˆίε~ΖχΟIw,\γͺγ―X…GΎ~‘‘όL Δ` Jάζ9&j’P~΅ƒΈ~κΰD‰S.­O’ύ@€v­r:lΟ₯ΎžFΕzHγΤ#ΜpςN^;α–N™bδMτ1iΦ“4ŽZ·ρ3]Ψ$β3 0˜γΦE‹­9hR°&½ Βκfm+°dκ%LSxΥƒρ•Π†Έ²–#Δ–KΠθNzΡiDΘ:ΗΔ§ μοΆϋίωήvΫλnΌλΊzπ죟j/<ρ¨œΖ¨λςŽfΩμέ¬ή ˜m;…Έ¬(lQ„³w*G:ΒΑΓUγνδ S‚0pΒ"σ(έΓΆ9YΆO|eΩώκ/œk·œ;γγδ”ŠCWπYucšY9ώλΧgQΞΉQPϊ¨ΗYπΔη‚D Ψp`qb&Ή<΅…mXkί~λ¬ύW>ӞήΈ£MΫΔ•`M4τu?@k¬ώδΑσΓ ΉS (>uΏ¨GHΘƒŽ•’+€eδ'0 ΓφJΐˆOQΔ^Χθ]Ϊ™΄‡™όΚ#λνSO¬΅ηΨw,ώn£RυG‘Α_k΅nλ“εζ:ΰ|³`z%*…^―m6¬Κ°Ό§POžν6¦RLΕΖΘ€ϊήhΗsyΪ.a4φξΈsyO½½•Α%†tNΏEωΊcΐ„P(9HŸY„€‘LM;tuμe₯ΖΛ—ωβαͺͺς=IΖU#HςŒΊiφ)ΩΎ]ΠΊ#κΐδZΌ<ΓO_fυ[π1’6gΩqf&ΉΆΑ8€ψξy ΰ}ͺA9ΟΝztΠZΖ$Β:ε πλ^’νΗΕ7pΩ%# (όwΧB™&4vΥΛ$Ά―Φxn€ΛuΪdΣ^&΅@4„lΰmwl‡I‘£ΔQ―ΨΏI«2`»5ϊβΏοφΓφΎ7―΅οxpΡξ8'sumί -g»ωU4!1%œju ²ΔQX—ϊJΟ*‚³θ•4Σ\!¨λ7! 7―&…±ŽHŸ<Π£"W}e*r%h&––°r™‰pΉτβΌš]–RN-V\ž8€$…Ÿ“+rŸΠ nRΦοψ›eΕ‘9π*sΑ½_―‡ϋ%>ε¬ϊQ9}N”ΆGτ axΈš`1Ω¨'5ρΟ=–#½ΧlΊΚ750:Wύ%Ύΰa|Z“ƒ9₯‰Όϋ·Ύpψ½Š΄#,’nx?Ÿ7]ωάΕΏτWη"p• ‰H»ςΜ(#ƒτ^ꌫIgM䜘ΆΆΏ˜ωζ»Ϋ·>ΰό­έzΦJΌΌ¦!ΆTNγ?„ g•oRB¦I°|sjRi^‡σl<α#gϊK6ΏLpͺ9RcΝγ(Μ©πΫU°ότ$,2=δWΉ–ηlD?/ΓLŠτδk8YψCcoΏκcι©s$Jy2LDΰ5έ2G‰ΐP’`=ΕUτ‘ΌJVδE ‰HΨ(f-…Λ$SΥε›NXtRcCrΤBBXy­”αK !+-šΘβ(£Nέ½~΅½σ{X;sσmΉέ ?ΨΉτlϋoύμΡKμ¬ή9*d;Ή'@eNZ­θ8μ ΰθ|ΛEVpžμθκΐ£kIδδ„aI:ΎŠΨΈΐ°Όλ¬p~ψ§Ξ·³γNm«£NUγ­’ΣΧθ”1·:“/ΊΕλ7Κ]έ™…’Š²›ϊνOܜ•ΎO(ԁΌ,ˆ:ά9˜Έ3AΪ5πό̟=Υ.œœ΄}χ`uΤ3Ί4|Β—žl‰Σ†—N„ΡP)άyVdƒ@žW—<Ξ9*ά›WAψίιp@Ζ§ΘŊξ(\ΎJΈ&σΩ€=ue­}β±Ζdΐ{Z»x΅ύ«Ε)m‘?¦b’·€εF./Ξt¦¨]Ι«°KΙ;ŒK.Pi€ΒŽΑ7k°œο‘O¦7άΆΠ τέ€'ωΓFj&*œrσ’»B_Γ)7]”q…K>j\η!εΧ»γ½NžΗύ¬ΏΣ˜»ύeFJβ¨ΟΧλ:ΙτΝUΆ%A³ΒW>ΠšUΈ4[Ώύ,λΒ7`υ₯MΜυ³ΆπŒ„…7&Β„ΑΓΨΐΡVΓ¦εš}ΨΕ3ygά/κ?2~ΜΚ‰όš<…½Ε~2“sŸ<ϊ­n.S…χŸΈτΰ©΄ύDΆ8½YnwήάΪ;^ΫΪ{ξo퍬ψΟζιŸZι3,p½ώbh"Œ•‘vηύόG'KγO«0ηΚNXΊͺ=½UΥά*~]α±DXŽoωbTς§ υOώ‘œε…©TŽβKΰε#ω%λΐΚ<SΥfκŒbω½CcΐΥ+'άέͺΏMŽJΑ«œΨkgLίtN”­.% !J½"MνζQ‡E‚Η΄RgθβgΞΐ(jSί™GZ Β0υfΣ۝Rζ›–Λ <σΩΌM6O΄χ|ΰOφœΣΡ†pα†v_όΝ–§d΄ΜΥ`…ι άΌάΒ9~”³ξ@°CHG`νn…&J0M₯ƒμpVσιΟή|»ztΈpeΛΠz3]:Φ<οΔdΩ>ϊ₯ΦώφoŸo7³BPθ²ͺΒhϊμρΌvΆBŸΆ§Ϋ§>ϋŒ^`ΜΙJ½umΈλ-o0Ϋ&Οk–SΑ“ο*N6v˜D ­\ψO™•όμκ'+sΥvΩ¦jTWێ&ύςΐ±ΓΑUνήΈeDΛϋΛߴœψΚ…I >yΟ_¬“ξ_ˎBrBΰΥ’)ώώl­=yy-―#ώΤ“­}Ž Α“ωr‘w[{°ΑΦρY*Ρ ‡‚Lt—8―aΖŽ8‡s©n‘ žΈBΚύΙ‘ψ™ωNΒ/‹ιˆ Wο  ~bδ’COgV ‘=cΰYΊšUfHΛ₯Ϊ9ΐϊ¦έʈ : εΌ™’rγξiH”β΄!γC>αϊt΅š$‚§rK~ηƒ9κx ‘—GNeVΪB>…ςH.ν ¬Gaœ˜RΡ–€sεYΰGδΔδAS\r°qΑgΘ^ˆJψ9²#Dt£Δi§Γχ±HΣθϋœώ着:ω« θ… ­ΕJ`ΰO*€γ0«΄NCΕ{žΎΏŠ€―σ5NZ­“Ύ·βJI9εX’L=Βw„GYTVΊ%+/T’\;YΒV^0κq’/Ή4`mΐUΘDΦIz ތξͺώ©v”n ίLΫR8<+.)NΤq£qΝΧΓKΚgŒzŠ€ΤkΊη΄|ŒI^α΅oυT΄ŒΈy  £’dO?^Ž΄Ϊ΅LΧƒ)O©šd…ξC^ο{ƒ@3ΦΥ‹Ά»s΅½ύ»?ΤnΊγ‘oXχ²˜μ]»ΤΎπk΄­on籊κ;XΦσΓψE€άάΞ1C1½™ι[ΒvVEͺΕgηŽ<…DΕmgŽΛy ΑΉΉη7Ϋφ›Σφ“Ožk§·€U 8ό£pΊρw›ΆF₯ˆηέε*tό(lσ•°Π£ΡŸψΨίIVϊ»΄Υτ~™tΊ’ς½‘Β8ιΨc άΎ=k?υ—oΙkƒkQ‚^Θ+~δsΰbΤ—ΐχ£·3i†“g½Iοα~©ω†/g(Š žfwE£I’m [„!¬_ £ΒγD‹œ0…_n¨xΉΐΥψ|ΎήžΉάΪΓO·Ο<΅Φ}΅/??i»ϋRP+;ηNvK‹ΪΡkΓΥ*Τέ£ŠφΆ… φ_†=/“ .εΛAnΌΰbPΐo<ε)cάzζΰΘΝ«„½†Φ€2ΰγάYΉ{τrιεrSZϊ2Ρδ9p21š'8žiI&"g*»ΣΔΏzάόHev°Lu@ƒ@2 yw‘ςLΊQΚQ,Oš‘ ›)³Κό.H†#2βΠgί)ΓfΏάjνφ³‡νή λν;[{Ηk/,Ϋ‰maκΞύπ©£HŸtTίUΧi3΄j«i#½ό”8ξ'ώλΦ@O—ήΑΌ±σ6Κ'Λ°& &ύ#ΉδPώ Ύ²” ƒαUzΣtβ5> 82@Ε1ϊV$έI‹΄ΚŸ„  c― ŠtΠ lD¨ž¨LM˜ΔͺΝύjΉ>9’GzΒp’J όbπ7Ββb4εw:‡α/hΊΌOχ=›ζ›"‘EΣ¨ΫΥl6k›Ϋ§Ϋ7ξFv/‹ €ξ˟ϊ₯vεY–| -$W‡’t]qγμŒ(5ςλ2€b« )6„8E€ט²|•Φq†+P–Ω-sσIs€nΊlω£'Ϋ£ϋ§ΪΦ&I ‡Š:ΖWΫό¦“OZžAv`&αυ™·53A릑Ω9¨€p3κέή(ΌFΣ«ϋΩ"^œΩjή7ψΰ™YϋΙ=Χ.ο‰ΒA 8 [ΖΏ§ς*oΜpΗn€o;vταΩ‡νας gp―κ2)-#j?ΨC„9pά*lF+·‚κY’MšyWkύμ”'.2!xz½=ςΤz{μ…ΓLvφ))<|Qb»B<^6wVρ„’PΣ¨ΠΎ9 ΄=™8©KvΑd«²Bό*,ήβ<ηΞ;s4ΰ°­ddΐEγ’Gά»~a.—vGϊεAδΤΈ'ύ 3ήέΘ "NYQΩ—ˆŸ΄$-ΦνG•ΜΟ$IΈ^»gΡΘ·TΗ)­ξ‚1h^E+νΈ k8₯§ΐwάOžξxqa©Χ:ε‘§ΑΎ$š©G>»AθΝ›fή^kχά|ΨήpΗz{󝇬τ—y)Οφ¦²NΏήš' „ΏD‹κŠ'8|Q§γz2ηΠψRŸSy9ΗΟ!ˆrΤaΛ‘ξΨJ˜ Lογ+αΰθOX…DίtŽ2ϊ‘άžζŸψJΞFZ•Kέ9ΤOϊβ2'…c,U%+1θΩ°N_—<ŽΠY™ s¬xh_t: "Ω8Ϋ7K²`Ÿ:ƊŠψΆΣ¨>"/—yV+:ϋ XSΜθωFLK HJ©‘0ψ3?:/°…'γ‚pDΚζπ“Œ”“>ϋMΚΉ#ζ΅oώΐŸj§Οί‘n\χ²™ΈŠϊΜ/όΚΧΧΑΐl;JC>‚‰€y/@)h…ι%ΟΈ’ŽLΉ*›› Ώ’ˆ¬γŽy•ž0=†zΘSό&‹yϋSό¦Ά7َ"˜[΅§iζ>CIΆΪkτ7_%λ΅+ί‹ξEΖά\εq‚…ψ½)psšk½)ažoX›<Ώ—χΊϋQ˜«³΅φΞ σφχώψΩvΡΗΌ p]†νpΚ™θ :νχΨF+†Ώ#?Όc|œ‚7qF“Δ‰φϋ‹“–c€WΒΕ}΄―ς8ϋ琭΄}AƒΕ|΄φδ kνΡgΧΪ瞚΄Η/-Ϋ3/¬·+{τ1“'ΛY&eν6·°­―WJSΣχήύ^υ―Όυ>”Έ('B& oY%Ž`ψΩ!^F6ΝιŠΧ§Κξ#'u=Uχ΅_χ]wέ[ΥSO=ΛC“…ςp| @`X”αMΊtF‘ρ›hp|l΅‘«N'nl)„λeULœπ΄»9ρ uΒ~ΚΊ'l–Ž_o²qyΘ_fφVA ½ψšʟςuΝG€eχ«+6τδ¦dΑI›ΟsΪy£'€Œ7πbˏΙ?hdςAY3Ή!SˆΠŽKύ*žπrΌkά\η2MΎαςΘγ·Žd{ΕΠJςΰώ½ρΜ‡>>>ύ‚ΜΎνα=³ή~υ₯ρΥ_ώΩ±³Ρ'EΟ€Žΐίkiπ/hΠΪs‹V4ηΚfš΄«qΑϋ\ŠκI‘šθκp£ΠIυW’½`•H+Β^'ΫΌ}οtό©ŸύΐΈpiΟW˜4P:Λ¦Ι„fΒν(ς£‘s…ξAˆpϊψψC”,ψ4|wήΦ%νήΆώs9]>ό΄Έqίoσΐ•δ­ΣνρΓ<υO=5nϊN9Σ98’€.!|ΠΔ΄\iŽ[ΌΚΐρ/ΘΟΣY€\S'*ίδU‘ιH*Σb¦ιpΚ‹²ά³Mμ+<GYP„%lΪΙ.»0ηCυΔ?λΎyk{|ι­­ρ›Ζo|mgΌzσΤ/,Ί―ΕΤ™N ƌ| ±B ·~’—Ϊ™hH>žœγΉ|Αžό»žγͺΪ?k“Hκ”Υ^9vβ’Ώ«N{κ[κ$ΦJΕP&Σ °MΙΞ©€*QGuΎŽ |f‰±’φ1RbO'%€θœWš·J|l@^‹v}ΫL}–2ω"$πŸέγωλΖG5ΩςΕ1>φάƒρβ“c<}•'χνκ²φ*kg΄ΡŽΰE»N-‰ΞRΤv!-Ib,xΗ9]θθοeƒˆΙ­„ύ“δ>^31QWh(:|Ehϊ$XΕe'ͺbk³-CtY°Ϊ¨lCx\¦5sόΎ{(ΰ€Ψ ֞"•ΝD*†— Ύœ¬Χn—:·™δS·vf—ŸΫ†φΨΓS36 –.ν’μ*Žϋ’dΉbΧ_±§Κ”FΎnΤ5ΎΔς8ΖxF܌W‘Ε7qΣζψ؜OλΛW>Δ<9=G‡‡γΗώψΏ-»χΌ§? Όχφ:)iœN’―ΘF½λΪ;·noυ@ΝNΡG§0(ŸP3vƒ sjgΐƒ€^p²5ΨǍ…ωωtΌtcŒσ}p\» Rh€bΒ'sΎ@c’Cα YŸΫΥȘΐι|ώvεΙ“}~sZ_…ςPΰ±ς½Μ2νdΜ+‚χξε;XΝ<ά#Σ…€’yΨΧL„Νš±ΙΥ Ϊ‹ψSΗcΌuwkΌvkk|ω†―o―έ9_{{ŒΫ’ί9䃓P&|ςΚ³\I'6ΐ±@“=tκLy’ĐIηΗ‹Ady°ͺ4 ₯”Χ TŸΔπ`΅Δ8N5{$ “ρJά†$frGΖ―.r·„ΚίχŒηž|0>t}ΗίΥξΖψΐ΅1žr‹_cw>ιj vlΕΣρwyΐ.ΛθΔ6.3΄e²·ίlγ*¬Œ-΄΅L kηCΫΒFηΫΌΜΜιΨΥ?pS>ΎΈΰ 9*ΘVˆ„+VϊkQœ ,™„Δσˆ—ΝΗo9˜±ˆ¦-ΪNyڏ‚ψŸH)Ό€’P>λλάδ\Ζ™9β‰°™’X―cσέH„RϊάȘCΖ/ΗkΖidθϊ€'>j|Τ>\G’)9qη‰:ΗβX.›’T©β€ΎM«Ρψ—OΠ|Π‘'›ΛΗρaš<c‘'¦ΔΙβtάΉ;ΎοΗj<ύΒGmω^€χά€ εΏϋΧu<Œ8€t†Lτώ €Ι_B?#@#*;όaΊΣ:LV5DžFα«I @^­›;œ}Li4πΌ°νήΨξsO]V‘ΓΡΡ°Qώg* ―v€ΓIΗίθXΤμς&&„θNΗφΫΎ»ΑίΚςΰψή‰2πϊ`~ΰŸ^ήσ»ξή{0ΎσΙΣρΣΪSγ@Ά\ Q.ΠXΰγΥfΪ-š°ΰ¦ΰ‘{QT2wΣLdtΥΌκ©εΔτ’ ίή+n0‰OΡ‚ηψυ©A [AΩ 3εγΘΑ”€D±˜‘lZ8³}#vξhσΔμκΦ‰<>Ω:wΆΗwΖxυ&ΏHγ-ήΈs:njqp0ωz¬:ηN,σ<7h(Gμ‡8Α¦Β“)υαˆ>“DκIσpωΊ!zΪΊΟ‘!Ψ!M-AjϋΙhΆ^ͺŠœ΄v£»^rv4π ηVό©;4n’Σ|X£ ΐh³π9ω·ί½³ρΜΥ1>όΔφxώ©³ρΡ§wΖ‡ž9O^ήO_ΞΥώvβυ-|β8—œΜ)_`ΡL„sωεDέΑt=v˜7N‚yck&~‘cΕB™u±΅,fXΊΌ€δKΊo˜ΦΗΟ}$¬E@ρ΅“§Dα.…hΏ ·dV“š¨ς)Ÿά%­γχ€·ΠC³ƒPΈŒ²%ŒθŒς Υζ‰ΧΎήΥͺ«-x2΅oyIHς1Ω'±‰JŸ@–<ΡKm|© Soˆal‹ψڟ8qWΒΔ/1S½Λέq*kќ‹ΚAΩ*œΗ~|οίψŽοŸόύ4οxο-χί~sόϊΟύοcΒeN…OJ'Ž―ϊ9Κ¦†ΟI£‘A Oڍ-q8ΥLd¦Υ0αKΟ›ύo{^ΜW»8°(`’»ͺAκο½Ί;ώΓ—^Ο\’‘*gίγ§ΣθΚΖƒ‡ςd2 8όή„Χ»\ΘΙWΎΒtnΎλη-~q ³‰φ;Z0$ΜΨΉs8ΆU'/ŒΣݝp_Ρ3;ΗγωWΗ΅K{ώσ wlΕKR˜βω@„9xΚGJΩΠρ‰ΐςtΦκ<*·Ϊι4žπESG½P<γΪΎDΛ–Δe ΡΊ”:Ν•2X `©[““ΚT6Ό"Ÿ•E–A¨)ΈtUHΦ›ΘYπS@&v&:€LfGΗΫγ@ΗvϋξΞΈyπ`Όy{ΛwήΤbα­;[γφα-ΖΈuΑΈ} jτ9d‘@zA θϊ!ο*hΟρΝΗk{ωεΘZz’BG[;u_Pν*`ښ€6jZΖqަόL6aƒ²V=‡y =e’ωΠ^©ώΣκEβΗΈvρΑxκΚφxφͺπ5΅KMμ/β*”QC«}`WmͺΛΪi0uΐUŠλ:e£μΤ} ]*'Rvι‘΅›ScdE›Χα~.}–QΕw–άŠ2¦ „Ηβά™H8ΩΰŸ‰;ν&_%U4Ιm‹·X‹?ΰΖ’†ŽΚέΓ'VbŸG'…΅#¦1hχΏN|ž˜Ώ θŸΜΡνά ΤΘηš²ΏυΥ½ρεCγ | ρΊ`B?Θ;]%V|V—{ώ&8vξ0ΐ:•T“ΛφνC? θί{+_~Ip¦ΩŸ² ΰ+ƒν[‡ώ’WφƒΜ―ε{|8ώΖ½4ΎϋΕ‹C&Š­Lj"GαȐμs̈R N,[6“Ψ¨nŒjB§Sp΄{ULΉ^"λϊ2mΫψLy8 έΩ”T*X^#’ή8Vη€›39Ulά=ΰˆ(TͺJKfΊΣ†„.ΗO ) ‹4Lf΄5°K-w|²=Ž·NΗύƒνqχ`gά;9wDίΊ?Ɲc-ξqϋΎΊZΔžπώƒγΰˆ?PγXτύ£L’,U₯Kcb—CBΪS—#‹ 86ιDƒykβ².x’ηX8ή"μw0hίΟνx~FwQmοΚώφΈvωl\UΏ΄·5^ΨO_;Χ„y|ε䉱³“Ω©ςρbςš&λΘSΆˆ*oOΞ.¦ )―U$νΰ Δ ΚιΆ•εΈKβ΄Tv+Ή}KnYλŒKΒ# ^ρrϋ3qΖe‹Γ<9;ν8”ƒ“Φ˜:Φ‰r}ΐPρVZ1³£ &O)Q‘ΡAν+|fr€ΠPFŠBΔ°¨υ’P4:ʐɟ$e! η £D²Z@{£ΔTJόκDΨFγ,η½ΤΪ•HθΫϊΒ©ρ2°Ž”@¦ςw 5(=@Ψ“6tΗ·ά΅Lx’,}‘°σ‘'9“’Ov%rrκE‰λŠw¬>ό™Ÿϊ3c›ŽςƒχμψΚ/μΈωΥ/¨βχ8₯:A4m7K/…Λm³^ˆιγ3ΝY€ˆR΄v―u’½`!p’ΐYί  1ρ0@ ‹ΆBƒαλ—χΗϊκ‹γ) ˜\}hβ'__Σ³κ–·@iP»C“»xV­Ό€ώΰ‚a*=/Ϊa‚ηΥΑ”π$_ ΘΦ―ƒΥ"ΐςζΈ«Zπ›Έ:>Ν!γΞkwΖύǟψα'Η›χι ΤKΐG¬Ν{ρ9ώA5vZ<EΗН¨wΥ“}/œθ0~§‚tώάφΨΔ^ [Ε n{žŸ’ΩB•s Oͺx“PΪ8ο°I]ΞbWqCvj(ν§a’ΝiƒΧ' H$N›­9€ptŽ_ερD―γaΑwεc‡φG›―‚Ζ/όύΉκ”;ΆΓWq$2~ ‰T.‡Fyϊ 7•όΞνό#^dΒά‚8έρτθύωgP,ΰά ‘GcκΧ4ΕΘ…<ΐ›Zΐ‚­κ1a{όD›C©cνΫΎf"ƒ΄Wdδ`»’ švJΌΕ“‡Ξ8|OδAβq,Hίe΄Ž³ˆ%jlΒM4Ηα„s ͺ‚ά_\VΖΩΈ€φwΫΟω"Œ>&ΚWυΡOϋ“ jθΈ8Aΰ6‡=i™ΘUωΛW§8e±K¦ΠτWlγeΐ&φΪΣοMΨKΊΔ‰€ Λ)±ππ("Ίl1!SΞΆyμ&D$žΟ/ΌΕ™”'²Μωv²ήγ dς³9΄γ(/bZž:¬©žυ‹iφŽΓΥι8>Έ?~ίϊ“γςυ§mω^ƒχτψΒ?ψ;~]πŽ4˜άΡβκΰt½ΣΈMKΓ θFΚΙτ©–[(ΣΥx8Ε>ΡΪωO&ψΣΣ“ιYΏ/9‘X €£Δχ©ύ1ώ·―πՏŒ§u54·x:F μ Ά·yS: ρŠΙC†nؚ½ω:η|pΖχό*°{C—Œ|ΑΟe§xŸνξŒS-ŒˆO+fΑpσ+χƟόΜΕρ—ΘΣγW’δAYάΡ\#w Σζͺc@,h΄mZ IΩžπUΖ\νΟ» ~Ί€=qL;jθΒF ˜€%Z¨ δ¬ mΉos*׏dS ςyž₯‚"2.Ε§$Ρ%TEo˜)C±΄?Žsβh”όΜ2©”Μ%1ci,’σ΄¬mΜ,ŽΛιΓpΎήΜ–lR‰ ^6ƒPu ­Ta|\ lΟ,’c<εiΦ *]’κάφ%3ιP(v«QΐΕj†)p‘ψškzΒ-oZcΌΚΩ·γ­5ά2Ά€ΉιΠΣΦ1ρδ64€/``޳— V"₯ Θίς²`!–$ω|Pvελ·–Z“ΌRGΉC™²εΜΊΊ\’ˆY€‹cZZη΄ς3 ΠΚ`š)zŽ•8Œ}‘τΥ΅Cα?ε+Ά £‘“_ιœΔ?eH^}A‚±yΤJϋx¬§n,§ΈŒIπ© ά©GβΈ¨ΛξέŸώΡ?<žyρ;βτ„χόψŸϋ[γπξΫΊzαœHŸf°šπYh’Τ2ΐ<ΰ―Dc‰…Ξmό"H#©ΖΑIgςΚ―ζg<‘q€FΎ•?‹ρ'wΗψ™7vǟϋβGΗΊšςO)V²Ν7ό\mιNpφ€Ώ7>΄tK‹Ž-ώώτDCΫχŽ%Υbδϊ%Z’ς”ίΝ{Ύΐ­ΏϋύP>ϊœ^Ϋ'ά%Ή©œ{―ίoo팏Ό°;ώηωΪψΐυύqϋ‹”;ΚοMŒYK" όa`CDœNGψ σ*?4w9XTU§σ!“1ΌχŠ]AΑ‡ &€Δ•κνahޱΚ~κΨ\Ba,mβ†a‘1?Ϊ„θAΟ€Σ„]JΘΣη‚Β¦Α:o…Ιί{hΛ’±!ΎU/ ΤAΛΞ|ΣK@ΦΊφ1H`™„₯£LΞΊκβ1οsaΞ₯χπ<²$¬ΗˆHiι<ωXY±Κ―ΈIξŽΫe±qŠQ0Ϋ‡›U‹XŠφ0aYΙΕη\  žΟN±Iώπ Šα€­Kξβ»N;dZςnί} ²Lo‘1@0–΅H‹δBGΨ‹GbΓ§6Ac}eά=:ŏξŽύ‡›Zdπ5 2¦SLŒUS3jΪιD£8‡u@žΰ½Σ Σι2ω ›Žκ؞ΆΥΎ|¨SΙ’Β" uf1žΐφη} T©Oΐ >?a¨GڎdΪs›0~>ί&%c0$ΉDΖ7rΐ2)aMΩ°›pΠ·Ξ–Ξ/εI~ΈŒΚ 2΄+2|„ρmrœ“M(AˆŸDs‰AR8ι’‡0`R‰bΔ/:«G"¨¨.Sκ·λ―λ8Ύ’–ρ#Ε¨ΈΠ˜dUΟΠuΗ%“ϋ\ΛaPρ‚ΠΛ² hK¨Ό>a:Ϊ9φ°’y²ΕWm"m §jG²₯«[VΈ²-¬rΦΈ•ς£šήγ/ΩΆΖ”© fC£}^€―U|‰°wΉ+Λ`n‰£ΐΞ©Ϋ_[ˆ’>œR›Δ-sbXκ8B.6ΪTEPβm¦ΎU‡Q Œ―™ͺ늧Ϊuήγ 6žΤ₯τ9ΐ^8m«@$:ν«οJ`Κ8vtpόοƒγΕοϊ~ΛίΛπΎYΏώχζ8Ί{klοε7σι<4ςρtπιg‚tt₯Εωίi>ίT‹D5/<ωkΐδΟέ€“OώύΠ[j’Ε…·δΡe]ύί<γ_ύεΖέ­kγŽg4Ίδ½½-ώUε·³₯ɍXjΔRσ@ή@kδ.ΐ}‰{rω‚μ˜X·ΖeαΈWί±ρŒΆ'O^τK…ήΊw6>σμιψο~κϊxϊκξΈ₯ΕΉΝΝ‘;”¨"&žŒΩ&[E%'AδME‹Rςό G;υŠΜ;ΌμˆM|ΆIŸx˜wj„7€4PΥmh2m£xE5ΨΞB‘R}8ί*·ϐƗ…s]Ύ$vUbΓΤ η?Νq9C€²=bϋWJ†Θυ1²ŠταJšŽySΦ–<¨γΫΘ€ΡT\I6&Ϊ`Η2ˆfsΕΔδ> I ~ΔΆΩδΧ€tΕχiτ3νΤ}_’iΡ₯`ΪβΨΨΎeΥ―,w !Κ4νΆ›r₯^΅‹a‘Gl„/fΐVB9nΖ¬ζCŒe«²ΕΉ(읿/‡40Ε}†ψ’7ύ%ΗCY“ςα; ‡Εœ³΄τ΅ΖΪ³9.ΐτ$›Π‰2#Ί qξG)GrMlκ2φ ’ΨΞΙΔ–<γGΧq.4PeΌαΰR ‘•§΅»γ°S’Uηf¬βη~}_Lώ€κΤUφΎίψωΏ=οή¬gh9™ιȚιΰΫόΉŽ:PwΡ|ŸΖα™’‘Γ“ €ω«ώ”ΘΟœdaΐŠ2.p“’Œ‘cŸ―€ό3Ώτμψ΅γ§Η՝\埝Q"_ΚSΌhšdΜΧϋ¬^u|"^†*ί)τvιχnϊNh ~ϋώ‘;όΩ>9 ˆ―²οΎvw<Π1ŸSΨ°QŸΨ©n‘™žφΤγŒ—: TCg#ζ¦uo0uˆά€­ud? μ¬nεB'0³p*||΄5ω,dσ3 `6ίMˆ4 R8}š¦ΝΊm€ΛΆΫŠp$’Ad~K‚Hˆ©΄ΕNΌC€•pΌM[A{Jθœp€ΰΩΙW΅π]aa„δ±a]&H9.ΌH»qL>W‘βn"²ζHͺ e<ΛDΉNR§½Π’vͺΫά%΄¬tΉΐVΨv(‹ΦNKœR\/z2~pΎ€·₯ψŽ I©‹B½Γ5Ε1P4κ“‹bŒ]fŸ±x[ -Yg«έΊHπ±a±»Β\ΊΫŽγΓΐΧΚ²ƒ¨xŽΰτE—ΙܐKIΏqޘwܘI_ς€ΚiZΉjηXOΰ Θž>.γΉ0)”γ7υI_’’ΩN†<πχ‰ίχcγωίυήΎνΏ„χέxωώήΈϋ櫚Μχ$›Vψ šDiτζԁxYΑό“Α<μαSΟ$€ΖΰΫΦ,h<~Pt-ϊ«j’.α•&™<ϋΚγšφ£_½6~ϊΦ γι½cωμhηn ‹δΏ+š“BΌέ­y§›ρ}Ώ…*4ν’χQοŸωφώΩε]MςRρo@<ΘKƒςxχx š‡wξYϊτ₯qv‰‡ )οΦΈqx6>vιdό·μερcŸΈ2ήΟ# Sƞ΄6wσV!‹o6Η(š°γ²«“E–y/  γ ωπΒΑ΄(hΩ9Ϋπ“$bρp“<ΠφœMΣ"bƒ΄­¬8 λY΄Α>ΑeΨς@S’2¨3Έc 6¬•1Έμ›–―½αΕ‘·I@]4 eg;I¬Εδ8 `!σqΓW=O }­¬ΙR%³ˆAΥV₯l*‰ΏKUΗe·6Ÿ«HΓ–] …fμlχ{d!\‡‘a©_ͺδφ™β,d7ΞFΙ݁Όƒ8 ·AΟF Τ³υ S’…RuhνΠΰ”!υ—σI~|]Hγ*ΆδΦ€‹ΖΟ ς»)…Δ‡vœ‰Νξγβζˆ†φ±X ύΘJωωbΉs’/q‘a*[«*ΐDWd‹•Ζ߁g‹¨”JΆΡΆB[ŽΗ\υu⦌|˜δ…αmΆ±¨Βu\q^κ‰MLMώ‡γSΰŸOΏψ1Όή7πΎ\―ώκ?o|ρscχΒΕ4 6”Υ:YμliςΧ$™―θ3LΘΥ8θΞτ &}db²Θ€ο‡ύU@ΎΕMηοΆTinΐόyΙΡЏκζέ“ρϝŽμ'žίωό₯ρφ}›ŽΏΛn.}₯ £€ϋx-ͺΨ š:uݚ!AL]ΫΎ |>,CΏYVz}|…νN[6Ο1”ˆπ!{‘E/ι`8ΰb­ΛΑj+9υaeŠ’P—Y›OΨP„e$‘i;4מΘf{η-Ω‚χ§γG=g# tδΡηίJ‚—ν _EλΌLZΏ/²Ι-)ΩD#˜Τ8£,!φ ήι‚Ά=T6%£+¨[^26cσ¦'ϋ,άΪΆνP›9M]κh9NΞmσ΄ιn›ˆΤΈάŽρˆ“­Yς±L|εΔ#ό$6Q‘ΉεŸ΅Β"8ŸœΡy•oάά՜―Υωy² 0ωχB°sηΖΨE\δΙC²:6—ίbψγV’‰Ά]3I q‘\mSΒODίSœ²σx„­ΚζqΐͺŒΫjhΖΘ.π%Ξ±@>] ΗMΰΗ]ί“ρύ?ώ/Ž+O>'αϋ ή· ΰΖW>?^ωμΟωΟƒά±υιΥ'_™kRdάΨΗ"ΐ––6 ,{Bn%Υ"€“?WZpG ί˜†(g“ς‡6›20ΰ<±`|ιΞΦ―|`Ό~v}<±wμ|lΓ,_‹KΔοŒSω).Νύ?œ6^ΔαˆζWgxΒπ_σ’ ΣΛΖο ¨χπŽ­ƒΏ'ΰψΉΛώ!Κγή@!…Ždwχε[γ§ΎχΒψ ?ωτψΞ§χΗ-ξЁ —ςΝ’η „j/΄@b1B6vˆkη‹:η<iΦ”Eξ6A™\.yΚ(TΛE3žα.@m7›Ϋήǐΐ±+ΐ.#eBζ~ΤA,ΓPΗΛσ,–΅}<=FΑ/Σ(š5Ξ‚’Ž©h"x“%μ<T;ΕH,mπΘ=2d—c)ƒό™θ:ΠYix=9φ‹ζ~Ο?σΗΖζχ#¨Ξ«†ή§pφ[γKί‘#₯W€!s’σήn}x€ΉJ bHͺ‘«Ad^§S«aΥ€Ύΰ―˜€iD^(ΰ’FTωΡTi¨ξ?s”HβΧ₯ͺ'Ώzuόo=7žΤ„Μs[Ό9MύΎt? (z‡_ pE/χ9…ά¦13Ήk†Ξ›εs7Οp럫Ύ‡δW~c .Δ„ώΔΏS ώΕ&Cέ;Η―ή·χwΗO~ΟΕρη?sy|Ο Ηέ£γΰ„a€Ž%;ν8ΐ[ &ˆ₯F#»RmηΝπPΣ3Km….j–/0Ίζ©pε(ri“=bμaύ£ψΨ†Ÿ|ΰ#s‘oΨσi9`rN*nl Ψ DLτ9θΙΐΪAО)‡λ™Ί_ΔE63Φ…Jβs?eji₯φ°Mω9€Ε6Rj£CΝ|ƒΙ ΡmVς–Ή?Ύ=νε±!Λ„Ν~αΫ²fIνΎ`&{Ε²Ϋ9]Ά ΥΠΌx&Ιϊj“Ξι«/4‡οΐͺΊMΉ³Ζ”Cq(sΙ\Φ:Ο>&I₯μc„vžΨζmnVΖ ΄ΪΠY₯ΒΏœŒC<£Δαα•ό9>•Z;m sχ‰ς3–]‹¦ψφ5‘|$D·|k""‡scάπoΉ{gδ²ΙΨΔ.*ƒ‚]‰…›2ίηΟήΧ©ŸCνάςΏώά‡ό;χ3Όοι}αε]·΄’ΫΧ‰φ5O°'|7MΣ|ΗΖ‡₯u€šl©"wr&wΒΛ―κŽ λ°₯ρ₯EΉα₯!3 Ϋ?C”tG‹]dϋυνροώΪσγήƒ'Ζυ½ϋn΄.–hžΰξEž0ο ‰E\ή θ΄d\υσώ€Sώ. ΠqaΛΟ·ψιΰŸ pMς§<+ΐŽ„_H·wσ`lέ9·ŸΉ:niψΜυγρgπβψßΊκpΌ²φ˜±±Σ)aˆ‘­haˆ_χYY.΄½M:ZΘ¬_X4ŸMI΄‰₯ωΔk·)²σtμH‚ZήrζΌΡf{ŸίEg}σ*›‹J(˜ΙYYQ7 ΛόhPζΩέ– LLάK Ργ6KνFλ-Xθ&ιBiςOΞ Ξ[°©vyθŸ“ΌlŒ³ΟΆ½'“ΘB³g‚‡\`“e›¦oπ€ >–‚xζΙB‹s΅λnKΎβΤΗ““ |ή„έμ‹&”'oaΣ…Iζ[σ‘frJa,vtΡU`O’Œ#S‚ζΩ3tL Ρ)φž|#³ΉΛ^ œdtρΌ{™εξγ>&Ζ’:VoωΈό‰k²2;»Ηnd`Υ+α¨ Η…DŒ½±v«pN=6Η&}0y{JΘyC;e4Εν|»|κ‡ΖG>ύC–½Ÿα±X4ΌϊkpΌυ₯ώ=ž  ΐDλj™Ε DκdξΈnht55Z_ήÞΙ~šό{!ΰwS»›žw<}¨ncεα%Xy^αύΕΟ]?ύζ³γ MΘϋrάήΙΣx< €1)°σΰ8eTΉωοP&IΈ%ΘδΏ}8 2ΰψNΚΆ₯«Ύ ·`Χ―¦³qΐ¬pkB~;7ΗƒK»γψƒW†Φ γήΫGγϊξιψ㟾0ώτχ]ίσ γHωqW€‡)?Η₯)•WTσ$.Έ³ƒ…“Ϋ©Ÿφ(‹2˜Μ,V’m²ο8‰1wκI§Δ΅d1zDf GFβqCΈνΫniSΪΠ!  ]•pŽ!°lNή‡U•2«šN]”% c«q ΅A»œ€Ιrι]`φœτ‘²MΎ'Ω [%¦Ύξ‰91Β[ΤςΙf‘‹b!CY‰έώ©W$›ίε $η`™x|fθ:BΣοΙ'}ά]‰™CΪKΞy‚ΞχςL◁)“3©Ebβ6vœsΙ|liΕkθ~Ώ &Q6ΑXιΒG%Άyξΰκ?χά_πρΔLή*‡Η>v=Ǎ]xΦϋ(ϋΈA±ΖΦͺ –ύΒνΏ,ΧΎ*ίΨΩF²”;xΚΖnΦΒ< @,‘@&‡%γ΄b1>šγδψXhk|κώδxβ™ΪκύΥΰ΅Α_ωG “½›«Λβάπ€fr’Π΄εŸνh@’xβΧΞ€?-’wΕ%­Ί Š‘χ^ °ΏŸάεnΐήπζΦψχ>ϋδxωτϊΈ¦²μνπK€ϊ}1}pζ― Ή ΅@ςπ’ :Z(/Ω¨€šψιXΪο«ΜWφά‘ύ&AΑΞ]u‚’Ώ΅ο©‹ι? ~Ί½3ξξοŽ{βΎλκΩψγίτ{γΣΟνsƒΑΟ©^N π©j€/²8q9"S“ ίcAZζ†ς³†H ¦›w§aVœCπd'ΜΦόσιSIZ*Λ-΄Ν')n²ΜθέΠ”Žμ‘±…Ί@ΕΟ€`³b–ΌΘ¨‡Ι ΞΛ–ΌQqηδ¦˜ΔWδE§V86Π‘GW·iπ$ a˜hΎY½χγ6­ Aί’;Π4—<=±˜ΠΞωKΟvgG$e·›”…ώ¦Ύˆ›Ξ—5”‹~)»eΩi`œR€r:ǎᲣΜ1₯4VŠ=δθ\)žΑ>”]»dπ‘ ³ε ί…ΐV:›8αλS—^˜χ™”4±MbTώ¨[iy³x¦~KύOώΗy\ΰ±[4Όό ΧΈσΖWό€`&}:·Υ"Ψι˜ Xn+΄ 7OώΒ^xβg»α³π-AΫγšρ Ό ©» ΙξΦΈΌσ`ό/νςεgΖΆ'4ρjΤSYς° ˜ΉCG½£<ιΘ”Ώ}έew–ˆΫύΊΪηh˜Πύp²αΟ„όwo:- ξx ή”‘χW½œπ3Β ;γdw{ά9Ϊ„:žέ9?φΒΆφ­ρ>Έ;ΎλΙ=³ά­γΊcβCΏ ’Œ<€u4o#Ÿan¦mΧi@&eίώ*CΗ2¬ϊά`ηˆήjψšτΦ›žδ’N&Ά˜l6tΒhωτρ'—:'ΦΔ.ΠDΫΒ"\ΒmΡΪK)ΏY.iM«‹†ΜΜδlt5‚pφ™.1KQ¨`i£Ε—<¬ff•3Ά™±™&HD“<™N:ΐ*%SΪ4X;€yΛdQεΧA™4%˜™~BΣΗӞ(•§<™v{rν3Άΐ)σ:λŽF —ϊΝ"Λ±ΐ+†θΨ1ZœŒf₯žšpωϊΨ].š…₯ωΔ1«Cδ8‰Š]¦ΡfRΕI†2YΰΟ1_›σ‚@N©R\/’Ϊ-6Œ Šκ{ ψ#—ήΆεƒaed]灨―ϊ7tΨS~9sG³ c»ά‘PΆŒ’Ÿϋ…nΌΩοƒΫ=NπΨ.€[―½<^ύάΟ«h‚Σ$˜UzͺƒΙ˜’† €!ϊŸoσӐζ ?Οp ˜NCυΊσ€έX Ll²R`ςΰ“EˆvΒΧφ΅ Χ„ύίόϊ…ρίΏϊ΄ζι=-ψY 'Žr`6vΗΙΨVήΉ’PlΚόvxΗ―ςζΥΐώŽŸW «¬@Ώ{œ‰žXLτ^(²έΎβjδώΛMν;·΅8όπW²@PlξHžνŒ#9πΔό,ςšά‡NΗ_ώΜ…qqGzuΗζΐέΑ9+¨aαΤ‰l`"eU™WAMΜ*Il‹’†.3“¨;–νB˜4Δ‰‹ƒbC/XςEL£σφSb˜¨"l ­όͺ¨U6@ZΣ(g;γ *ΏΦ5,lb‘Ί7έjͺ ΰ<ΛH|,•–Κ&FŸlB[0‡qΆ 6σ&O£ΠkΆιΰKTΔ€y uCΜ&eψ™έ ιΫδŸq@nΜ6ȝ$/ώΟ9S…Ny9jIΗ”ΑΊlC Œ:τtΜθ+!ʜKςN§ρ”-Lƞ‰.I8DΨΉδbΰιξžμ]–Ψ:†°§XΔΠξCυ±eΌλ˜ΦΕ!›}όuΙΚb A¦Όm_ρ)/ΑΞ‰ αIžόœ"_ΟΩ89<—Ÿxf|χ?ρ/Œ½ —°xμΰ±^ώWιgƝ7_ρΫ{ςυe?Q“όTMΒPi\jHLώ4,aίΠή‹θ^‘BΣ‹πεj!M21{0Λ»Θ“Ε@ς嫉'φΆΖΝƒγΏϊό…ρΧ_{jlομΛκη< Θχό:`ϋΑ‰'uώιΆ3Κw[εό$XŸΑό•/βΙρν{'.―¦Hόz€― x`_όx8?wί}σΎ‡γψΉ+yΖ`Ÿ_0TδλΏΨHyhApηhw\άΎ3>'v•ίŽκ@ wζt~W)„_‰εf³”’%­μΏ³]U«ΕŒ“~ιΧFU8Ϋ.lB*-YΞbΑD–ΥB΅p˜`C-p¬σBCδKΥT,ωJ*Ϊ*ΊυΏ(·s0ε2λΫ(βIΈ‘{’xg ³mΒδ@f™Μϊ₯IL₯*S₯‹reΈΡ&π“κΡ¦%}²+gΑ· yVH\ƒΕΛ0WΒ”έΦ"8,”€8sξΦΕnQή›68~K«D’ωbΓ΄Ά§}’›,K_ΆC –˜"Ρ‘hψLΨΥeθLΎΥ‰Ώ`ο’οig!π¦ε {γ―Ύz}ΛγΪξ©ζ`ΚU>TLΝΩ*ΧξΨΡβ€²Ήϊ€rŸ)]ƒŸ ςςΩΎ«Ιž«yή ΘŽgxΙΡE-›— έ?{χΗΙ΅ύq¦οHΰmMϊάEΗ’αl[lwΌy΄=ώσ|{όΩΈλ€Έ>πO‹t‘•Ύ %†­:#β!Wj‘vνŠδβ-g“ΙΉΫ”Ζ hιΌ )wΪ’αŸσ,žμžΤ‰-šΨŽμΌTg.T“N"Λ"%ώπδ“Έ|ΨB+•]₯ίΟFΜu_ωŠφΨ[~ŒΑΗG‡γΪ3Ÿό‘?4vχ/Xχ8ƒκ©jhΓ/ύβΈρςηΌΨΡ€ιΪQ[t‡§sˆ΅HͺK»β. pžψσu€:ˆuι8^»—o*΄b3οκ3έΠ•€πμ»βy{ ±§/ς₯'ΖΛ'ϋγɝcΝΟ[~[ΰΙ–&b•ρl쌽G.―¦xuΒν±§Υ/?σσCΎ}/t ς±P8:ΙΛ„.i@ώ§y0εα—”yŸ· P Uprν‚βR/ΖΙΰO˜ψΩ$P~g;γζΙφψχΏονρόΰ%?€₯+ΓIx1 β7X¬D"ͺ«Αƒ)ySwθ,UΊ°1ι:γ¨N– ΦΚς7ω0TL“’Ϋ¬#LnΛB/ ³YB›h_ nΤRκw‘6Aϋ>[Ή΄D“¬ŒΞ۝‡ΦƒΆύF~οΛP†EŒ‰T!»žΟ&m©γ,΄˜Λ<³mv>ΨC°ιΨ-Χ4ρ΅c‘+NBΪJ >1Τ“>PΉ@bͺ€9―επ ш£€bάO[ξ(b|ΧQς.#3ANe–elΓ„(Ž’”=BΖ-Œd«1Μνρ ‰Τ‰Šζ@ιΗ$n΅ΘΠ)γ\}[Sϋ…ΰMΆ@Ÿ’ =ΰ=―ΎΌ;Ζρ?ΖƒρΧΎΌ?ώφ[—ΗρφήΈΜ ƒTζg{cϋΎ;₯‹OαUθ|―cQ ?;ΐ³\]«Sω•ΒZ pΥΏΝkƒ·/¨D§ώC"~NΘƒ„ϋ_Ήε ΰŠο$π$Β©όΥq‡­qpΊ3n_όc§γCOμωΧ*½? Š–ztR F+Π" *Χ!εΖOΥ`ΘƒœΘΒ7Ll*α!˜υΪ'%’Ί Ϊ²› εΑdΈ8ξŽyκΘE„ΐ²1ΠΚd‹›Δ νφt.›e°Μυo’πΉ f•τLœ3™ƒ6±4 l˜ n²Ψu—γν)aŠ’¦²t"ή‚ήΐο³CΗo—δΫχΝ’λ―跜/·OV3··γΠ_ΛΣΎζΏaa6€ΙΜ€Κ)θ3ŸγŒŽ€(CμK¦|]Nσ₯u,•¦bK+Yϊ.ε)žώ%96τ9Η€—Y_¬χ1&lΥ“σLdσDΩ;8;‡ωΝ6‘΅mΧ­0c‰β{‘@Ά( θT^ΗΤX|ͺ‹]τΌπ‰ούή)›ΦΐׁϋoΏ9Ύφk??N½=5*Ν6j°nŒήEϋ _4›:…―ό…ωΐΌtΉ3@ƒdΒ‡§aF+•„¦5‹ω+™ΤB£hΊυϋΊ’Ώ’ύHρ|eŒΏωϊ…ρwίΊ0ξn폋κ‘ϋšύ₯φ/Ά•'o δρΞv4Ι«ƒp…ο;δxκ8Ήͺ«y'’΅βκ~oλΐwψ;β ―ήΫwŽΗαǞG.{QαW©~ξοŽ{Η§γ‡Ÿ½?ώρ#/^’,•ŠbxΰΣΠ”λU›icKdaμf?ͺ—mENfUg"j\3`ΘΒa);‰x*Ÿ Ÿ₯α2>PόςXίΊLεT©σ%FxG3y.X[ΖΜcλΧ‡ŽΫ Ύ|gρ9½‘eTάBθ:>·χ‡ φ5L"Γ‚ΘS‚6› λρ!ΕyˆQ·ΘHšžϋωςόYο1@4}IνKhšmd(‰ν%†QΜa‚φ#–Ν‹Θ׏sΫθΣe"α:uo7Κh% Y•%Υgƒ BΉ=/ΪWώdΚFδΕ‚m ηxhΡ£L~α Bαbh©εU>FoOΩΪ$–*Žb3ΑƒKΗxͺΠUΗ8εNθΡαxκƒŸψ}Τz»`]Ό ΈύΪΛγ/ό‚ΩήΞKrhd4B0M0}DMQ Χ–Π“ώ©€ΏŸ§a‚ν‘E!ϊ'&_Π ημEΆΉsWΰ‚φKυ«>ξ όΧχΗΟΌΉ=^Ί§ΥςήώΨW;ZπΞΖI†γ-π΅P8:Θ±υ―Dr—ΰθεqrΖW"ΗcoΚC - φ_Ύ5Ž΅PΈχά΅qοδΒ8V¦W·ŽΖΏτΡγ/ύπΞxZΉsʝ€δCΉ}L€κΘΝqτMPԏPΛ†₯`ς)ӎ6΅h lΣω —8ΠN³σ¦Œ R5ρ{\ψ,VxXΏ Έρε_υ›ihΫ;υ³AΊ m’ZT’Ρ`k!€ŒΙT>ηο€ρqΓ…LsV\†5ha†&η£Ζο«Yhuž‰ίΉΆ ‹ύένqQ<@xγpŒ_Ύω`όΒ[[γίήŸ½·;ΎvηllρtΎΏβΨΏμίχοhΖζ =N5‘Ÿμξh] +}8'γl/œœŒœŽO~τΒψ=ΟοŒ}~kόώη/Œε{ηD~)YΚHYΟi#Ath}RHΛN»λ₯κ'W:±2nχEŠ‹ΙΧ sN™hςοό<˜Ν£ΎMΒ ηωΔί xDVπpΎίͺ’œ‡w.ΩyΝ|ΎΠUO’ΈΙsιόMžπ7σO Π5mž’`ŒœF3 ‚θӞ3‘QΒ”εημΏΨ”xϊ$±$Uή’³'ΎΙt΅Ο.ΡTf%)Zά2/@gΆd6ιΊh»ΨΈ¦f3وOΑ‹ŸΆ…n#/^ΌrbήΆ²τθ*Žv ή·τ3”š2'8κCγξρραΈxε ?έδσ±~…―λΰήzω³γΖ—>η†Έ½Γ zΤύ ·NOόω €E ΄ΤΒΐΝW4<Ά|—θFμΉ₯h†Ž"Ϊ«vΟLψˆ$λ]»?–Χ‚Wλbο?’έž˜=MΜ,.π° τG*ΟχŒWορ•ΫΖλχŒ{ʏwώά9x0ŽœλW5•_ΏΆ3.νρδ…νρΡ«cΌxyk<ug\ήίφBζXΗΟ_ Ÿπ’Βω/Λ‹ΥDE†A#~€«ΑԜΊ¦P¨ΫH‹kΣ Ώ¨g?»΄EΡ <θpπ5X= *ά„gxXςξΜΞϋ§οPŒw ηύ;—–Γ/iΰQy.uηKΨ΄x(–H]η(Α΄›GΑyρ;˜–₯ρτ£ žE282αΚίai‹<ǚσ/aΩΉqˆC₯#­οΗ[½„δ.\a<SB]²YŽ₯™ςR©Eη(l%žφ ŽΓ‹,C$Μ]Ύθ’@•$z€Έ­Eœ§PΪ*η:ζ₯μΈΙœͺlŒƒ9Ξπ"°°­‘|ςέ?<žωΠΗj…wλΰ·ά Θ ,»»i€J<©kŸ¨ DΌΤv?θ_˜F&·i1ξXι< !ΔM@εΙ¦<$ˆ>ίyK"~ώš ΅ϋ'†²σ«mΎcF–₯?ΚΖεr9΅λPΖ©?ί€M©£:²‹@Α#ξrPV‹ΰC(©ο­υNM*ιΒF»³Mζ’Α’N m‰Ώ!𝠄Όι†σ²ψ&Κ;ωuόX…Ub%ͺη₯΅ŽΊέ CΑyώ!X:ΓΕ!gΫω¨gEλΙjΆLY$γόΗlΞΧ΄’n+“Ό!LLNDΜΣΖΒnEή(GTρO›Yδ+}(εUΩΫΘ‹q^ψβdCEH!ΩA&/’(¦r˜O—]€e ΪΎVFL]q\S ‰βΛX€BŽ#ΒΨς©­τPΡωΑ²η|Ωh‡2ψ!?Ι<^Šπ/­NŽΖώΕΛγßϊύγ™ΒρVψΝΑΊψ&ΐ[/ΞwΞNNύΥŽί  ͺΝΧΉϊχ@ 8RνΨi§Ρ°%σΰ%Μ'-€Sc(O’;Φϊ0ɊɀΛO[ΟDίtlfί’αŸΰ•§6:rΡέRX‘UΈxwδΔ ž έEΡ…# n»""ͺ$(u=SΘ$υ6Χcƒu³Ήυψ4id•’Ξ/Θΰςٝ€cΩ·Ι’Ϊf©_Βy[Žγl–rζ€^Θ4,cp&I`9H‹Ε[ή|©-,υWΤBiΆ ³ΟΑ£Δο`:Cε5e6;p~yͺ%Eˆ]kc)iζ'I)›ψ―βΪ04ΆΣιaZͺw:œ%`R59§9V, "lΫΔVΫ%K\j3“=z}JΰΕ‚ιΔJ»—Η”o IΙ„¨ν—L£sŒX%ΆUΨ S<’ιͺά:TΉ θ~—°θRΆ,ΒI4J#χ«κ•NΈ€α2b;OόΗGΎΥ!MόλOϊ~k°.Ύ‰πφ+_oΌτKγψΰΎ.©λ­zͺ^5eOτω:@‹?ΘF™Θc{φt$ωΡLΧi‚_ܠߐψΣ:^lο˜/μ@τώ,|Ό‰v6…†,wζΦWΉ“Ρ‚$7ΆF Z₯"1δ†Ή”ζΪΆι°BM§s‰Κ¨Λ%€΅˜Νb–‚φ˜(«HΔs>ZξίR*”EˆfM”[ησŽ²†σ²wkχ›₯/mmQ—V2OτAyΐNΓ Ν’’ύ ΟΛ°mŸ…ψ‘πτΑ2θ’ζΌζjΏ‘ΟΨΤeΜ()>ίR…ι6g3" ‰pΩ)Π6‘‚› €ί*·O~?lgZŸ> Iεί¦l΅zΩhΣ™΄M„ΣΈQ¨›UBέ`R‘š±ŸMeΠ1¦‰χΧΠKo;9™Εן’CƒO~"Ό%ΊAς,]jcŒŸ§ΊΈbβΏςΤsΊβAW ΏuXίΈσΦ+γίψ₯qχζšd™δΆΥ„UΝjΜύL@hξ°«‘3˜N„>݌7uΦθη ’­:G9qd§Δ―Ζ&y~ θΖ½ θ|ΨH,7`ιπ}+Υ6 J&PeΪ HΗ˜K™ kM]aΆβEˆum] Ί 4“VΈώ UΥfd$6 ί0ΗΓR4ηe’?„Κufφ=‡—πneη=“sCΫŸΟ›Δ_Ζ^ζΧΊΉΫξ›Z–σ6θ\v_‘jΊk΅4ΨFtcτωώΌθ…]‘%WbΊ₯iεΆΐέ¦$)Ÿnƒ.^•‘0Ά¬x%Ά s—φγSaΎdcΜ°‹m»Ό¦#$-LέT~Ξ.²O‰Y3^Οaμγ([Η“»–ˆ‘²A›T2½ι$Ή#ˆ0 ΆqdωL}Εy€ΡΕ;ώ’χμτd<ωΑŽςΗ₯'ž²ω ίXίB8ΌwkΌώω_7Ώφ%7歝aΎΏΪœψy‚π/hόξ‰ξξξή`b˜T’NζΙ[2 ½₯Ce ‚Ι_Ÿι?΄{ΧΓyp%‘ΩŽ’„/Jtδ”+cGxΫΐƒΩ UώJ=˜XŽ}Œ&Œ…6Ύm Ϋgiςœ/)ςŠ*ωα`™ιΧl•έ”Q€3x@:'›#•|B y—­e­,ΘίΑθͺ,>ΏΆ•la η? ιάVλμŠ9ίF&ws‹tq-έ5 Ψ՟c ŠτΠ ­«ύ&3K-‘mB’Ff’Φ₯`'‘ε‘Ή,θC¨%ΛΨB–±ο λ”BΫ>JƒH‘O^ϊδΑHbΝΗο8e„ ·ω™ψyΫsϋδxα»~οΨέΫΓh…o2¬ €ί Š_ι—Η_όμ8>Ό―‰˜·ε₯±ηaAlφ _IΞN°ν!O«Σ™°!‘C1F¦i΄ξˆΪnyγ²iJ#ι; 6q^OΞΔ`‰v‰\XΑ¦ViK€Hπ` ;/σxŠρ݁rΐ„F%&ή‘Z‘|βKYΐKΪ£ΜΠΪ©σ%˜_ΊΜ"βΔ'©ψ…]Yœ“ :^αώf–y.αQ²†―W°oZaρς;_œiΨͺςpͺ#θ‰₯[W“^1e_©P·RΰrNΕ FVΊδQβΦ- NΔ’,nΗπΞΑ@ }ΨFHJ·°Ÿ&y‰_Η" *ˆ_I)Άπl“žςFg:κ6A€V½΄Ύ²Μ݊ψ˜eΡ"’kΓ `₯$Κ³Q'γτψd\Ίφδxώγί7žύΘwΖn…o¬ €ίfΈύΖ+γυ/ώŠπWΣ‘xe―δ~.€_ΞΣ™θ@ΒyΚ^Zw¨t^Ÿ-tBtL1ή<ΨTΏ‚π”n™v_ωχďx¦§…A}dH€B–65ΣٜππU… *_/*œΈ|΅ `ώ*!c‰Ž°τ„0Γf&Ή›δNF9Α–QPσ€Aε±DJk›ψwUpŸZψEhΈβΞ©]τ2‡¨$aγslQYΑF=Ϋ’•² ©MiΰQ²GB2ywπ> οΆ,ΫΈέŸ/Ÿ±#βBΩηΓH}₯3΄IΥςD§MYjYWΎEw‘O‰ψΚ’’ΐhG₯²υWΆ2²›Η‡ƒ6 »|8Τ "r|KΈ!2—<„ݜˆΩmΗ!Δ8ΦΒ¦ΨψSΏ`Bc[4]ΡΔΒ»ΆπΛrβiMΥΙT&@ϊΌ6ύDΜΦxϊ…οπΔy½ΝΫλΰwx«ΰ_ϊάxγ‹ŸGχξ2«sπ*]:‹ί` €—؞<FSލϊθ€ΐΎmΛ―’Χy«όώίμ§δ‘ίώ Ym‘“`8cγϊΐ±‹‰t\ΊϊΤxξ;>5žϋθ' °Βo3¬ €oΈ{γυρϊ?;nΌςΕqvΚποψͺ1ŸΏHGοY²§Ί™°RuΊLά™@ιˆπt~}»eσnδ‹mqiΏw’αΒwΪfΒJ)½ΉP‘f@(ήGC9DΔ,=Ÿ;VE‰MΩm: ƝΏ·Ν¨ˆΔ lƌ_ 6Ρ»ƒ9μL ϋκ°"u·σ±•MΖύ… N*Ψ€ŸXlς‘΄κXΙΒ qV”ŸP‰ΥΌRη~Λ\K'ƒ9ΠzηkΜi(AΛ 9ΐΠ"ηI0Πα±3b>Κ”§,§ΈŽ°°7Q ―%_κ)^AΫlδ³Π»έ f 2%8N¨΄υηa³FΙζσ=eιL›Ÿ„%M8‰u΄'cΖ‰ͺχŽΈΰWg ΦΊτ$½‘ŽΟ.Θ19αΌΫJ‰Ϋi‡΅½ ¨™.΅?oΰΌ&!FžπœœŒ Ζ3/~ηxVΕΛΧ’_αwΦΐ·άxε₯ρϊ~eΌύζ«κ3gš¨y― ŽΕ" :›;§/άι$s/Ž:’&tθyΒG±Ν Ρ Z x/^dΉ,ΐΔ°:'ΔiΈdmΓ  cd©ˆ‰Κ˜j CSΝΪ₯œŽC»E>Qχ"F’ oΦx4βΰ$οŸΌ¨ι8Φ[χŽ nPΆΏŒf΅vηV‚–]XΖ·sα.v‹« 9ΗΒlQ ¬žhšJy[ζ3Ρ2ρ΄'ΞA™[ΓΗvζ# ΜΗ?ε3If°ΞqgpŒ#°g‹†)°8–l‚Yn2c*[QC‘A€ΔΒpƒ›F&&eΘΩ^ΰΌΐΕBP§%pκ0υ2"hMάΑγj;Ε$ ½1£/lg™κ₯d9D&žΤJͺ(Μ(’I₯ψγkYβδ–>ͺ*|Εoxο‘N|ΣKl C’όŸ&~ϊδυη?’+ύOωψWψφ€uπm §§ΗγΝ/ύϊxν₯Οϊη„Μσ<Kο’+ΊsωΜ…gΘ¬Tƒ§Φ}±&xσLζυž…^ϊdU`_UcNβ-|&ώ’?xCňe‡oyίτΨΞΰΕd λcjqPΫPεQ&Ν;ΥΖoˆ3½…Oe%L½ΑΪvZ”ΘTΎ&[·Ys†ΊΝN²"KIΪΛΒY9σ))°Xϊ ΜY&Λ2ξ|¨Ι¨L'c‘¨H΅γŸ¨eΞΗκ`βbƒ6fΝLuX†3)Hώ3υ'TCq 9ga.›εΗ„ΫςgŒP:OςΒΗΌ ₯›°ΜδοάήΧ•>―>υA?ΙΤ λ {Ύa]Όΰδθ`ΌΑbΰ‹Ÿw΄ΆψυΈω{΅¬Πω”Fηώ„IφΫ¦—{žΟp‰΄―φK–;₯ΗΚjfI‹H1/b {°‰έΘ&μBNζ6–¨υD9ΈbΫΨBs]™δ-Ά\D³|ƒδŠHΓFΙ Mζ’Q‹ΩzΏ;ΩΦ€Ω|aœˆ1•ݍqCgŒ6oy’)_φβ[Jβ6IͺΐF0ΪΕgˆ¨P=€.λ­eσ1J/8Ί–C7ΪΉNβHνk ~I ―ͺO›o{•Δy4Hζ MS+₯'‘οTΩ8¦1•JͺL'ϋ‰˜|6dΪs…ΫžUJdˆAI{1ΨΠηGχ‚"Œdδz0ΥηͺΚ^ZKΨΝcZ·‘ێ]ΆUΆH‹¨vα,ΰQε΅zΆι"ΫΟ™!ŒΤε aη]vφΡ>Ώω°ŸΕ’³ΧΟυΈΨΨ½pΙ³ΟUώϊ ίϋΦΐϋξίΎ9^ωσγ ν·oΎαŽΜ-ξτ$ξB»h{CZ,Ψ|F\όΒ&X £Ε½ωΊc`VHs%o~ΦYk‘ύ*€|2ŒX' £Hλ¦V-Β†JJg°mλ Π—&ib&NfϋHˆ…m0˜ΒX[|Ρ ­pΎ-θ!Ÿ¨lrφ:FΫ-βM°΄Ϊ{#Κ;Ί01ΘN†›6ε-ρBY²ΤΧμh™θΤΖΒ^0K³ΞωΖ«dαL{k]£Θρ™£=|’΅ϋηP:ί² ™ΩW"–«˜₯ƒmVNΥ™Ϊv>NŌ‹β•‹,$m˜2fLbE™˜nΎ!KοόEγߚΚ:o&Ν€OŸΏtύMϊO~πcγΒϊ“½χ¬ €ΗήϊκKΎ3πζWΏ0οή•δΑΨήΝέ?¨γEG%€ΗCc„™ΜlV_pιmΠrF*R[”>t [s^ˆ.¦$ Ώ°I㝼„–‚Ιla#²cxŒυθΉ°X聦»ΌkdηΚd(]ΘĊΑ|C\% h°d‚δ“4έΜ- D“‰+0O=yΜήP•ί$ŒΜΉšTB;Ϊ2,dΐ€Θ‰Ϊ@ ­ΘŽ5 QΞ0dPκ½ ζI± ΗΡ‚”Θξ|5fΩ:¦Ϋ¬ O–.N±£8ΡΛzלil[μTž ΣΔ„ˆMj]©ŸΗ@η&SΛ°ΰ}> v/‡m ‘gφ°‘Μ „ ΠάTͺΚΗΕfΒ?Νm}Τ{/'žϋφλψ°ΝWxΒΊx #xλ•/7΅ xλ•/Žγ£ ^μvvΗΞφŽΝhž{3z€SRCPθ’yX΅MΡ€Aζ—WΙΦ/}ψ S~Ά‹‘“ΔkΆ¨l‘ Ϊm‘ΘW13°ΟΆM>$cΐ/šCo-±;Bδ΄ΕX_εΫp€ ctηδρ1³Ήu“¨πvEσdΤπ0Ώ, (£…¦έz¦ΣΎι{ސM‰g(k‘.φlRTΙ:vp MW&¬)έ€™¨Y'ˆΚ>”ά6*H―pΪ}|ΡEŽ Φ! Ό‰«ΫνX&„Κƒ‘\„°'T¬‘i›<Ψ*–w›‹ΧδοbΗ‡ηwΘ¨~ΏγΌό20•±‚„R*:j|ΝNν&R“ςγa>ώŒŒ'φΉΪίΩ»0<υΌ'}&ό½ —l»Βγλ`…qpχv_}iάόΪ—΅ 8Τψ±5v|‡ ?7τDμΩŒ¬Η™Π$ύa<ςځ¬uΆB—₯uA“$ >YΞzΘσ6ΑΪ<"C3d3,.,!ΪfB3ΙB,LΔ”Μ]E4¬E³UYL’Ž;©R‡ξ1Όt›P–ι–‚*Ψ¨t¦+0&KπTv) XuΘ6}›λω-° DΞΉ–δΐ͐ͺnDN60i ΙΎσά<Αbcθ:€t]h…­―pό€œΥδŠά)D₯_ŸΎΊO–=OΔ‹w61‡·ΛŸ ]: ‰DΎ₯Σ'q$ΥAXΞ&α\±eΉm 6Ιΐιͺί0η<΅;8ωΰwφ OλCooοΛO>λ Ϊ³/Ž‹Wž°ύ '¬ €‚ƒ{·ΗW^o½ϊ₯qγk_Ηχ$Υΰ±³γ§MΉšbLΜ€ #/%z“Ρ6Α’ @ΖϜύ"·,Β"bZΝΆν`£ξD ²τYΛ>&Λ8ν‘4„a–A1˜ΩvŽΓyΜΚH¨u!΄{°i("¨ψ38Η6μΎœw 4•Ι"–[²‰7Ά• γ1±„j»Š[!ΫΣ“CN± ŸyRΨσSA&»Ήφ#μΤ>γ—vΐ>•#Ησ&&ηfΫϝΏγ―3Tv|h>l8α—IyΆuΠ^DztRˆζƒ½c•ohΤh+„Η#$ΗαπΠIδφηηyZΉΐομξλ Ήqυ™ύS½‹WΫv…€u°Β7„“γ£q󡯌―~ΩwξέzK ―χάΙ’@xk‡i‘+Χ,AΝz34;ψβx¦M@‘LΈ(ΓL m’„]*ΗaΌdδΦΘκ<¬4d{tβM—I§›ˆ(“ ι Ι¦Ι@ό"g OlΚηωο†(5€―›#3=ώ—?β±sεIiϋη~άΎ§w!/Cλό}ϊ£={―θ Ήqν™ΖMψλ-ύΎ¬ €ώ±ΰ΍7ΖΫoΌβΑ­7Ώζ»όi_μhQΰ—0ΨΦχv5v:ΡθŒ#˜ρ’^ΐ†ΝyH~ΣδPC©χ.‹·hC&Σ ψŽ=Η=I„p›PΠͺι€ζL`R“ΊΑβV›±s, j=BσΡ7Ό£Ÿ”Ψ>ρ ’­iΓOι9`rKŠu$)a‡7šΕS6"TΈ„5T9΅©8‘EIl&F’mf ͺ€σy‚˜Μ„pυwσžQπέΉΟ©Wnο^\0 #›m©ΙW‰ jc‡Ze1-C`ΣρL™δ ¬,8bΐwχr;{{γO+O~ΐ“ύελΟ(―|ρΆΒ οΦΐ ίΰέί·ίzM‹WΖ­7^wήz}œjΌβΚ„ YlϋJ†Α«ی–‘=8ϊƒ 0|Ε Š―mαZV‘=œ"Νΐ :lβΪΤ¦DFk[K€¦ρε­$ ŠQ‹θ±Ψ bN ‘Κψ†Π&Tβ—„ Ι7„wg΅€επQY"κl»LOJ&(;‹‹άι§ςΛΪΎT (·˜laΆgCΧJ!‘Y`Ύ„Α$₯Λp(~:α.{Mπ`OΜΰ6Mθό|€U$Π6NΎ˜I=„)·οeDίa²gRίήέ―^χΥύε'ž—Ÿznμν_Δq…ώ±a]¬π-~K|W‹‚Ϋ7_·ίόšΤθθΰŸ@Ξ3Ό—`G˜Α™ΡVΠXCaμ\Ρτΰ@W$-υc ΩΥtτN+ ZΘΦ°ΙwΊPpώˆΟΔ"‘'μ)#—e.rψ@„%ξ$0Ωΰ¦)Ί³l0[64gΣ‡Υο Ω–ΐΔ,3XΨ7›Isb6νρS• <rΊ%Γ΄«#K€2.a§fυΆΡ–Δ"ΩΈ“#pήuΑœQ°β-&|όZܞπ%φ•Ω0YΫΕφεΗ:~qφTˆ΄ egy8―Τΐ”ον/^{JWχΟθκώ9_ε―·ςWψVΐΊXα·xIΡέ·ίwnΌ6ξά|sάΏυΦ8=>Φ ¨…ο#ΰ™ „ΌΈΜΠΊhΟƒυx=p{HΒχw¬φDVˆdb<˜€T(Μ6šυNa«L%GγΨ€΅5κYƒ΄ς-YhΔ± ”έ€lIΌOΚGA‡ψFπυμ–α;»eΆE»4ŽQJί₯ ”^`ͺΈb 5.±₯’aYϐ“Dd3ίτœ#qGy¨sΪΛ£ζ5ΑS Oό·ψu΅ž ωLϊΨM /°·δςΞγω ǏΈLτυ=?·ε;ϋ‹WŸ—?;.igβίΡΥώ +όvΐΊXαΫΈbapο֍qχΖλγžh|ΰ–1Σ‹ϊ, όk„j½^„π Ν8^ ’|…0„0n+1μͺ€b·if μΈ)±J x§V˜Y8·Έμ”Τ4WPΤ,IR1šlˆi94’A/<–Ά@ΛΚt‚Θ%m8―H„O¬Κφ8ξCς"JEjάϊ/c9Λ¦Xμ:.’ΤM)KηΊ΅r -¨šΔ’?½ΞχšΈ™ί©T^”ƒH?pG#‘Mέΐ―φž[φ觉^°΅³£+ψΛcς5_Υ_ΈόDκ/^Ά~…~§`]¬πmwoƒ;·΄8xΣ „ƒ;oCΙό€Ϊ‡½0`Π‹ƒΕΠR˜H.bΊSα$ΛOXδ2‘ΑήΙ²π°Ct•&Vl¦ )᜴…1Š€τ >ιΘ1Ν&"a:@“ ƒ% ~”yAΌΪ·5β9τͺ'Xςμc²UΙ&0Y’/e@Ά‘ x°„-7nΫJ³ΠYΘB.bωŸL΅μI[Fu&λpbΩ1MσμŠόω §ί€ηΙ_»dLϊ=Ι{UΫΫήD―I~οeMπώύKW}uΏ»~WΏΒ·)¬ €ή³ΐδΟB€άΎ1ξkaptοŽριι±h_ρ¬>Μβΐ“ͺqβd’Ν€ΒŒ`deΠC4 ΨW4wθ(+†Άx+΅I”Ιv­j”σvάΚ_‚‰#od•ο${ι3ΩΛfΙ& ¦b5“9L1*ή”]69n "fοςLˆγHIΜ΅½Λ?Ϋ†6Q’’fΉσ• Ϊe¨2Bb»MΑ%X~Οο\arΗ~"Xΐ²›Ϊ8h&tYκͺΪ L΅!¬±βm™{―ϊqφ/_Ύ<.\yΒWσ΄³Vx/ΑΊXα} Η‡^ ή·΄@Έϋφ89:wnŽΣγ# τ\Ιi€Χΰξɝق;ϊxΑ°Π=‘`3Ν@Lvη&6ι’! eλ9Ρήzφe<ψ$FPqLB—½AšlRΐeœPVe3Oπˆ(˜ !>Qš‡t9")Β€ΤuΉπ™τΫ"Ρ-ύL'¦Σ ˜™\―±7UyMV"|΅τύ{νώ=ΏΙπΩρη\λΌοξέ‹WTΤ]Α?‘ ώΚΨΩΏ «ϊk~8o…ήO°.VxlαττdίΏ3οέ§'Gγώ­›’σΥ_;œψ.“Ετ —'}Md|εΰI'xžΠzRj]w―HAσdΫΆˆΙt!Ÿ€X£Yoεκ$³IΧ“œνLFZ vKEOώ³¨‰PΣ±š/Φ’bB‹°u\ښ~$Τ-ϊm•ΐƒ*0φG΄ΰ˜“(UΆ#_MξΘwvφ5ΉλJ]“{β/kΒGΖ$I‹ΎEYa…ΗΦΐ +|ΰg‹GχΖqύ„‘?Sβ«ξ,œϋnΒ‘όΏιZ00a η7άΜv™8‹WΪIσ‘DΞ1θͺKύR›Ή2jbENŸe“ ¨`Ξ[K† …¦bυΙΜ\΄δ|οι7_n0ωΚ’γ,γΞ ‰ά GΣ6ΝςΛe«‹ΚΌ½³—Ι{gwšΨw˜ΨωN~οβΨ•Ž?ΌYa…†u°Β ί89ΊοŸ6žœŽ“ΓόΉΏh8ΊwΫ ƒΣγρΗ™$ΥYTpΒ &Δ~‚\ϊ³d0lžωHΝ[‚‘6ώc[p]‚γί‹Η«En‹^.οK€θ¬ήИ@j!’€IDATxΪμύyπ%Ωuί~ξ’™oωm΅/]Υ;ϋF ‚ΕΕ\DZ‘²υe.ΞkŠΚ²ZU9½1ΰέ'Vy© ήsr ½άvή4’DDξτA,ε΅‹ψΗΏ{–ω“ίδΩ«΄V₯P ⅁ΰ=¦ Γ»ŠίφνΗωΰ[ςΎ7 oN#§Σΐ§žΌΐ―}φΏώ₯s«IzΫΒΐ §5Ά¨Ϊ##‹ͺ‚ΰW hΈD*ƒXW¦;œέ] ‚ρ‡ε?ύΎΗοτRήΧ²„cξRω₯o\bwξnόGP@<Κ{Tt(­pJšGV@C8–vί™‡B!#Κ ΊPhΐ:‰ΚΏη 2(`Αj” `@†J VƒM2Jϋ•?9Z)>σΒ֝^Ζϋ^–Y€»TΎ|v «΄ΗGtπHπIϋˆI|@ xA5³έ #‹S {nŠ*M§€΅@‘AG@‘BEΐjA{‘>1ΒLa£@σ*JHΘ!κϊ½ζn}§—ρΎ—₯ΈKegZcC£πIΉTπΘά#Mάν£ςkψ»(Z£¬Β=0 xj‡ζ! 44QΩΡL"ΐ-1ΙΝ© ΏQΖο ²`’2€¬Δ’X-oΏ;-Λ+p—ΚHi¨=ͺΠθΔ˜8Bάo£ΐ*P‚ˆ@ΚύK!Q ΪiπΗθ©Η Ϊ Ίˆ¨θή ͺ „‘Α4ΕΥ9υCcT©«E&H«οxό~£QJ*Yxλ‘•;½Œχ½, ΐ]*―‘]‡'·}ζ‘δφ‹O(^””I[²Q$Ɗ¬Ψφ’Z6!b•ŠFΒ 2ˆΈ^°» ξp…²Š00 U»Λgέ—Ή‡Ϊ#”Q¨‚@jŒ  † =΄q§—ρΎ—₯ΈKε½§ {.ξδ: A’›?χQq`4"­T„{“"‹€h….4zζ“PNFMCΜ¬˜ ’ΐ©PTŒBBoΧw°η’'β“—a₯(€ŠFA”¦΄šο{ΛΡ;½Œχ½, ΐ]*οzhƒΓƒ‚k. >Ζμ„ΈΓ»@uι,οάώfχ ύָψφ?€90ˆήBς0Zόq…Ύ<…i …šxόŠΨΑΨ’*MšσK·ν7ž°ΑG_k|Π•ΖΰΡ.‚J4hAι@@σCo=ΚϊθVr’—ςZdiξR±Vρο=Ν?ώόs0²(a`καςŒώΎψμcΌo8ζ»Ώω«Έχύ`τ” JCΑ £ k8ΑξΈh$ΌΖ&ΖςC #ƒdεD$ba«Wω±ηώ'ΎšOξσωΏ+}0&H™ -γψ³|ψN/αRXςξjωίχXŒΫ3ΰ&3O1?Ο[7FL‡ΨZ9ΚΙcπ}\ 8’Knuu$ύ γzw ΔWšPi˜yόZA8\"₯FŒξ>TΘ<Ίόb4oΉό«όυ·½•cΥ€bώΏŸ_B.ΜsOhB „=tˆίσΰϊ^Ύ₯°4w΅ΌσΡuΎλ-ǐ:ΕΧ₯‘eGœξ06Ί ό—~—«j™…ξKLΥǜΒ Cƒb’Α(ah£ςM—ή A'qη‚4Šχ…ηΨ»x•/Κ€#'NsΑmb7w`―AœοώΛλwzι–’diξrω‹ΰέΘΜ#NΠ•«Π±9;Μz%¨ͺδΜ:σ4ρuΚ¨„ΰnj˜˜£ΘΨΰ7JάZΉύ…ŠOε…BΜχG$1–ϋ(­`κΩΦ{όΒό<{»3/NKšρ*xp^ψΣ|˜·_¦ή,²4wΉΌχρ ώΰοy8ξΔm?ΉωmόγŸa}ο"'Κ;³IΜΒΫ^J0‘ς1KR£Κh (#i(³%Χφ’R9έ(Σ o 3ώψ‰wΣΜw‘Υΐ7έGΠtL%š‡­π—~ΰνwzΙ–“₯ΈδΏό#οf#Δ*=₯’"―ΎγΫΩ[ϋΣόέσ{όΝωYΜ£‹O―rPAτ ₯ν⏲U(€ΠψB!ZEΜ ₯ZBͺΩeΣΟ¨Ο|'·ρΧΎ Ω}οG"Ρ¨ΐ¬YώΞ–Λ[ξΝ$Λjΐ{D~ξSgωsο“£‚0sHP"Τ› κωOQ¬ƒΎ†ΘK₯Ί…4Z€νUήέH‘£π•ΉRP^Ppς©‚•ΑcόŽ0+ίφQτJ,rVρίόΨ{ψ“yψN/ΣRφΙάCςWώήος“Ÿ}.*x& ΰ$Ίσλe|Ϊ]?θ"Υ Ϊ{”Ot`+"OHξΏKšAΐy*>Ί:‡+3d}„Z-P•Αkψρ>Θήw§—g)7₯Έ‡d6 όΡρΧψΒσΧ0₯ŽΰžΥˆ3ͺΥ`‘Φt:’P*z 9ΞWAΘ ό„T(Dϊ³‹€£ΦlΥ€ΔοZ)ΠCK@ψΠ#‡ω‡ςCίͺcΡRξ˜, ΐ=&6gόΫέ/p9Qό\¨bμH`€( —κ :τA§4‘(ŠŽ}ͺ!˜ΟW€Έˆ „™ίa5z`­xβπ τίNV‡KΎΩ›U–ˆΜ=&Η6όΐΡΏu$xdά/³w³rέ~•Z…Iε„@Rώός΄ϋkˆY:γ€Υ˜υ³V’Η1ЇŽψ‡κCKε“Λάƒς…―oQρSΘ•kΠh5ΊU~…’ΞЁ±R­Πύ6ƒ‘ίa˜cP0]”Ι…>ЇŽω'ζ#Y­ξτR,εdiξAως3[gώωO Ξn‘'ΥT#¨ ±a’*υ­Λ―"Σg‘Wn$Xτ‚φ©Ί_eλ’πAxβΰ t©όw, ΐ=&/ί₯φmc‹#ΜΎφ)ΤΕτεu­Fm;˜:T D O{«ϋn τη (η£'3 όžγ;ŽnπSΑG8΄RΎΊƒ]Κ—₯ΈΗδ‹Ο_£ZLΉ‚ΆGΠφΣ§~›0›‘LμxΨr¨m;ώ֞ζκ”ιΉk F B΄uξέΧrhΓ ΅ηqWjώΰ;ΰόae°Œωο&Y€{Lž9ΏK5.ΡVcΚΥhΜa¦O}†0‘ƒ κΨ4„ZbƒkΏω+μ~ρΛQΉC"χΈΐή“Οrιgώχθξ;iŠ€BfvώςΎƒχΏχν”Ε2Υw·ΙάcςΜΉ «5ΖjŒaμ!ly‚ιӟΑmnFe.P€†ιΩ)«οψv˜'¬ Π†Ηΐ];DΨv¨™‡Y€i€‰G]σήασΏυNŸφR^£,ύ΅{LΎςυΛq@e ‰ _Cμo«¨_ό aςεD2‚pτc?ΐ`\@*-Ξ]ƒ¬Rϋ‘’g.„ή£„©£Ή0ε?όχ?|§Oy)―C–ΰ’Νέ†oΎ΄EUΤΔ‘†=χVΡLͺ²ψkη™ξlQ=ψ(z4―Θ€I€‘”Lqώ`T ηiή·mŽί¬ωo;Αχψԝ>ν₯ΌY€{H~εσg1NP΅Γ―ZD§φήT Vο,Vn›ΩӟE― 8|³Ί‚ D„ŸΔL΄a₯ˆεΒ3‡»4CζžΖ ?ώCOάιS^Κλ”₯Έ‡δgώ¬Υx«pλ{qNX+bSΟKα-vfpσT‰2cΒ|›ωσ_C)ͺ6ΠΕ„eU¬ώS±π'Lti°•αθΡ!?ς±Gξτ)/εuΚά#rm§ζ·Ύt5²ψ΅= ΘΐΨ'ΈC¨zj(ζ½ Α[Čρφ0βk€0χ ε—Έσ«X@€ Y-ΡFa Ïπ˜eSί»^–ΰ‘Ÿϋδ ‘²›†„( ~£ΐ\˜αŽpG*ΚgχbgίBcŠ!fΪc‹„Α ΑχΖ{ h=­¦Œ™…f»αrͺο½ KpΘΏψεgc^-¨άΡζςœζα1ζ₯)fΗαWqΚ―@X+0—ηψ£μfƒ€6Σ€.Ξ … fζQF£΄β]o[η‘–}ύξYςξΉpeΞ矼ˆπ6NκU š€ήlp―`ΞM «z␑AJ[³ρ=λώPE8TV dlp «ξ­”ΉΗ#όΫίϋ؝>ε₯ά"Y€{@~ώΟ‘Jݎ·ΧjΤ嚰V &>ζοQθ‰Γo”θ& /ΟQ¨8"|Υ’$ ΙΘΰW $€;T‘φ< H©Q^π3Ϗ|τ‘;}ΚKΉE²4χ€όλί~ S”€έjJc&.Nγέu0υΘΨ /ΟΡ{ްjΑ ‘Τ„RΓn τž#,AΗn@Ίφ˜k5ώP‰A#Γϋ9ΘΡΓƒ;}ΚKΉE²ΔξrΉ²9ηwχEi‐‘»~κφ«Œ’|f?2„‘‘Έ4GΨ‰G73(42¨‰ƒ:€ug¨™Gο4θ™GON„?Ί€ύήS²4wΉόΛ_y6ι±}ήσ©jͺ'Ρm[Κ‹sšE£Πg§q~@jηEτ™RΜΥ9橐^WΏoύ=ΠΆo+%Ο€ν gHΏ©c`‚ [`«Ε`D9ZΑTCŠr@1Z‘―QŽΧΠΆΈΣK|ίΛάa‘ΰ™οnRοl2ŸμΠLw©χΆi¦»ηγτӎδΚL½Ά‡—κ=G«}υ–ΉΧώWZmο”ΏίEXr» ςΛ$=ΪnBω½’ή›[eσ’α0ΖDƒ° Β`ΌΚpύΥΪA΄Y:§o„, ΐ(Νd‡ιζe&›W¨w·¨χΆpυ₯M§θyηmEυYn¬Θ―ζ ͺW|‚ΕΎα/ςlD:c“½ˆΦ›θT;G%mGbθΝ(Hη-! !PΡ¬n0Ϊ8ΔψΰQΚρϊ~ΝξuY€Ϋ$ΑΥLœgΊu™Ωφ5&Χ.ΡΜkBˆtA‘ΒXCΦφRδ½ύΜ%ϊmίτ²\»*άύθAμχ ΤΒc (Qέg QqΣ– €κόQ½c‚τά„>τn΅WuΣ%G$H€ΰΡΦ0>p„αϊ!F²zτL±μ>όzdin‘ψfΞήΕ—˜l]fzυυd₯ (Eπί8BIi{oL½κ?™½o _OjΑŸλ‹μ%Δέ5‘ΥέVρ_αR+Υνβνg¨Φ)½ΠBς‘φzοiW½Χη?KοŸξ»>’/έ€²}NΑ{ΚјΥ#'Y9t”΅aΛ%y3²4―Y„Ι•sμ^:ΗήεsΤ{;hcΊqΫDPΜ{Oπ Pϋœϋύς'Η«ύžΑώΗέ‹χ½[-|υSIΥΓϊΑeίηιλ΄oŽHρ}ϋΝ²x~κ:—ζ0ЬψjίλχΓ Ι‘ !„ΑΚ kΗN³~μ+ΗN‘Τ2mω­dinBΔ;ΆΟ=Ηήεsμ^:υBwEρy? !€2€©Ί!ύ}Qn¨Pm>±έ"{>AΎ΅žχΏΈθέH%ΟΖGuΐR ct²έχφqΒ.›ΠσN²χ9ΎgAΑ―·Dέk»0½sx¬β$ƒ§Y?ρ k'A›eΦaΏ, ΐ+ˆΗφΩηΨΉπ“+η£[―;Εh_'rΛ •ΆχΩY‰z±Ώ,ΈΘν [ρύπ²J±Ο©ξvηή'νχ<‚,„']ΦAPZ₯ρΰ-€ίэ•ZψVi³jρDTΟ3ΈΑA/„A ‘ΊΑω«…GrύΫg‚-,Z+VcεθiΦN>―γR–ΰεdrυ<[/=ΓΞΕQJ£•J»\‡nΗΎ™‚χ‘@9ΕΥ)܍]Ϋ¬δέŽˆŒgη_SώG©…”=½Ίέ˜ΕGƒ$5DήΧν§ρ­WA‹ωΈrξ΄Άhm[7Ϋθ"Ν4cΣHqΣ_Nd@ξ)HΟ ΰzdΏhPΦΞ τr‰ϋV₯oTο‰ΎAP*‚­Ζšψ1"¬υcxΰΨvO½eiz\ΓΦKίΰΪ‹OαζS΄Ά©kΆΰš† ‚-Š„• β>μΫρΉΑ}ž§νdο8ί©Y9z€ΨuΚ QιƒΗ‡οσOƒ >8Ό―ρ„ΰz)·΄±{¨u[*Α,ΌZ]v$‰Θ|ΪΞ•2 4Φ”hc1ΊΐΨ k l9 °вBk“ hJf1„«kA)ΙF鐣θ…EjŸ§Π[kΥχ†ϊ„¦ΤμDkΆ­ Z«"Glœ~ §ή‚ΊΉKΈω„+ίψ2ΫgŸ£?Wήœk€”²“Ξέίοκχy:}ειoΥΛͺIK€Ι·nžΖMpn†ssœ›αύοkBpι Τ’qΘϋχz/νΠtΥΥΎ|_LΎ–ιΪόΖ5€UjIk²˜φ‹oΖ”Ε ώ”CŠb@U­PV#:…Ρ“R)ΔΘ^I4jͺ ;€g)χŒO³x J (1mtΚΎhρzx˜#oyΆέι[ς “ϋΪ4Σ].γ‹lŸ{“h©Ωύ‚χIΙ₯Uš|σ‡ΔfλΛ1fˆ••_©x3Χ³=ζ³]šf‚5›‚λνa7Žμ9ί}φ •ώFοMaLίΌB€uθyιRƒ)-Ώ?¦:£!γw£tƒH@¬­¨Κ1ΥpDYPU«Ρ0hΉΩ(ΥyYΙυ·@²Η°/•¨Ak΄ΖΗ;‡„ΐΖΙG8ςΔ{)†χ~Ϋ³ϋψfΞ₯§>ΟφΉgŠŸέΜ΄ω@pΩ₯ο3Ϊ’WBr-“ Ϋ*~§œνΎ™2―w™Οv™ξ^c:έ‰Σuγέ‡) :₯«dΠy ΅Ÿ[KM ϋπυ€f!7Χ!ρ½χΕΰάȊ„©Ύž{>ήE*Qo₯Γ;φ‡έρυŸˆψE\Ϋͺ3¬1­1mP”Bπ-€Ϊv.ξeΪsΛΧ-Ώ&Œl΄FιΨπ4ΏTB@ƒHΰΐ©G9ςΔϋοi²Ρ}g=5.}γ‹ΙυλNĝΖ{>ξbέ.ίΫΑΪ›*ή`‘εΔζέΘPΟχ˜Ν·˜Οv˜ΟwΰήyΌsΔ»΄ηc'Cr£ΨVυB’ύz©΄n]'‘*ΕΈΆέύ”Š7z3€6MΦWΠΜΡ_(ςιΉσ?τOΛψλ\{ΌϋΞ3~Qzu€.JJ₯BK‰ΣΛC (J†£ Ζγ †”ƒQ z.'{qΊ3Z‘ €nΧ>25‰)O­αθ[ήˁ‡ήώΖί¬o€άπOώ•πΎw(ήςȝ>½Nζ;W9χεOQOvZrHŒΕγξΔΌH~άζάψnD%ΕΠHπΜf[Μ¦›L§›qϋN$*ΎŸΓμΛΉίP ₯5Ά(1&"ρΪΨH<κΡδ²’.THˆ”Βž’·fϊ{λ#­{έƒ… A^Yάν_ζΚήΑΒF―φ?ίχVϊΟJϋ\Uι›…x|Ά¨1^9Δx|  XLcJχ-σ2S• e9QΙ(ͺ•UNΌλΓT«ί ;φ‘ϋΒΈϊμ—ΉόΝ/_—ϋ•°ΜkA=ΪΫ ςύέC£ο=“½+L'W¨η{νΝ$ Ÿν .$΄a°P“1­uThΖ©@HcŒι@Cο.xΌσ4ήaλ Sαέ”ΰkͺ•1{—/0:ςΑšν Tkk +š½kˆk˜Ν5«#fӆᨀ™Ο1ε€ΑΥ ƒ΅ κΙ­+ζΣIΟlΑ^x[Ζ9Ζ`΅ΑI»hη9’ΖΜXτΤ»E [ί~°¦Jψν{Ώ"67A­duυ«λG>,β=r“Φ=#@ ’§Τy61u˜B% ~μ]|δ]wϊ–ΎerO Ž—>χkL·\Η΅Α/wzΜ6€}4D"½έ+LφPΟwZw²Ÿξ˟ΌO©2Rξ^Π$W\k”Ψ²l]σθnͺ‡ sΤ‘iwx稝ù&)QβXqSŽ WQΖαΓΈΊ¦X9@ΑΟv(W7˜^Ύ@9ᛆέν†ΡΚmšΔV<ΜδκeF±uφE¬αζ3†«GπagΎΗΥb₯υςzF¦`4…΅Xk)Œ₯(,₯-°ιo/‘E-ξεG2 Ρ7’ h6Πa_eίuOΖb4>ΘΪϊ1Ζ+‡Ωοiε₯³GŸ%§ uLG*C*₯:9RΓυCœϊΆ‘τݟ6Όg €›νρΒg>Žoκ…ηs[’β‡ΰ Ω‘ 1U΄>*ΝtΊΝdη“ΙU΄Rψ}4XDbΑOπ  ΄@Φ&"ΞΖ`lIEIΩE„¦i˜Χρ§q uSγ3ȘxˆDΕoσγ9G΄›σθΚ*u³K1ͺpΝ tw5h8ŒΩ=λ'`χκ&"ΡUkΗϊΖ;²wεΖF‚OΉ²†K;32^?ΑlχΥΚOO&]±GnϋμΗ2RLnŒ‘* ͺͺ’*JΚ² *Jlš1Φ….υ—ώ°ΘύXEη]©}Ύ… hkΗYίx€²DÜ-{zρ½νΞh“pΙF ]”<τοΓξξLΑ=iκέ-^ψΜΗ{l·(!1φ$d֞τbyΨ•²·s‰­σ?s0…MŠ?3$Ε'¨H²Ρ[T[΄H³ 8η™ΝηΜη5³ωœΊiΊ›1“…Bf¦<{η_ΧΕΰ!‚4Φ8ΙφΕσT+«ΜΆ·<‚›μbŠ!υξ6Ε`ΔdwBQΪθ!£ρ βMMπŽ„•ΓΜ'»Υ­J6/>ΛϊαGxa>c–ΐΔ,EΈΊ=]υ²‹’Ph£”UU2¨*UIiΛ”Άμ°‡°μ $=ΐ2;)YDΪ΅ΟpΈΖƒ§.ψxJ+”ι2"JE# ’<‚–7Ο'†~ΰϋ©V6ξτ-šεž3n>αΉίϊ7©ϊ.‰>$€ΨKΪae„ξ‘έ»[ηΨΪ>Χ–„ ΈΊAˆ]l2ͺήξ>$—ή–hc’;―ΐ5Žι|Ξt6c:›Σ8ΧRQ3(=Ω΄W…Ό“ΖlDšΘΒΉμ§Ε pΐO9yδΫWΞ£ ƒΙΜ;ePhόl3ά`ϋκ&­2ŸΦ„ΰŽJκ: Έω„ρΪQκιΪ–h[œc²u‰’‘”E›1[†YTšήN­“‘ ή/x9}μd?`Έ"φHXJQ%ΓAΕ ͺ* S©G€Ta—š”˜šάO;Ξαή7 BY 9pΰAΖ+G hUGhδB(έσρ*ωπcγ;}λΏ&Ή§ €„ΐsΏυ―pσYοΙ¨όΡM—O‹d'είΩΎΐφζ™xc₯M$΄Š*(eΊ=şΖhS`΄AD˜ΞgμM§­Βηγςήu»zowΙ0Ε Dn"Χ»ΡΉ}W;§±Ζ…屃kLχ6±ΓεxΜήε‹(eβ)™Lζ †ͺaΕto !Pβa>amώ9ŸνP­c6ŸbT3@€] 81Όθv:%θ_OΊuκρBŸ_!ϋάόξsnτΨΓ ͺ Œͺв(ZCΩ7와Ϊ@₯—E΄RΨ’δΰΑGΕtl"‘ZΕ°bσ–.¬‹€aϊpl9ΰαόΘ]Y`tO€s_ϊD,ΣM"9―Ÿ\λ:π)+ΏRš½K\»ς|t³3Eί‡˜³—˜ƒΦΪ΄ya₯-Ά¬(ŠŠ ½½=v§S&ΣY›+π.ά4σ9@βΈ…ψ`άάΗTJσ  3…ΥΪδ…δόΎjρˆSzΖhmƒzΆG3έe°z€ΙΦœ7hc)CD`0ΰf (Γ^4V !εΪΰΣy‹ŠΐΡ–uSP‘π‘δ’ίe..2σz·d¨NαRp,z2ΩϋYδ d#z1/#ΠϋW+Ε¨0VŒ k»π¨Ώνχ©Ω…§ϋ e4₯rθπcTƒ•HͺJΰ nS†QιCς 5ΙΔ^9|‚οωθV›–{ΖLžηΕΟώjL₯$€-?ΞΚίΏœ«Ή|ι4σ½Έ;(Ί&Α§ΦΊ:qζuT’ͺΒ%{Σ);»»Lfs²‹œΓ{oœwmΆ‘ΏΛΡKϋe>Bn ΣRw‘SN[i΅qVmhFΨ*2ΫΐXωΤmW¬`²y1%Zxη±ΆΐhŎ‡@ΓF!”&ξ|Ϊ(΄5©0G£Λ1€ΧE,«j…i˜rΥΧϋ8$c΄ŸύΧμBˆΈKθΠzθχ³’35ν_H3φΎ Ba £Α€ρ0ώδ%οyΞAJG‡Nμ!“ξ›ρψ0=―‰Vm‘½Œ#kP{$ο9υώοf|ψδV…›’{Ζ<ϋ‰‰›Oγ/·B€Œ$f_{J³½}«WžK1yΌ2’/)ώΦΦ¦βKQ J±½·ΛΞή$ΖθΉτυ|Λ‚ΣN]¨.‚)bΐδ]ΌM₯*Έžxί΄)¨œNT*ePͺs~W+ΓF³‡2³mŒ΅ ΄ΆΜgσθΊV—š†ca­Ι—Υ‘g/¨)ή#.Δ‘£!ξ› †+nˆhΛT€=ΎΜΐS*Ž^+ΰŽx“bόœΒ‹”ί Κ.Άο#O#ztΩ@Λ κ1β[γs£Α€•ѐ•α0a+Π…Wρ΄$e<Ÿ£ΗήΚ`΄ŽΡK@©h±Kk…- ΄Υ-ΫVCωΞ½ΣͺpSrO€½+η8σ»ΏΦΖ`!Dχ]Rz.KΞ_Όπu¦ΣΝΈΣυΙ@iΗA@[‹ΦE5`:Ÿ³Ή³ΗάΥmβg'°«ijΌs'ΈΑRjcβkm4*&Φ€ηxίΫi’+€Ν2„^Μ AΟFIεάtŸγ/-6S“ (VJ‚ή‹·(ΡvΐEηY«<‡†C|΅B5œ‘‹"ξΚFqζΚbž>Š1 Z”V|γ…kθαΓƒΗ M^―±…F|`΅ρΫρ<{ΩαΌ'x‡s.νΞu{ΠǞο_@ιΦ‘ηŒ†+ψΖ΅ΆΘΨk#%ZχtEQPΫ‚ωtBέΤ7ΜDτ%ͺy]sq>ηβ΅kŒ‡CΦΖcΖΓB&EΑ˜N―ρΒ³ŸγΑGޏz‘NΊΟœΗ)MQ*Εή•σwZnJξzΰfκ½½ΨΝE# mG$pφΜ—γξλ₯%ΫH’ψ*mPW·wΨ›mΕ™wΙ₯ΣZB`6DW?ρ ϊΚ¨”’¨Ψ’HŠξSž?ǐ9ΦΝqlΞ?ίHrͺ}JӐ[X [φ:σΔΧ9Ρ ΗγT!£ΉΠJV‡ΎbSFODicΐ;Ÿ8Ν η‚)p °Ϊαƒβθ‘‚“ΗλχΫ§Ψά–‘ ΅n5OTšg.„*ΊψΖ¦ušfNSΟ©ηsκω,³"/{ή‘άδ©λ¦Ν(γlm eU1Žb1”6ŒΖ+TΓ!“έφvΆ_υ}“Ώ2‹i[£5kγ1λ«+X«"ώ (‘žοqζΕ/qκΑχ† 9RrΞ‘MΜΜ§{7δjΌYεΗ¦[Wxζ7SΨΈθApΞueͺJqαμW¨η“Δϋο1ν”ΒhΛΞtΖ•Νxσ”U…66)«GiM=›1μu»χΒ *Κ²J»ctύ3HΥ‚^i§χή½Ά“LbŒ•Mϋ=Ω[ˆΟiŠ’δ€­I…α²ίαˆ1¬¬jΜHS{2…Ηh‡χš EΫF,(Γ&q8η±F(Μtπ@l©e«ڐbxƒκž>γρ‘KΥε”g>ΎhCbjt60ŸM<5υJJ#©6QΑ`0dem£νξŠz>γΪε‹νuiθMHa<¨X_]ee8 bΊOΰΰαΣ>ϊh$j…Πf2tΊφEU@<φ±ίOq—ƒξzΐ–U ‚Eύ—ΆK Ά7ΟRΟχ:°|λΪqiσJΚσλζtqΈη™Ο¦/{sšbΕT’Ε5uμq'ΰSΈ ΙyΝ’5EQ΄σς$H€#§ο΄e™ˆDžιΌFαΌΫβDYQ­(BZχ5ψθή6¨1’%’BF4?&Ν:X 5a>'HClE [UEΑ£Η_{FR£Π|ΞA”ƒLΐQΪ0ZYe΄²JSΟqΑ7±Π©6\'ͺ[ΕωlΚl:a4^eu}₯e5ΰθΙΣ\ΉέpQᦍVŠιΌf2»Œ5†k+l¬­ ”βΪ•―b0\M­cHχJLϊF£ wUϋρ»ή˜’"ˆ`$Wu…!Α;6―ΎΨSώΈ#Š‹ΧΆΨΪέ‹ dmλfBΒΛŸcςύίo !Κͺj~ νw―Oωj0€(«θΉdšmΒκzΞdo·Už]₯©΄εΔ¨`ΈR :–ε Ψ2²εƒh£A#’@4!ΠυΫ IΥo₯­Pπ QΤ„Ά­ΌkΖ»¦5ΐΎττ^Ν”n‰KΉρ§±©ͺΝ§G,£DΧσΆ@*Ό ΕUJ1ΩΫΑΉ†•΅uͺALχ>vœk—/Ε’-Šσ²ŸνCΰ΅-.mns`m…Ck\8χ$=φ‘5˜P'I†Π9G©-¦(oυm~Ϋδ7ΪΨ"υσK»&~lnΎy!ξ*Αf󆳗|ŠΏI₯νίΑΎ‘Z)η~#ŒΩ€TVδώ«˜ΣN©Ηό―GD„ΑpΔp4¦©λΆ‡χŽω|Ϊ¦:;V£Β!<8PŒΧ-vθȟtjЉψTδ’πή€ Ι;Π€Ai!\Ό…ΑaΠjˆ`¬ΗΨ‚rh˜#‚wTΑρŽ'*ΎόυkΜφyN-βŸβω’,)Λͺ%Ω’hΑΝfΞ«Ϊ½cffΖΞfΐ£ Εκϊ;[›m±Φk•|ό›Ϋ»\ΫΪa}uΜځ³¬―ηvβ½yH"ΨΡΪλΏ©ί@Ήλ €ρΣIK=Ν Νφζ‚ψ–δsωΪ6W·wZPHx@,ψQZΠd’ DΪobδνΧώDYΥ).W¨θI@<–[EΠk€„€±Χ4ΤσyG€Q}ί"†, ㍠Β% • NαΔdΪe+œ³νPHn– !Κ‘΄’(@λlθb·δΰ]k€΄­8tΈβύΓ ^8;c6°»u —κ""΅Ϊ§\~Ν|6₯(ˎπD «€ o¨λω+“R ηvwΆhκ9£•ΥΆο‚6ζu€ύί³½;α+_ϋύΘγΕ΅| »qεΘίς3‚oπσ Ύžšyά($ E™]VΨαΪFά`΄ΎΑφd—\ϊ)ζ“]Δ{”€σž30«›¨ό=Ι9yο\›wV©&=3ξ΄6Hs`‹²m”™«β΄ΦP”aoά}.7+±Ο_4RΉύWT2Ϋk»fŠ†°±’1λŒ ” ‘Hε΅zuσρδ£ϋ*ήα]ςv4Ι­… 8§A^p^γ΅ΖΖζ†&ρE±’­U°± ΧdΘ`0d:ΩcλZ―1K2`!ζΣYά‘IA6Ύf>§(Jš}=“lE˜Ο¦ΨδYTΥ—ιΰ·P]»½“¬όι¦ρζπ£Χχ ήαφΆpσ=‚«S‡'Z#ž_£ό΄ΖOΆ0εˆbυκ6‚{Β([P­¬1ΫέN€b2ΉΣٜ—.\ξYκύ" to»ZT[PbŠ‚PŒ1Έ~z.ŽF$΅ωŠ”Ψr0ˆ)‘Ό[scΚκ+žW*U΄)ΛPVlYΆΜΓZ§žφ6pbM‘ΦKTΎgr"ΔΪλΒ—LŒ0Ϊ‘-—0 Ρt T€S@ι„‘xπυ<ργ-Ÿ~ιI¦ώ2kφo;ρ0++8η8²aΈreBγγ±G+Μ¦‘£@ΫΠ” W Ε`u‰Χ± ΖpΓ5κκ‡[juΏ>AnΡξΏπZ³½s΅ρψ½ιΠ†STέtb7Ϋ£™lαη½P¨Nζ4α!„Δ84υζ9μΚaΜmœSpOŒNΖ‡Ž,tΜiκ)Ϋ{^8wι7ΰ·7MCΏp»HΪ΄d[ΰ]άY|*Κ&sήmY2±EΡζΕo6,ήΣΤ5{;ΫΤ©ΔY)…΅EUQ † ‘ερ#cΚC«˜AIξxl΄f«v|ϊΕ§™—Ζ•η:ύ|ξCh₯‡ί²Vθjg~΅CE<Σωίyβ*­™οΟ"/\ΊΤM}δT‰BG‚”ξf#tutνEh©Ϊ©n"W'ΎΜmͺTjν«Ϊ4¬΅‘%˜λΒkHΎ’h₯ΨΫΫ‰Ρ^„ΣqήΔΥηŸdλμ7i¦{-φ‘N”άˆ’Ε‰€›”{(₯Q¦ΐν]%Τ“[~Yξ ΐˆœ~ˆKΟ~­ {Ӛ3\ηςΏœΔή&*ͺ΅1#Π²Ψ" η€Xβ‘₯Υ€²Jιΐ8²EΦ&¦Ήœk{ΌΪόtωlŠ$ΧΦΛp<ξBƒt3^U”+j₯μXj!πΣΏύežxψY€Λ!~uT ρIυβ},4ςD@?21΅…ΚδNγ}­9zπ γGΆ8vΜrϊ%ΕόΡΟΆ£ Γ‘εδ1Ησ/]‹\ЬΜiέbιA‡ghm°eϋ+@J£ξ³ΰmqQχ{/(’·δΌ‹)ΩΫ@sk‹Δ‘β`}jΌΖζ™oβη{‘«a,žcF#RΝY,<ΫGύn[™³NΉ.B)…2†fοZμtzήΐΎžPVX?zŒνΛ—pW­ό@»Ή΄³cμBw˜Xn ήcSš'»βu˜αƒgP ’‘θΝώ‹Y‚:ηZArΛ+ά ύΨ{ΗξφJGOQUͺ4㑆‘m=%Š_{ϊΛΌΈσώ#†σΧΟ½σΣJ `1™j⏛Υ(ε™οNnŒ±ƒA܍Š F 3,3ZzςΠ1žόζ <±’xξ9ψƒ@xιμ\9‰w «Υ_οΕ«™ΣŸΞIθΘ’+ͺв¬"£RiΊί:go…I٘˜ *'B‚tΧς‹ˆ0­΄@9Q1ΫΌJS†˜TΦm’‘h[‰νΛ&εnΤΖΖB£Φπ9—ξ³ΘΙp“MŠ•Γ·ό\ξ‰  ΫNΌNœb΄ΆΞΑG_ϊ+©’°έ­sU½Š6]Ϊ¨μ1VφMΓd²Ηt²KΣtο ‰ξ+ΗJͺΑ€ΑpD5R–elϋ­sˆ_ρλωŒΙφ&Βή4‰Νη!vš)}ίs¨\BΈ|AWͺ7ΔƒΦ ΝΜjΔšέ)+‡7Ψ:5† σ:nΣΉ_-rΛwKF<ώ6ΟC{κ©cΌ6agχ n>kwπŽΫΔl[°ΆMNͺjΐhΌΚ`8N #LvwΪΎ* ΅<Ρ+Άa+°EI5΄Κο½g>›ΰέ­!’ΖVΖλ(Ά°vόd\C?ct4”Jγ\½·αΚτjR#V²KΥr<ϊS’₯™#α52IΏ…ά€)m»χž=1–ίώΜ―άΤηde O’JUuΪωˆClΡ”e‰Σͺ-•qο|ͺj‹=Ι­§΅B₯›1+E[=ΏPZάNδ!»Θ±6A€“#‹i‚nπsϋ\ΓωΙ%†»5ηΟA0P†έ½ ΞΜڎ?]ƒE¨ŒLvvρ>0ΉΌEQ–4»SκωŒΰεx€ΞΆŽ―­ΡSςΉ/NΝk;8ϋ›Β‘Α!‚oΪγ?΄fy±¬qN΅υE„ΖΪάΥΧΤ-κ―΄B|Heή‹@΄5­ς—UΥ†eBjžϋ5ήz9~μ4<ΥxΜΖΙS)Œ­Ψs,_ Rν‚taOnη–€C•]‹ ^TΞΏF Tw\”0ίΓ ΧoιΉά ŒΫŠΊ η='{ ώžίΗoώΟPά3KD ΌRˆ€ψΣDKΫθR₯F’$Nλ±Ε‚Χ$P'Š«ΙύŠ"‘μΚ¦Σ"’ŒŠ!Ξ'7Hξ\άΥ.„΄ϋfώύ±UƒY©βg₯ŒΪ+5₯;0o«Ήςλ‚S“^Εd†ζΫ S0ߝβƒb0(„έΩBΐ4“­)¦,:Ÿ±Λ—π°ύ=όςWΟσ]o±ΒAŠ’ˆ} N ΄ζτΙUΞ_‘Τ‘h[j‰|Σ =·?VvύCͺΤΛτη²,)ͺ˜mΙ‘UΧ4Δέ–τΔύԏ2:pˆ΅cΗzd¬θŠχ¨’ ©η †γΆ/β!ͺχoΏΠ*λγO];#Ehζ˜αMξ+Κ=aͺՍvΠ‡€ηϊΨqόΔƒότφiQιW#'ΰΫϊ«_ΪFm³ŽdTQ΄Υ€yΚl‚ ή{tΣ€zwIm£ŒψŠ.>Ž7Ί‰ρΊ˜Ξ{@₯JΉš&080€R§Φζe Pprύ$Ο|ξyήύπΧΎ.|ξlΕ;n]χάzη˜oMΩΊΈΙ`Pα›Xΐ΄ΉνX?rε=³Ι[0 m5Αt«'#oγ΅rΐZυH¦$ΖΎ‰YΡM6†c؜ξ΅λ›+"}j·T SΪ+Ή#P―_QV1š0•Ίž§¦/±ΜΥυk/Ίz²±~˜·}Ϋ‡(†£ΨŒ₯νbΣ±ΚκΩ΄m!7Ž›Ύ@«ΠŸΖ·-›‘Ÿ": SmΑΡ³·Dξϊjΐ,/}ρ7„’(ZΧZ‹Υ–½fΖΟώΓΏΑΩŸm{Ώ½ϊRm**§sgΰύυωωΪ΄Ν-}l4β[,B₯n@&•¦τ ‰pdςγ^ϋͺΈεŽ4YI;d―•ΎΓ7dN ΒF„ypŒL‰΅¦έAE)pΏ;e>™‘Ep΅gϋΚ&γ•1Σέ «Χ% ΧWΈψΒy6ND;,)ΖƒXF•<Ώ0’x”ΆϋΦ&΄ Ν'ŸΩc6σm½DΫη―W*ΫeτΜZKQξb‘ΰqk;#ε6κuʘά ήsψΨόπϊS”eKΊύΩ5 ή Υ`ΤrQι^Yέ8Ψm«–g\œ5‘n±Ls‚χiΈ )ΜjRƒ–Ϋτ4υœ·½η|μ‡ώ…)bŸ‰ΰZ‚Qΰ<«―€,«65ՈυγbΏ•²Š0ίL―·|₯U"Υ7―ˆkXY]ηΐΏΛχύώ‡Ο}β—ψςοό&—.œΉ)A+Ω3θΡU―3τCΟ δ£κwΣΝ―Ρ -šΪI4l¦γχ¦GJΪ~„9ƒ‘‚0\Ya8^³­=κɌb΄ΚšΡΤσΌΚͺ’,JvΆ7Y9pˆΊž£%ΰ(«1ΥκˆΪ W7§?=hwje4(Ϋ#Ή€Σ‹³:°½ΣDͺtθ)Σaσψ4Ÿ²'ϋΧβVŠkjΦζ½ϊήχ‘ίΛ ¬ήΕqς —­χ½°|^!vŸ~δύί΅PxS’ΓC›Œ)ΊjAE#½5½4ΏΣ|Πs©ή₯Ql ΧδΦKδΧ<ρoγΫ>ό½<πθ[ρΉoCo0kƒ……XΏ5tS†k_“ςηβδ©v…€’ϊ΅χ‹οgJ΅ήJΉ+³βΏώ8ŸϋκεvQ:N΅™‹πρτ0Φ¦ΆΩE‰-KŠΤW>ƒt™€#7ΈΧφ/Fnχυβ³OρΕίώ žώΚοRΟgm²Ϋ"ϋ*έrΪ±ε HL“=ό@Εƚ] ’Άωδ|Y}ˆ}½BtΉΔhΒ‘S½φγ›Ohv/·}fjΆ‡–kQ’Xxΰ5}ΟΛΙ]ηόΜ―?Η§?’4muΟ`σ₯ήχ°Ž9ZΠΑ‚νxδ½ζ7’~‘Πή¬§|ŒΣΏm-gžϋ_ύβοπτ?ΓζΥKqΗ­tΝϊ`>(΅HΌ!Ύϊτ.Η•œ<6 Lα@#†”B@Ρ8Ψ› ;Εηφ`ΦHtΥo°7ΎΉ[Γξ^άuXγV*ΕΚP1h¬MEQ"ԍ0{v&žΝΗdκ;―λ6K¬ ¨YY?ΐ[ήώ>ηϋyτmοFe΁w7rSӜ‘A€9Φ'9‡’†Θ6Œ7ŽΌΎγυΝ’ΧΣK‹Κ>;|;ƏίUΐΌρ|ηŸώgμMΐϊαc„Τ’ύείά ΡOυ>gεςν\¬•Ή q4σš·|ψ_ΧρΧΫ—7oΟφ“©βρ)₯1ε»rθ–ί]εό­φvvšΦ &†Ώ@LPόφ3 ρχCέγtœέ‹3―ηTgχ«—œ{Y|=g8Zα=οοϋ=ί‰²›Ορo|•³ίό:/<σuΆ]ΑcnΟRgΝ³y聝;ߝiείZ3%m?²ΩΕ΅Ι<ύώ$Ή©^ 7!ΉΦ’ 9ωΰ£<ϊφχqϊαΗ9vκαv—𩃫AB—- τΌEz^@fY&C8\ύΚΨ)Wμ΄p2>€ n’―ςjδ1>ώΦO-΅»Zΰ—΄‹₯€§Ξ;.l{6Ζͺ›)ο=br#M‰(΄θΰMWΠ1±^έρΕ^yžΥυΌΫΏ“oΐw‘lA=πά7Ÿδμ‹ΟpιΜσΌψμSΜ¦“X`Μ­GΈχιvί}O*άΗ [‚E~B_ΓHn'h—ζ eΙ±rμΤÜxθqN=ψ(Žž@Ό‹MH‰)έ›re{»Ž.ΥΦcε{%,†D!g-|`΄~πuŸΈΘ1ΘsΫ.L½K”{Eˆάηΰύ—O1Ÿ…Δ…Š¨Έθ–‰NΓ©¬ζŸύΜ?ύέ¦œB μD΅RΓOΥnkϋΏΒ,δ›ΈΓBPΟ1ΖπΨοδ±·Ύ+ζΖ­₯žμqξ₯g9wξ%Ά.žγΒΩΈ|α,³Ι^€-'†άm‘W:‰E|cDΪ’ H’:xψ8‡δΠ‰S;qšγ'N±qμp7»Γί軇1τ=ϊ€ΠzΙ^¦ν_$Φf¬:ώΊΞέΧ{τcΠ…UpCDτύlώΦO}«5AεLΙOΉS­ΡώιοΜψ3ί=$€άr—Β‘gέΥ 3NΉ)eϊ₯Y΄Μ7-"qڍνΗO=τ8§zΌ­2Δζ³)[—Οsω.]ΊΐΞ₯σμl]εΚ₯σlo^Sƒ·xΌ-"ιψ–˜5ŽX]?ΘΑ#ΗY=p˜ƒGsΰΰa>΁#ΗSJΞ·<€ŸMoΣα₯Φο-ΎΡKυ΅»Ύt”ζ^ώί؊rπϊ8ωΎž¦ΓHί—lBpΨj·QΉ+ ΐOύ3\Ύ4₯X΄Š­Έq‚.t"Χ΅ ˆΡ4Aψ…―Μωώw>vό ¦_˜’Η9-Ί°ύXωΊΕ~5ΐΐkœVΒ5X₯8tδ‡Žœΰ­ΠR‰γΐ9Νt{“νΝ«L&»μlorνΪeκι”ωή»Ϋ›xοΈvωJivΆ―΅™ƒ\—Πς ϊ '^ρ3ί½ϋ½ν—rΨ£•5вbu}ƒ’¨X?p˜rIG«kl8ΔϊϊAΖ)Κ Rc“vΐI*†€Όώo—ϊ"t±Ύτ\–ΨΚz$  ŒΎNτί5„¦ŽΕ\ΠΥJd*χώΝΗT·e9ξ πΏώΜ“ψpEή=Z΅?ͺPΘΜ£lΜ›ΜηζόΘ{mΞΆcΖι? Β~<αF©ήΣκ΅z7)!„H!*rYV>zbα5ύΚΔΨN&y½6T{W/3™μ2›NPΐφφ΅Wέ0S)́‡Ϋσ-ˊρΚ*"ΒxύΪνξHΪ)³b_—`y}ϋλ•~xΣ{œIZYαeŸGŽ^WΪμ½c΄ώϊtΊωNΚDυn&ՏMB[=‚»m#ΗήτΰΩ3Ϋ|ώ«—W•:§Jˆ U\‡*tTΜ: †š―œχέ5Ž ;wυߚοv·―vI&NgώCŸβ+ύ>χp–aKͺv ή±rπΨk?θU^·ϋ΄O·DŠA;ϊVΛ›=ˆδ'~ϊk Σ±4­B[ιX[TγγΌk£  ΠͺRρ~k†Q]ΉΠφΧΛξ/»•§Μπ"Χ~Ÿ(υΖxKΉΫρUzόrΚίέ#aΑνΟΥƒ•ΧΊιNΟ;Ϊ·›δM(ύΌΗVnΫ²Ό© €σςKΟ`JFwτΝD5«™Cκ€ΜaββΛ:N΄ωψη\άJΝBCΧΔa±ΌΆΗzλ]ƒΤƒϊ½ϊz’Σ·—YΚmϏΏΡ»τ‘σ/ό$l — ΐθu²š½MnP]ψ7s8\3§oάΆuyS€ϊkΟΫι%>φ»hΧIY«Ο΅R Χ ΤF‰λ@Q;ώΦΏήΖSM™Ρ%β{EισφNR]–τ A"eΝm§Ό-ε΅IΊ\ͺϋ·>Ι#dί¦™Œ!t»?!χ%hX=όΪΣ~>AzMK7#> £ΥΫBΞς¦6βןΓ)ηί„Άl4‹h…‚&Yw/0402`jlωΕ/ΜΈp- ΣLm₯rjΠ5Ÿ|9{Όώο²C²€Ωμ{ΣRήT’*ύZ[ŒσCΞωK·ΛΗϋ¬χ’Ζ΄—T£ΥΧ|$υξ•τHυϊ ΘBˆ΄₯ΡΥψυ‘^Iή΄`oΦπΙ/^ˆK₯#ߟΜΥΞ `uB!LΑΪ•¦Z-ψ»ΏΈƒ&tFΐχΊ»,Τ₯ΏœE½EΐοτŠ-ε:Ι±>Š ‘ζ<~χwKΧω§Χ¨η-„8ϊšΗΝ'1ΥΩVnvάφ»θ6%­-vxϋβx€ώ …ονΓ&6Nu Μ=aΉOY2 Cš…4ΐ6ΥΦšύΉ7sY§οfτAž—aΕvΩΛ μΉž—ΙRξ€μKω©ήξOΟ͏ƒQcaIV|Ÿ8 yK4±΄ψυδg›qC˟ˆ]Υal™πMΑΏΡϊm_¦7­ψν/^Δφσ€ ΄ŽΓ)”‰Ή σKkl`κ‘飨V-σvRF aήw€`θΈρmί΅λœΝuΫ|ΛΨJΗ›v5ο3ι]»1ξρ»Άq©YΏp¬ΗaΘHFᡦšΙ6‘™ΗMg‘φBŒΰ}Ju;ͺ•·}©ή΄·μηΎtHJžΣ€*~΄MΓ(΅bQ%24Hi«.ΝαZMΨsH9ζΏς΅†.ΥIρ;,`‘η[ϊξ>`΄¨σ]Ώώφο‹–r§₯M²sΦWζώ˜΅Άη_¦*‡4ެg$ZŽS`΄qψ5?ν]y‰Άο‘Ou ‰γβγοŠρΫΪ)Λ›Φ|σ…ν|υΪn6} .€NΙ JΡ «i<χ<ΐ, ‡LΦώηŸΫΑκD NC:~ΐΎ~oίR©{°ΏΠ–g§c™Έ³²?»cκΠ{bL/Ϋ/Πλ$έέmz0υ­»lλ"Α5iƁ‡TύŽ/"­‡|4:XνXΓΝΘ›\ޚΡ8ii’ψό-Ψ¦b@|βM·BJC8<@ rΈB­H© ^ψ­―ΞωΒ3³4ΦΙχ†MτΑŸœŽΗ²’Ν~·@εΎνn©wDRμΦ•ˆw½σ ΥλΈ )Ώ-ΐ€Ν2νŸθδ|ΓΪΡ›oΗ%ή1ΉzˆB<Č-DΔ{—6žx\εκΑ7¬‘Μ›|σΕmlΛe?*tΚ€5±ΡεΔ!› ϊκuuŽή©γ{ζκ@(4ͺ2(­¨††Η?ίξ2ύ¦! hpόŠ…-τ•:5hH»?φŒƒ, Α*-‚ΎŸ’-mASώξΟχχX’ν=Π)ΡΪAŒ½ω>{—_‚Ά˜*K―IMž€k@ϊFνώπ&5ϞΩƚJGιDϊ±i¬†"6 P₯†KΫX"<σ¨Ν†ΠT’ρ0 =ˆ“u.^ όνŸίF»ΡΊΰ{,ΑΕ‘}ο’렐-Ϋ’ξ'2ώΎ4―VTt•tορ+ΔUύΏ₯ω†EWίw.θ₯όb(Ψ)=ΔY΄‚›Ώ–ΪfΊΓ|χZΫh2φ>p=ΦaolZςD†Ηή°έή€Ε@.OˆS΅ΛqtΡvΊˆδΒΐŠΨ$Δμ9μΩ€Σ‡”5¨Y Jσχ?ΎΓΗή9ΰ‰Ϊ)|/pWŠnŽ©₯–,φΩ‡Ύ£ΩΣzΥκ"μλ.τFUή}σ―ςuPύ…nsx]ηώŽ΄zί½Z?„ώΠΫύύBΎXςKΛquΓΖρΣ7}f»Ÿo=ΓάΉ9Ά±_d‘Άγ?΄‘Z{}U†7+oJ`{―n{άηK\ΐΧ±Η}»ΫΤ YU‚2 e4a₯ΐ©PS; L“λg Α†CΓώχ―4η<εtaA*LΜκ§ό;ιzΆΗDnfμK!ΌRΎϋNRήTiP&φί„τΜυqιύ,}ΕΏΡO&Tθλ_Χz Hή½ #–G‹οJ₯ΪΒχΕ†<–,ƒ~Ήμ7ΒΞ±~τδMa3έeΊy‘+]Oί«RνzqΨGΖ¦|CςώϋεMiκ¦G©K5·Š¨ΒD7}κγQ‘ό¨y€:uΖ-4~£ŠιΑU %jΕ’‡– Ϊ(~ύ«sώω'w␇‹ΐ_ΏHΪΗ6Π΅0σΎaγΨƒ―ϊ,E„­³ίHΓ>SΉ}˜_'ιΌ«Y=ϊΠΉ*oJ ΐϋ¨):χμԐXdγD™ϋΨU₯΄σθ‹5 H₯ k%baΕbζ:Ά/ΪE―‘(5ύg·xc8m.ϋ\§–ZMl’>Υϊυ7θΦΦx'aΧχ©“{Ϊ ά Ά_ˆσα† Ώ―ήβ:ε€P#ψ.ΣψtΜ½δ ]*°₯ζp/χΌ§Φά‘gβθ―W?5zϋά7!ΈΕ˜?)­T[ΗC‚hhΚΥC©ηί/oJ@wpnO΅Aι6¨‹π©•‚1„#%αPZa^ΪΓΎ4‰ D'Άd»Ž½’§©MΌςO\Ζ5.ώ8‡λ{Ύ—² ]ΥXΖ€:Oΰεv―~jΰ^/Κk°?ΆοοόΩ€έ\zoS7ZΓύŸŸ)*9[W?‡y=Th=„~‡θE/OZ6 $’" ΎzχΎs•ωΞΥΨvΞθtœΊw=o2CάJ³zμΞμώπ&5E‘/5γȍU s΄–š\ZUμδF°€y`DZΒ‘*z ιsΥΨ"MΐO’.°2гΧΧνZ_Λ€ό!k5ίρ_}/ξ½βJψfΞΦΉoΖ‘θ)›€zٌΨA¨“Ό§©η¬{薏όΎyS€ρ°h-xή]D¦Vβ4`ˆBz{Š­‚&Œ-ώΤχπl€¬0²]θβηkΏυdΝίψΉ-TpΡ4 ή9\p ž€—λΩdWΠ1Ώ₯δΙΉ  ΐέ@ΪΏΓλ}ʞ·;}ΜλŸί«ήρμ6΅3ϋθΉλϋ›z΄`―@|«μ‘Ν„}AnΏώ|‡―σ›Τ¬―” ©e7SJ‘K N`cωΰΆtγ»ψZErήs«BŒΒ§>‚±ƒ°Δ4’Uνg[ ϋφψΙ_ΫA…WΧ8Χτp׎φΑ'YΞt4v³Υ³jρω>λ-+–˜Ηά·sΚ­2κe~ϊi·ύŠoRαo°ΛΏ₯οxΩ½š‹ ϊυwσλΫzΕΏHΞ―…ŝΏύœ.ώω_πlKL?kΰσO_ΉααMžgΊuTπ’΄κ9v)ΛίRΚΣωaνδγo(γοεδM™8r`@‚1½ΕΜL»\$ 33^bL2 ?4Ψ]‡ΩnbΪO=«"mxβγοžθΡdUhώ—οqq3πŸόΫkΈ†v·0&ξZkŒ˜ΤJ\‘tœήΪήη‰— z1+Σν4£Ά†π:W8»‹ιfW½Eho±€7rυΥ‹Όο-‹Γ@λ½-Ά/<‡±Ά­7ˆKέΓ*zv+ΪBM΅vˆς6υωΏYyΣ€+›{όΧύ')ν1M’Υ(χγPqQ΅DšF£”`Μ!ώPS₯¦xq«Y|‘Ρ£¨S:D…³SŒF¬Vόάgg<ωRΓ_ύγœ8.b$2΅`ЈhβχΗκEݎΰΞ.}§θΔίH-}—ΏΑΨJ,2 _΅¨—{ΓΎέϊ†ΎΕgήμάŒτΞ=qσiQtί)θάύΤΛ¬ΏΪͺΟΰύ€λσŽί+o<όGΏ/ν¦ ‘Π`΄β‹ίXτšΩWžZ› ]›x!y™N P‘lΑψπΝӊo—Όι ΐώ ƒ‹—―pπhβΫη°){ΒyqQΡΝ―„`SΩp˜k zΟᎠ%2σΘ@£wτU”UθΘ ¨cVA‹‚IΌ t•[‘xώ’ηίϋλ—ωcί5ζΗΏ{•Υ‘ΰDb’7 ­γM§Ϋρ[)TQqχhŽhP’έώόΔΛδΑeί ,…W’ε|%—αU(πmΩέ_Nϊ)Nωσ”$ ΎUΪ ά―ψΉπ¦Νήt`[OΐωΆ?zπ—^Ϊ&νό³[£S|·ξžsώ©ί₯,ˎ1ͺ" ©3ρ'€™κ3ۊυίζυ»9yS€ΏόϊΎφΜy΄xDjvΑs]Ѝ Y.γRuΐ=0„&PΌΈ‡€4Lύΰad1Wζ‘-Έλ‘Κ }+¬”B¦_‡ΨvŒŽ[ώ“Ώ²Γ?ϊ•~μƒcώΠw­ςΨœ h0ΖΖ]E”(t ²r+t E#J₯±d‰ΪΌP.ΈοD[Ι1π·pΏΏ₯ΌΚΧΏ‘Jώ2²Ιύ²ςw»ψB―χΎ~ΑOΞΪΔ²ο~%`¬ΘλcˆπW~žΌ’ŠΗΚhΈΥάΓ ΆδΞμΤΰηžό,Α54ν%± qζbxύ³ηKΦO?ΉΙš‚Ϋ-oπ©/½Θίύ™ΟPiC¦Ω₯(Φ»8Z:L-eWΏ$¨– ‚ΰT„±Ε\ž£κ€}i?CDιΨ1¨Τ«Ρ.e΄"ΜbΗMΌpσRόάg'ότ§&|π­ξχσνOŒ H¬T‚ΌI»ΏŽό— A”R€Cνδeέσ…ΌŒQΰ5θκΞ0άHφοϊΉϊ/ηη=β=\μθΌΐ­ΪοώK·λ·=RΧRWθ~αόΏ_<FόΐB©c7©B§{N؝6 Βs_ψŠhD΄±‘4qkjί‚Gέ±}`επošΈΏ/oπώϋŸg8¨yJγλLyrΥ ‰s(@oΣHࠞxdUπ+–f­°η¦QΉGFΔ(‚ήŒΓ<ΊqZ³ ZP(©š‹V§ΤΒ矚ςΉ§¦Όετ€?ϊΡ >ϊΦΧο5"Ρ- ₯bη€ Ρ­Τ9N\`Ώ…T―ύ­o*Ω§ψΠξϊΨ—•?₯θzŠλέώΠφ}\`pϊΨyΗ»4#B³ZψΟžΪΤh+ψ"e›ΜΔ!•ŸΈϋ(^όςo1ίΫΑZKQUνwk1ΦΆ#͍1w<ƒ΅ŒŸΊΣ‹}CyS€ΏωΣ_ΰάΥ JDEξ}Σl3„ΕμYˆ€Š ΩD· V©8#`ΟaΞM '‡ΘΈΐ—uD(^˜ΰŽ“λ―Q`Š9?C”PΔτ’‰τa/(mΠVγfρ&DΖ‚2£ϝwό΅ŸΊΔίω…«|읫όΨ‡ΧxόT‰χBP:β€Φ)6T:Ζ΄θ„άι•Ώ²Ÿ-½ης°–>3Οέ°[o ³_Fω{pxΧM†Ί°ω/Αε)h+‘1j#ζ€4θY 5ͺ‰ίεg›μ]έ]ΰ½G{GU QΚ£΅NαI€{ηRZPa#Φx˝^τ—•;nœόδwc,νmƒ$ΧlΗ|³χΉ§Bk―©)hϊW§MΜ<ϊ<Ζάc‹[-Ρλ ϊ |AD r|€ς‚š8dβ’±έ«˜B§&€Ρηn{Ώ«»ύΧ―ςόϊήώπˆ?πλόΐ·­1hB›Ι‹ ”₯Q…ξςCχ¬ΘΒ?‹Ο§'ƒcώ–fνsΡΞ ζ9φψmηΪnΟ²?Ώο}T~οP~ϋωΐχiCγCRό¨ό‘*±ΡbxΈ>Jρ=`¬Ε;Η€Ω‘¨ͺΘόσ>ΞςsŽ’,cΨ‘ œ~;ofKΗ ΐί…'™Φ­£λŒ6ΰ5oφ0fΠ*?Ώ q: tάQΕh”xΒjrsi†Ϊ5¨υw€’ϊϊŒ,~=r…;> x~‚š‡Θάj0c B©ωˆ*4VΗ1γή|ΠF₯ωξq'/KCp―=?εkΟΟψώΩ%~θkόΡο9Δ§F1.T Q*b(Π1FΌχŒΐ«PϊhΝi‰2ωqΡε— ˆΚuύ΄~‘ία9dΰ/+Ηδό_>αψιohΜ *°:ΒDβμI/-—_Ti¨8<m,!H$πAΣ΅φ‚h£iκ9EYrψ‘w£_CΑ7RξΈψ‰_z ct,€Π’j”)©gWŒXμœζΥ#Π&^“.”V-_@ΦJ˜:ΤΜc§S‚ΥH₯1f„΅"ΖαFCξhEρ΄ΒΘN ‡r_\Y–²xήΗΖ$¦ˆCJΪ>5V‘]τRšFψΩOnςSΏ±ΙΫς‡Ώϋ0?ς‡XGw1Ύ'²κTΞψ]eδ†―²oϊ _ήωI»ןΏΫΕSÎΌλgj/‘‡κ/2ύ2-8„¨όΟs—<ν/n‚^Oεδ)mΫQΙSϊΞ§ΠΝK I< ψnΚΆ(©g3ΚΑ … Š β}bŒαΘ£ο‘ά™ί›‘;j>ΜežΉ4‘₯5’-*ύ@ ™_fΈr •Uτ“TA–Y>βBΜο5sȁ F΅ΣĝΌΤ¨«&³Y#V‘g=q1l¨=ͺ ψEL n&nwN;(ΐ…VhΊ‚$y°‰ € (p.`τ©η'όΧ?ρ<ν?z‘ψφƒό‘γοXG)!x=₯;o žoΧρ•}^Υ‡φρυ£ΩσΞί1ό‚|+–_ΰˈΏ„Φ€„ΰ ΞQמυS ό+±ƒ”€qΰĚ•λ6Z/L‘k ¨ο―wž u,rΞΕpΞDcnŒFŒΦH~δ]·y¦ί­’;jώι'Ÿ₯² %q>ςΜM4"7ΫlΛ:‘γRη,«A‚BΫθ½α"}“yTf1šp ‚ν)5Νι1φ©mμ3{q’ΠŠ‰ρߊ"(‘ρ‘ͺτΤ£6y`βPAΠΉ+‘  €Š!cδh…φο―ΐΝhΩΏόΩkό›O_επFΑ|Ηa~컎ςΞGVΡ™¨υyT[ΈΕA^ΥSίϊErc_wύΎ’§ΐm_e\WΨ“™yώ:nbηžΩgGξY,θρΏόΥ†ΏσɚMk>‚ъPθDΜκp―™š{”‹ °Φ§6η -Δ΄σzNU ˜O=Ϊ΄Ρœxβύ Vοl…ίΝΘ5ζσgβ£SΟ€.S ‚aΐτ2εΰπ!νΘ›ξ#νG ³•BΝ}ŠαH“ƒTŒχΖ?ΆΈγ΄‹ O€nPD ξδ{q†©¬Ψ˜ΐr/­qΰ«06ΣcŠQ΄A₯A>₯t:μ*6w=πΟσχώΝY:6ΰΏγ0Ώο#GxβτJLC†lŒŸ™έ‘Ϋ%™Tπ-”Ό΅Δ}š.ν{ΪοkΔ‘cRcθ^z»~[­Gon#½–νmeέbύ\©‚η3ί¨ω;Ώ1γωk‘Ω©LΌ¦btdζx?$Ew<ρί@ ΛtΪ=r4ΞK0Ζb‹‘,+”Φi”·ηΨγοa°z{ΗyίjΉcΰΜε]^ڜ2Κ΅ΦΔΰZ[”)ί μ€zzjxφ…ρί—«˜c ™g Q 9u±4x`Q₯ΖΠM@†uqŽΩi¨·4c]{¨U{΄Ά"}˜Ž<"e‘έ†P{l©±’Ζ”₯[UY ˆ‰Κr‹ι=OL6h …\ΈRσκ ϋg_β#ίσΎƒόπ‡σήΗΧ) …O ak HήPK„x)EΉξΑβον?ίΚ•οώ•}.}χΧ~ ήφήΧ!ϊ΄JμCXpοsΌίώτΒ„ώγόΊίψڌψΙΟ\τΨRaLάυC‘‘΄ϋ«YΉ|4­mUΉ0Œd|οiƒσθσΜ\$ώh₯iκc §ήρAVΎq=n•ά1π+_9Giuμθ (k Ρ`,„e ŽfΆ™PίψΎ~݌JάoBμ·¦t4μβΣ™Y sΎ<‡™ΗλζΉ=‚€;X’―ΦΨK±ˆHo9”K[“UI˜bΙJ T–0uθ θRcP8'`UΫΉ:Ύ7…,HΥ‚A i„ΪŠTνθθŽg.ΝψΏp–ŸψΉ3l¬Z>ϊξƒ|ο·δ£ο9ΐα!τ¦ͺξ_φE ²Έ/η?uϊ,t/_€Τvreo•Όk±}cQt6JϊG¦Ζvωϋž’·έ|ϊ˜z»}Oρ΅Ά'Ÿϋά”ŸωμŒ‹ΫkΆΤ]u?‡β…Q^’·ηR–G«Ά2ί΅Wƒ•B7»G"²Ÿ {*FΗrρy@5-θΚcu xh£a6kΠJsͺΑ ]‘#ΌύU~ΈƒΰΛ/n΅ δςY£QΖ B‘„ΰPΆbΎ{ŽΑθxαφ»Ί*3^"ίήκΘΠi‡ςs•Aο4˜ΝΥTνahπnΣ‘½ΰW)ή&¦vLΐF{v9βλ1―«\ˆή‹‹?ͺ2„Ϊ\*&’w’CœDiμV›—Ϊά J&9B©”ΒšΨBϊω³Sώώ™)η_ΎDU(ήzz…οxη|ΫοydΔƚM-²Ί―LŸΏ?žΟ7ΊZ47Œ&k;{Π €{5Δt°EΫΑ»ΗάkΧBn4Wίˆ  G#4΅π…ηζόςf|ς©9[SΑjA±+¦†u,·:Υc’Ώ|ΐV!K(4Ϊ zξQSΧ°iΦ„ψπ£mmμ:­5ΖΨt-W/]δςΉ3<•ίၷΌ›γΌ-ΦάE’Dσϋήyη_όgL›Dύt!0.€χΘά!Νi&„z?½ΚκαχbΜ(oYmό+) ιϊζ@EB{‹Ψ@΄JΓBf«ΖΰVŠXΑη<εS;ψ†„e[‚ DΘ ϊΪϋ”p¨D›˜jl]Ε (9!Μ##L:Πzβ%‘άΡΫ4 ŒΡέ™I;¬ ½σάΗ­ Ωc@αBτ$NπξGΖΌο‰1ο}dΜ#ΗŒF±(Εχ†žφ₯³7ψ»βϊφy΄Κ½BάE]έ>ΘΧzq&Ÿ’€"ΠΤΒ —j>σΤ”O?9η /Μ™Ξ HTl ‘Ns˜rΣΎŒ€+•Z}λTςΫ‚’υDΠ{ωWZAθΔh<όߐβ­ΗaΌΊŠwc ΌkΨΩΌEDΙ{ΡΖpό‘·qβρwSξŽ4ΰ1!§ώ£Β ˆΕ" œGΥ‘’j‡4ŽΠL‘z?Ϋš!+ή–ήΕ$cZuY£@©FͺΨP’.+6"ΒF΅»”½4£Ό4gώΆ΅Έsψ€ͺγ./(TΠS=?k‡φ›>fb‘ˆΠL=E©ΡV'—]ܘ+ΞUcή%Ε„Ά‰ˆΡ ύoσΣΠ JλΝ3¦ι:$eqA˜»@Y*>:ΰ‰S#ήzzΘ[Oxτψ£–A=/‘kΏΖ"¨φϋY‰χ£ϋΖΆqCοΩμώχx A)Α{ΈΆνxϊ̌/>;η+ΟΝxς₯›»‚1©¦"Uƒ)­ΫϊTtσ[ό"‡…ŠξϊvΥ„Ϋ§NMN•έԎ“ζA†|θΒ°~βΟ(¦-ϊρ‘žMΫξ>*/y݌Ρ­Y?zŠcΎ‹Ρϊb‘7›άεφ*›²„Œ}„˜‚’. ¨'ρ£Ρ˜>Oυ)υŠΨνW<4Ίˆφ±Π¨Τθ]‡ΊVGΓ10ψ‘ο―’A0g¦ψ“Šgχ0“Τg0DOƒ26Q€;5Œ`…Y¬ T M"$Ν<‚L‘'&ο΅Q΅ŒtΪ‘pAπAQ‘©–λΆ‡Φs@|L&Ε cAΎΛsΤΤΓΜ#«±iˆ?> ¬YΒΑ± s₯†&ΰO ΡiΜ4ƒ˜ηΧ^PΫ”Ρ Τ;σH ήΧU©‚€Αβ"Hδͺ΄Ν*bMi£λšόϋΦ TΪΑθTjaγο°ΆTΡf{0"Δd„—.ΟΉr΅α³O†U©ˆ H5Π¬–šρΐ°6²ŒŠΥ‘aPΚBQδ™"x‰ηαΌb: LΗήLΨ™&ΣΐΞΜ±3 Μλψ]F©Xo•ΟECΩ›ίΰ‰)RIΗ«Hλ£Iʚ²™\‘!hE¨T,ζΙξ~ˆMbr)ΈT&ֈ_7O`‰Φ¨¬όC―ΪΓ½«IηΣ4@Π₯b*Πθ^λ$ΰΔα­KηΨ»zυ#'9φψϋ¬¬ί •{YΉ#`oζ’»₯UΚ§&uνE·]’μ=<@3Ή‚¬>ά¦Ώ  OcK°7›δ'χ[JP’03GV¬°QΖ`@Ձβ»„uK[Δ*š£Γœχ•‘zf—0°xγD@tμ?¨]μ2P*ΤΘ’š€qf&žPFτ9ίv‘βW]j¬κŠ$Εώ(€½9•R-’έT€Επb’,ƒd?CΕsΟ8I|kt—$εθM’V›λ°©…ΛΣ†«ΫΤ<ΑΠα-ِ”Η7‰<Σ„€±όYCšά$΅0½ρξ€γ§XF0O©˜ΝώŽOο ν9&#i: "ρχ„ h'θšγq΄— ‚™φšΑ&, oΚD%Wςύ ‡Χ³yCŽ΄d(“HBZcŒ‰@/“#„Υ,T'n]:ΛξΥσ8ρΗ3Π1AΊ ―l"φΊ” 0šH‹W\a! "ˆ3XgΆϋΓ•GcNHΥY+HˆΫŸJq£nAi°ρ»‚Q±lXΕ  ,ώΔsiNszάΖΧmΡΐDasŽ€Φxˆ 5ώθ}vŠL=2Žν‘”‰g] Tv]4UœGwkM/†N1rπ± Ivacf ΄7v«|ͺΫ³!^\ *τ―tk―²ΥΨ—)θ?Μ%ϐΏχ|~«χ‘[{Œ°Π«ρΡeΆιd₯?φ½ίξ\ρ™„tG£cΡNϋhΓƒηη”QΙΓӝEτސκqTHŒΏIάtν'‘σŸ.‚Jλ]ΧΒώ Lφ¦Ι°JJΖψ^›¨όΪljžΉΉ™–€wΊε2Μζ5F.y†νK/qτΡχpπԝοxG @Ή’‚)@ͺ‘‰€`P-@iaΦΔΪS‘ ‘ή=O5~-&υΧμrθωq{4©5W’λ―@i·&‚B>uθΡBX/1—η˜ν†f½BIdͺδ~ϋ£Κ§w¨Χ‹c*F£Œΰ&œϋΜ^ΔJΣ6ΪZΤΠΔ›oΗL@mΗj ½PH¬_…Φ*u˜Ν'α'Ήξ*₯#ZR^"‘Sp™(Τ%ώS>>‘ *Š₯ݘCϋUt}ψHσu‹‚ηΧθΌK£ΠZ£δΨEw™Ž,CΕσ *ρφ΄˜O2Ώ"cuˆΧ·2Pf› Ώήsψf«Ž8€IχB*Ϊh™μE₯ί=xμΰœ&$ 5j™€ψΦX €1&ύ½3δΑ{\πγc(›OyεK_ϋ [žγ·πŽΆ »#`₯*Ϊ\«$—Roβ΄_εΥ$ΧH€  Ψα!¦{Ο0^y"5`l―[όœ”ΖΧI2GNšŸZ€Ή"/|`λΑjό‘*Ά7Š0²±v?]Y70؁‘xj'Φ‘L|ΝΘΔΎ‚+zˆ}aBxhL(u€™†ˆC(« ,1—kΒΌFm±Kq ζcϋ1@™6­Σ[‚Ά&νήTλrΖRε49YΡz *OJ‘@¨l)š€Τ ƒNμΔΰ₯Λ.θ€Τ¨Φ;ΚγΥTκ„ΫGζQ3Ρ΄^†,ΜΚCuŸ•wχDS|ή*Δ$ΔGA ž8TZ&¨ŒU{/ι& ¦κ€½<#γƒD‡5΄KQ»$«Φ4πγίαZεWDl΄ΖXKa-EQbŒΑX{暍œ%±“šhέU*†ήlBΫW.1ύτΟsμ±wsθΑ·έ UΌCΐ™―’9³m©oΪ).ήπήΔ\»Ξ βŽΚ1nη,NM°2Œ;CNο(ZeΘ<MJ R?›Έ9‚žτΉ)~½ΐŸV,α’’ϊΪ6ξ`„.ρE¬ΫoN¨žά&¬π`―Να\™dΥΦ όΑ}i†<ς†co¨πMΝӟω%Β΅Λ +i·NΡS(c0©ΏΏ¦E™bυVlͺM…fΊω +‡ίst„;Q—;.tΊΈ>ζΛΕΗΌ°*5ndqλϊβŒβω=BZΨJΉ2Η^šΗαα ?ΆψœΠ<ΊŠx°WgΟξ"Cƒ}q’X†^šΆ @Ε ΊŽˆ΄€32±τΈΠΘάΗέIω AπF‘Χ5j§&ԍ&¨Ttd5xBŽMΣ •nβ6οoΗH§%ςΪ4Δ›ί…X‹o‹ŸΪλΜK ςZ£ {,œԝqi‡₯]R–C’l½‚:X¨65'Δ5 .†:Έυ=wθY@ŠθΪ›­&€‘!HΛχgh[εΟ…>i P! ’πϊ»]TώtjF«΄ΫΫτc ;,Dλ€…ˆM΄cul¦΅ΰ½j—+/\@γ!Α*ΗuΙ„T֎bh₯₯BΦ τ΅·^bΆκgŽ ~4Δ\šΖ½έ`ΟLΪτ’r‚·wxˆ½:G†ϊΑQ$ =Ώ‡Ή:'¬ΘΨF)”΄γΙΪΈ[@]£&ž°R 6ε0°ΆBν6„]‡r©ΰΙ z ‘ „ b{2«[―§χγ%>)tΪΝσ§Rί½Τ Ωtq°’§TΖAͺ"‚―Cς τ’ς·$€δ‚‹ζ!~_‘2νζ.fC”UHAc’/Z₯p'΄b"sSοΦΘZΉθ//θiƒˆBΘΊ%Tκ„Hμηΰ$Άr˜ι$ƒκαDβαϋίx萴₯:)ΏMqΏ1=—Ÿ<*©rj—ν™H*1G”ΖŸώ¦h”J…e‘Τ¦‰¦›zΞ“Ÿό9ϋφίΛϊ‘ξ0ΩΎΖΧλίBvΏΗG—f&ζΥc9_Œ—“ϋƒ#Μ΅ ήj‚n|L (oΡΥ …ίβΑcš/ψE6œΚ;™Zτ:Cb…₯œ’Φ€2„4fœRΣ<ΆΔΧ*μsόCš•2rΖ›>Ίς JγN)_˜ΰUψ΅y«¦xzU{όc+ρfO»‘šψΘd€!ͺPg&θέYBe"πH]¦ΦKTe`ΟA·/ε°(MJ‰¦KFΈ ¦X‹Fθ”Χ' v•<‘μσ{G•:’ρ> )ΦVFwΎκΒƒvΔyi,›Ψδή‡η0X™V§zh" 琷bΫ^όzΟ†‘±Ω*uHΜOΧ…8JIά‹Μψ£4P(”'££ρO)ΨΜφki%ό‰ΈVω#θgθg± υΟ€`ξc¨΅Ι΄¬|Ν@Ηyέ¨μΥ€SπΙ]Υ*¦B<ύισΘϋΎ“Co@–࢏'ξ\γΙOώkDB2Ě 'PG¦– ρ&P^4΅!γŠbζQ>&Ν΅5hGd‰hKcVψ/~ Ζ[Š@2Ё¬yWΥ^ Nΐ\#˜Ϊ£%*Ό½σ€Ÿ8όž#L]Yg±Š00‘5™±iŠΙ‚±^ΡΠΰ±`všψϊaμΠ€B$TΙΨΖτm"y ±Œ·­Ωq1ξίqθ=‡j€+€"b#’&ͺNz½ώsΓψ‹?μ°&g%Tλζ[k1Ζφ:5±ΐ‡zπ…j―W΅ŽŸ₯E8“²:z1m’†ΤFΗ7>ϋ\zαι»ΫΤΣ=Ύώ[?ΣMJ‘tObjΉωήΜ…E V+ατ'*g"cμ―£²mboΐ€Ψh•RΙρŠξœVιšΐ΄¦@§4­"em’‡+ν€Ω.#’b¨a-ίψά―πŽώθmo›xξ‹Ÿ`>έM Ω‰ΰΙ»Ž¦šΆΆdV‰΅‘¨Κ …ΑμΜQ.Ίΰa\‚R˜™Η+Π…βΒœ\ΥόΩΝ9Ώ©xϊ’M–Y:RεWΔ8R§Θ6LνΔJB¦‚$–υ†C%ακœpl€ž8Μ {˜Κ fwrˆ;P!(܁’ςς½SγΦKP?΄BυδzβΪ”₯;>hΣj0ηfX©£3Qj8XE…©bXc–ΘΥ:z(Š3 \R¬±‰œƒ@ς&tτ2ΐX(¨κqπ‰ )#’ˆΚ›Β “™;οj€?›^hEΒCO}$μvŠͺˆyό„?¨ΉG5>±<#Ι‘޽ηΰ@Χ±Αo±λΔ£§s­FJ/5 -n£Š‘Ϊ₯¬ΪXΆ›\{•]{Έy ΎK1gnH.~Υœ‡Ώτ£ΎkΚ’E·Kλ΄q©ή.Mmv€ΛΆ\O€Κy.ΙSŠ" &ήορ{’tdΆΆ)šd\žϊτΗyΟχώΑΫkΰΆ„[_δκΩηΪŒξŽM.O\Ψͺ0Όγ mγ…ΆMSHΖ0ΫXaΎk«νζ,r± ƒΨΚπΒvt·|Pό₯οΫεδJ“Έτ‹ Μb=½J`RμC κw:ή|vΟ‘η‘Η―λ›J,ΡΫ ΝΙ!ώΤY‰|ϋάε‹{Ψ½¬&`/Μ"%νΜa5fP©ŒΈ4+Cš•~£Β€ά©!ξΔ $¬q7―4ξPI8=ŠαΙΥ:Ίϊ‡ΛX΄΄^"•‰yπ΅φ"σ £β UЁ΅δΖ«ΤJΗ’¨ΐΊ–Ν—Ž‘΅"’ο:–Uc#Xͺ5Ρ@@ #’sήb5j€cΘ°^F0Ξ€Š!KszDύψ*αψ¦>ΞhΤ*‚C%ξԐϊ‘υι~­ΐμFlDV-zζΡΙγ’& η3u¨½ˆd"Yτξ:–‘Zώ‚RΠ8ψ“ίxτhbΖΐΎέύۍ«·s΄1}Ώ 3χlσ―ϋ@΅ρΩVιϋ"Ϋ³{BΛ56‹iζ3žύβoέUΕόΥΏϊWκ­ό@ §ϋγΡ ›ߘž bΞukκωό₯^VT"*,F£œ‡$ΞŒλ03iZΕVUΑ‘σmΗiΫG}δα=>`{ni9,EcxόΡB§μΓ<ν.=ρPucΎ) z³ŽJ0²Θ02υ₯4ψΚ<Qs«K…Ή:Ǟ™’vΚΕέ]½Ϋΰb<©JExΟF{!3Νuν*η‹₯έ›AΝ"ε.>Α fΟ†1…‡Iw•©,2H.τ4Ζη "Ψ©ζ©f>ύ››k„B§Τ$Qρη9P΍a“šΈΈ#kPE&μ(τVί³j a¨[/$¬„5‹½4O ^Dΐ2ο&`―ԈRψΓ%@4€•‰…={³λ’ϋŸβ}eϋ.{²ψ!Ί:“σπCο όαBKŠˆEQ™ήkŒΑh›ΏhΚΠ{ŠΫΩT*ΚΖ`ΡXμ―@ηŠ’Ή="RKεNa^7›l^aύθ)Κ[ey…ΤΡ“Ι}%ΔoΎ”Κ@HΌ·œΎ=ŸύOΝeu’*k΅θϊχAλΆ©ΗŠŒ:ͺZ Ί‘6΄·ϊ>ΙYšE"¨°Uh±ͺt]w―^`oσ2γ[Ψ\δ–†ΫΟpυμ³ρψ3…RGE>Q₯PGσ―³:π…‹Qyu*«1ΞGv—Υ˜½:*Qjξ V#EDΣeTπΐŠπΠz’ΌΆ&Ώηδ”a)|ιcθόŒ³τπ¦Bv)jhbιθŽ‹[θΔ£R TGΔ‰ARƝNo7±©ˆV„Β΄£ΙuPΫ1eh7η)U8G'D]Ζ–°QD€πb -z,65 Θ(5,9PΕsΆ:ΖΓ{5ibά]GŽž4θYΘ€ΘJCΉςcI;Ι-—‘ιϊε7!6ζ¨ ‘2-ιGJE1½—ZjΗT]d1ζVjahγ8YΙ¦}μ:¦S}FγT‘Kq)Ξjp'†°bcJŠ”:/ΡVsj£Q₯ιθί9ušͺ(s/vηᏧπ'>β۝_§’ψώΖ&ΐ˜ΤΊ-§η?+»‹ΚΏoκ,€$p[ BΎΡ”D €nK’«cEξΟφcu\ߍc§o™Έ₯!ΐξ•³hmRΣoYΥ-R.ΰVkΣψ1‘NΉu!ψP~PΔt•Υρω½&Ίau$°`RYŠΖσΛΟV†%Ά°­ΡQJшζ{π_}οΖΓXwGz„!Υrν}[¬dΗuέͺͺsΊϋ>ζΞάy’3‡#‰Ι”(9²dΩ΄¬Gb;‘#8δ/A°ό‘ψ#  Ĉα $AΎό™ †ƒ‚„ΐŠ Α²-K–BλIŠδœαΌξάΉο>§ͺv>φͺ]§{†s‡"5]ΐιΫ·ϋtŸsjΏΦ^{οΜσ”Σlφ =ΑMηΓd²Χ§u.DΨ₯ΝΆ‡Ω0SιB#‚Τ\ο`CδyƒΔB‘‘l3xe³γρόύ« UœprŒΈκ@ΛύύΛπη–αο]-sΉ1ΐlNaΆ{Έ+ϋrxV8Kώδ΄"­―'œUˆΛ-ΒZ‹ώ؈Β±cε*eΊ°†Η©IΛ-š HJ€Fα8x‹VμΈ+°›¨±π'Ζθο[FnαΤΖ"‡Ψκaφ8݊ΦΒ€ΰnNΡ\;ΰφκRˆEžz‚ι_C~±2|‚K -ΪqZ: Ώρ‰ˆ_~/[ώ$ΤΩΚ‹e·P3³21ΉΚ1‘ŒΡ‚―R„•μ‹p£*ΧGr™y½Œt™όΪ”aLμ‚Ν—/¦Θn°{γ 3ώ…ΚτHβœ4g¬§Y―Η–-~κLΔΧΉΜ~³!"XiεΩ½‹­cΰωζΫίΈ±Υ΅˜Œ'Ήtr‘„sG<~λ—γχΏ½‚ΟώΕ„1€H₯—`Ύώœ‹5$€c„σΆeJ.Y“-'IΌμœŒόΠlvˆk#%°trΒ•#Iι9Η'Ž=ΐ³K ΥΜζq•[—ιAh", 'Η<›ΰΪύΉ%g˜opεΝ͞[‹β’Φδ¬@ވ ӍΩnv°;žΓ•ε&χIΰnΉ1ΙΗFpΧ§ˆΞrΟόŽZΓ‘‘`$>’ΉrΐaΜj ’±YqΙ±Wa1λ#ΈΟδ§.€Ž΄LyY 4AhΌ­α|Λ‚νzN ڎ[)SH _@ΊψJY4€8"<πΐIΒ?ώDΔ± ’-Β›‹ΞΨRΩg…Ρ#œZv;°ψfΎπg«/J€R§" ”’廘ha-»‘1rθ€™ŠžZδϋ)¦{Ϋ/9™=΄€bΐεgώo¦ΰrz#)6)€B!B ά³πΩgRL­pΔvqα μ^/ΦOn„“¦­ΓϋοoyΌTn^ς΄€Α£g~ξm=Ά;‡η7\~ 9΅cL†97+σγjυcv8lH“dˆ@;\“`…‚Γ…}Ÿ…’¬ ΥΫ=ƒΗΖpI(˜)wv»LŒ±»žΩŠ‘Ο’nΏe - ΒΩeΐ8i€AWxϊcι›?q緌GΨ­n―ηΝEΰ2ΪΟ,½φfυΰ°Μph²ΦrΛ~€ΫμαΛ\τdφ₯΅»΅0AZr…ΘΦΊ‹h_ήg/bΉA8Αe―v?p›.λ2u¦‹‚ς ΐΫIFH „Ε•0έΔυΣ$|ζ―Ζ­Ι)ΎβΞσ=²0%=-\”κ+(|IΩεTΰν„Εβ'„?OQJŸλ)I$ A„β7ΐςΡ“X:€€‡ζτΣ=ή,©ΙJ›$; I”XˆŸ{Χƒ·―y\Ψis*FAρέ%Λt`2 )Έπ?Ύπχί;Βχa“Β=&Ĉ•ψ{οίΕ'ίΥΰχΎ9Ζ—Ÿv…8gLž2”Y˜΅±'„`ŽCΙΐJKp8€ιG#ωδ1O1ϋnί#z τ–§ma7zΠ½@89FϋμšKœaH”XqΌNcs}Κn»lΙΝ~ΰTΫn€»>…?9†έυh.νq[³6Ε©©ί=—ΣΖ»πvΧΓlυ°7:Έ”I<’ϋߚ¬Dγ"­v£γTaΙ!œ]f£2‰kρΩ °ΈλSnΓΥXxi κφz˜Ž³†―•(d'BΨλ’πˆy ¦χ₯3°³yH‹οwΎψ8}d2…7Ήν,c\ˆnUœŸ^Γo°9'hρ ’g; όUΦ^Οrα§ςKΩηκύY-Hˆœ ―τ‡‹ώ`ο°Δφπ<€ώ`›—ž-θe*¨0ͺuWΎHe #ΆgW">œK!]Ύΐ€ϊδ“γΚ14±qbY<σΚ₯šο‰³mnT™S/ε$/dάήwΏΗ‡ίΘΰω ƒ˜λ΅MޱC:I !’'©Dcα°Eε3Xμ ¨γ˜5bΞ€Ή{vΫ³•ήυ ȍΔέ½>εb‘c-ΒΙ βϊ΄,VŸnc w£‡έζsΖΘ‚ΦZЊγ2κ‘…ΫβΒζΨΒBK©PIi‘Zt£Ύ6B\kXΑN+©υ—Bw\'`χΩ3ˆ«L{ζΙ\£Ÿ ͺ‚Œf? ΉΡΓξΨ=VΐΐνΈ=/Jόε¬εΨΧ …ΨΆμ†ΔσΰηJlLΒjΑO ΗWΏφW"ώφ‡“‘²ϊΖε,‰rk{&~JΝΛ7Άπχs?ΐ ήΖς+χ?‰= =:΅UΟ‚žζR̘ΐι˘'Γ‘oŸΑκϊ)¬?s( ΰΠ<cXkΌ/a(‘nOwžΙžŠi ήs―Α£λOίtΩκ<³=:[¦δ4V,?gβR 3αχwΏπ©ΗZŒΪψ—Γ³—hΚ :0σ«'ƒεΆΗ§ίηρ+O8|ρ{-Ύπ”Γ΅-ƒF6—5A(³<ά‚"Α{‚υ± Λ©Dη,Œ”Σ΄ΞΉέΦΐLyτuμάvθ»η&rk`ϊluθ+Ÿω'¬ΘcδωVR~ »μ $Ξ@ͺωΟ!Εύ―RψΣ”ά% 7ψ§™’¬§3QρΤsL<:<μώΠ@;Yζ‰­Ζ‚ΚπwˆT#͌Jd}~υ _ϋ‚Γ¨M^»IΦG™οPγ`}Ο —³0Α3{pΩ‚|ΔοόQ‡ςδ8ίc |oΰƒτf‹ά#š_.Φ |δΔβ;/;|ώ©_^bQρ )Œ3yzOŒAΐ˜ά_ΰκ+?HqJ’b—χ:eβ^Ο¨ϊΔ’Ωθa£ψQ¬΅IͺrΉα½kSΨε³Η…>Ω½”88q ±ΩιŽ΄μNzξŠk¦ά,Σbχ9r6" 5M‘„qhχ˜Zna–85H°>’½6εT–xٜ.ψώΔ“c·z˜+ ފέσ ]γ™±˜xυΙΝ‡Τώ'ͺxNFφΞzœZ#|ςg~L¨γΉςn ό&½]΅)ψT²ώ%Φ/±=€Psη Μ3“ω§(ͺˆ©*©xšdAΙ}ύ ₯kΨ{ xh ΐΊΝx }wΐ1D»εώτι4)q§K¬#ηqώ¨ΑΗΞχψβKγμ)CeΜ“2ϋΝpκ œ—Ά! Ά Œ³ψά3ύα€GNΆΉ©Co;Xοα{‹| "q΅`v Ή9ζC§"~ύ£{Γ=γπΕoY\Ψ°šŒ‡:αŒG’άΘ!PR ά(“’t¦±"XgΞg7œ³π°±ƒ_mΉƒνnΰξΔλ#&$y&ό€]ZqpΧ¦\Μ”Σ$8™θ`Λ£ ά}Ηm{ζρά`΅ΙνΗάΖ”Α<Ο©΅ΨXΔ~M© T£Θε5tΒJeŸρ‘Γ—“γO05~%ΐ.9¦/oυ\ΎpΏΓoώόS/Η½οΡχόγ}`o $ν[6IP„ΆΑΖßωˆΨΗ,ΤΞΩ<[€E—Θ―"πkC0X]"<~?πSo‹xό<iζγ₯<όawcΕ£kόK.›{eι㏿+Άκ*zoJσe€_+ΕHόχb Ωϊα±ΔJ©€†6Σƒ)ύΠ/aνΔαd]ΐΥgŸΒŸΞšΜYi©μJ­Ž}ψ=ϊŽ…!ΰ3_ZΕ΅žgυ‘sς?σΟν~Ο1¬t”mΆ§ˆ-PΈ‚π»~η―-αρ·ŒΩB)‚ΰα=fB}Γ΅(Φ’πΝ) ½ΊeρΤ‹O½hρέ‹WΆmc1JŒ;Px`-ε^rQ,%4Ξdzt!Eρ(.nοvgω!Β4ΒΆ†›zJ/}j ΠlrΕ‰;ι‚”ή›&δδH7aκYˆœe yΗΛ5EΑ”t«Q„Hg’σΪIqŒΰό)ΰo!<ρ πΰIfhFBΙ΅›Y n₯7)½—.F:4Β”ΎλΓ²O‰υuΉoŠΊ /!έ· μ™"ό τ,?ΗE!dՂ$‚Ξ‘Q―9TJC €ΰΪ₯U<ώαO¦ΈΎˆΑγ™―|}ίƒ[o³ζmš–ΗΙ²5&b`W¬ BŒμ’ϋΑGάΨ#|ζ+Gy”—³BN—Τ—"d™> °εβμ@D³5EYό—Ώ±ŒσλMAΣψb€χήχY $W,…+9£ 6tnδ˜βBe-Œ‘~ςΦΰΪΆΑ3W-Ύ2\Όμχ\¦/†EUI2βλCDγl–τ½­)^…Kƒ>΄"€ΙYŽUot=$ΆfSXP瘷ΙόEBvgPnύE37^GΨΚ¬Œ‡NέρπYΰΑΣΐς˜η,6CkŸψ•ΕW±{VΦ‰(€_ξθ“S|6ί£r Θ΅Τ3 šͺέώZψ‹{―σόιu‚&λOυOΤ@ ϋ§°¨\Ψέτ|πpμταΆ ?t0pι»_e£τVoΈ«― Ždλ«‹CDο{t= #…ˆo]ώωwN’i„&]gΞ³RΒj<β€eΟ`ά€ˆΠμ2:mFυ——pξ˜Nzd–Ι"ψή{υ{Μ^AΜ: ά]Εl1Τι5Φ²UΡΰΚΆΑsΧ,žΏfπμUƒ―›ϋœ!αQ{lQ¬+.maOLŸζS ,tΈ΄<Φ PΜ> o±ΙΗT{«*-―SΕΪΘΖXPoγ0WIZŒ[Β}ΗΨΒ?pšπθ9ΰΎγgλ€τYkŸ@ς$°©©LΘ‘+κδΊ%ΕX ½-uV)JTχ3Yt­θ’žT`ŸζόΟ(=Ο@‘A ΛχLǍ2(*C“\‹(_"„€Υγχβ‘|ό°EυΞ(xρ©/cϋΪˌ|vΏlS*υhθQ”L@iΗ@ωŠxΉΕΏyαš‘)ΰ$+"’εςaC€ΫšfΜΐτνΝDgΡ β?όΚΌυd›3ίΈHY$,`¨4 J”aX"M͏Kœ«Ÿ36₯€xxφ.ή°ΈΈipιpω&puΫ`{θ₯vάIJ‘{'/GRg,xCΎB±(O%©Φ› {:¦<Ÿ{θSκ<Ξ1oΣD[Ξ58{œpφπ–ΐύ'ΗV œ‹X&Ή(I-όΙ›BΖY’§etIΥlδ{ˆE7€τγΧ¬ΎUŠΕ&w_a=C$pΉž‘‘₯W)<2γ"f`φ‡ KΓEkfζ |~Œ<ρ±O£ώΨ°;¦’ορƒ?ύ|ΐC/UX€Ι㦓ˆ’½xΣƒο9ώDψάε ώσΥShŒ„”ΰ$ “ζ»7ϋΝΝ)—ΔΚ”^»ηŽŒCΔo=ΩβCŒΙdAɞ€δub(―π‚ήΓΖ Ÿ ;lεΗ}ΎHΦ¦θ’ς˜i¨ΒΠ‹έ)°Ήgpc—ΏΎlοYμvΐ΁ΕφAΔξcu=0ν™Ÿ ΅Ό‹΅·’™m’X¬fc2VFΛcΒΚX›ΗV€cΛ„γ«ΐϊ*SoWΗMΓχ J³γ4’Ό(ΜΉƒΤZe‘mγη]“`1R꬏Π_£ό™]Εϊ?€ŠΣ‹°c¨²υ€%gd•꽁ΠgΓ‘„Ώμ³”2L©Οτ»οz<ϊΣΏˆ΅“χά 1½s ¦{[xφ«[ϊ²Ϋfs“Νͺ* „€ΎοpΠuθ§B`7ΙΒΊ²Œtυ4œα!™ΰΰ 5υ‘7ŸΫι€–‰˜>ΒψΏ2΅ ϊ_}ΜΰώΜΡ”‘βχΡΐMΣ±šR½ο3pθ=7ΣPA+Ε` ΐ6Oθ‡ΦqψX  !I#Œrγ…Π$›ΡvΙcδ·!ƒΔΐLRj,ο,ΑΒΒ:Fže7ζΜ^ ωM>·Ω¬Σ”gT„}6Ώž· υiΟθBZqS ’°)+Τμϊ'CIΡIί+Α‘΅KŸn[‘φ†όMŠΖΚχ κš$ΓJ{+‘Οb5-8Rΰω|η؜Ι~τ}iœ>Ž;%’wVΐΑφ <η'ƒ3ΊεRφRH ‚Φχ=¦έΣiίy1-%όΑυeόλ‹χ’±DΖDXΓ}m p‡ΫναN`¦ΝNΗ7%ϊ#cΆ°Wχπψ;–ρΫ[ΑκΔ‚’Ι^HNηh%στLb β „HEΙ[(δŽλ‘r%‘,ˆz"EΠ―D«BΥ2θ˜ )^±& MJRQN!z?r—«τgCΥ³·@κσ!P+€Ϋα •Λ’fΛY•{―-ΎΝυωI©Tœ\o(ΕdY³ „Θ‘D “”ωɐʬπW ?φιά~vά Ν«œρςΚ(― ήγάΫίƒsοxχΟ;―`kΎρ₯dP„‘%tL§‘\0ί£λ’`@0]]k€oνŽρOŸ;‹H€…1‹Gvκ[.*j7χ–G0Σ E+nλqΤΐΆμc|ς‘%tAίd rxc²o%Ηq‰Ν%|ƒδξ%ΰΣž΅Bъ "Œθˆ‘rPBͺV Ž•.L:…YˆκωΑα++?ψLS Ώς”{9B―έ{γΈZ§ζ S¬6£gοKΎ7κΣu¬ »όšέiΤy cύtΞI,Jh¨²Ϊ[Π ζ½nΰIJΌ)y.³― A„νοΎΣ’ωϊ(˜ξήΔ…o| Αχ•‹Φ4…—β9|ί£λ:L»}η…iΗΧƒήα7žΉWϊ ς\!Cpθαbΰ"c`§άίm0‘Εɐ½αψ2ϊΥα»βκ:ΆΞ%m\1Aύύ3¦2/²2Ν†Y«―Ύ’Z,)>MμΡ€`°GΤί2γ5²ΰ—tεγαxΰ]Έ£nu_/~ΊŸϊ2Ί½ΞX½l‘fΚ `/€ωϋ]NJ€γ¦’YS.ψ·Ÿ_Η6b”7g€C€\lϋΘψg p»μnώδ2“‰ŒAΏέαgO~ύΙ5ΞΏσύ―›μιυ#Sϊ).?ύuμέΈ¬*΄ΈZŒo|ΰ|ί£σ^ΌV 2’φqΞbΤ8\λώύσGπ›«g<κ€”˜2’f§τ²—ώ{εΗ 1h6φѝZML©Α4¬7ΐΏϋ πΎ3ΆXZ Hr¬ŒR(w;έσκwΉ¨<‡’%ζ{r r¨ΩΗΩ1Qͺ$ξ^εΖ(>u₯ *W{ήλ’λ=x.‹k%“:‰X=₯W κ\†ΦΏŠVfΕ>]•J‘LցϊΌ$ψ)ƒ£Ύ ¦ϋWξ )ΑOΟi‘ŸΑwΐJ­MπΦχό,ŽR{―Χ²~€ ­λ—pνΩo‚‚W±§Ξ ψRΑη EWS?υΌυqcqιΐβw_ZΕη.‘±Ž"’qpΡ# D‚έλΉ«MM?ΈΧ 9 Ώ:B„CDƒZόΔqφqΛ`aΊαz_0ΊαvLW;εΩS±Ο+/jΟSI{Ης'$'W~§όκgΤ†PŠ+gnVηZj*Λ¬_dnσήκ:e+ͺΠ~Υoq†Qα$’~ΚΗfaδ|#Ŝƒ'a[F€χ„Rΐ…°“°‚*iaωΙς3ύΧγ̏αΎGž˜’Όžλ ‘…ΉρΒw°yρ¦Έ’‘ Ιyφ€4G?ψPΊψ*°)5zl…ŸΏ:Β΄ψϊφZΠβ.BΦΑF.(²Σ€0q λ@°p‘Ηθβ6ξ; oGθB‹ΖDό«Ώπ‹ηeψIJR ΉrRκB+4:M!ςωƒΫ+νJΟ|Ԑ_`JΝ}xΫΟU™δšxΛCP;Ό²2쉙t)P°ƒωW<¦”wVΒ[pdF)KŸ0φ =·Ζ\ —ΔT`νμ¦οBπX;yη{?Ζ+k―ώBάΑυ†Qi…~Š ίΖΦ•D0Οέ'^ΰFΑ‡άY(JΝwξΩκΎΣΟΔμƒ?ΩhπυM‡oί4ψΑžΓ94DL":ΰqdΣv Φtp7pb xψά>rψΤCΑXΩ„ƒΈΆψσ]mͺΡλΫmβΓZ·TJζΥiΌςRξ ”‰Όμ‡ε!½rΰR²$ƒσ¬=²0L½Uό}Τ@³Žϋ‹ΰσσ5h;ΐc@$BπX:²Žϋ~ίλŠπΏšυ†Siωι6.|ΫW_Œεf}Ο“+υŠΠιξ.6³Ι\¦”ZλΠ:‹qΓσίΆ:ƒΛϋ„έ`°Χ8V'ΗFg–₯Φrs˜œβʜτ4†Ίͺ&*©±;ΚΣΥ’Ύ{·ΞΗίζΝuΡο„bΉΝ9θοπZή@hάϊšUΚμΫ*μc(τ(ŠC |EΧΦi[¨bΜ|ΚΒ ’ΚβG­y@œFσ¦ΤϋA§[$ d+=daB«ͺXωΘ"?γπσz/EύκΔ`›Nœ{§Ξ?ŒvΌτΓ_΄αzS*€΄Ίύ\»π}lΌτ ¦{ΰΎώ…‹O1–«zπΦdQ©©¬Ζΐ@υœδΊόΨ"/Φp±ς $`œΌV―.ͺΉΕšQlzͺͺ>PΙL˜ΪΠ˜χ—dYuΰQώΧ―©‰‘;MsΎoώuˆθkΛ]#xκΎaφ ƒΐΧξ½ώ1ƒ/TŸΝΊω’U*ν’Ώ/οα½uδΤ9?ϋΠήΝΏέzS+½Ά^Δυ—~€—ž…χ}ςξ2«+ ¬)•sN †LtUέkN{Π'f\—Ή¦•³eU\³αP+…αρΤ{ χNžΫ”ηδ T₯ΌIΠε­ε 3ΎEΞ₯U―rθ³@&₯€ί3'>―ΐ:υš'ύ X­?Ÿ sO§ϊ(ͺšuό‘Ξӟ¦\ό*Ζ ό1F,―ΐϊΩqόήαΪΡήΥw~ύΨ(€΄ˆ"6/]ΐυ‹ΟαϊΕηΰϋΎž„š·nu‘ŠmμŒ¨”†Β›kξЌδ–}¨_ok%Q”ΓΎ;0γκW’«?²6ϋr}„₯š΅ΆσΠv}œδT(­ΊΩ€/RΉΤUx­Tή›Ώͺς¬”μͺ“˜‰υ5#Og†„¬Μ¬π₯TθΐΔ ˆ₯΅γ8vζ<ΦΟ>ψ¦sρ_iύΨ)€αΪΌό"+ƒ—žC·Ώ k[θ™x]7§Π}ηRXΕ‡·•υם+Ϊ|C@«ͺ‘„ΘήGν5Tο«XvΥAgRϋ³k¨’~~h½• @5ͺ„ͺό“^cτ1L’ΝΔξ3΅u6¨.V₯DtjN»ώ( DΡ+¨:/  /@ζEχt "¬¬ŸΑ±ΣχαΨ=ηь&whwώθ׏½Πkoλ6.=—.ΰζ΅—AaSE’rΗ+E0ΐ*pP)ƒβ ΤB…$Ι0F‘κ”Ϋ=§ΜΆ( ?} ΩμZ¦ζ­g›ŠMΦ.|ε.bχYχ^ƒdT+‚ Ξί9a{σ,΅V,­Ο…―&J ŸΏJΝ ρŒLδd G;^Ζ‘χςΟ©³άΒώ.Xw•Π‹bΔΝ«—pγς ΨΌςvn\ΐ= sW¬ ~ƒ(1΄άσΓA5\• ΠρόžBƒόώrΌNθα–XΑL<š(Iψ‘Ϋ>+θό¨ΰΪRן£Žg@š©@TR)ΊbΥ9΄›―†y}ή Γ!Aφc °ΕΚϊi¬¬ŸΖΪΙs-­ΌΫξ ·ξZ0\1άΌz 7―^ΒΦ΅KΨήΈ‚ΌΜ5¬§Η&‹―…?{ (^BΪΤIaσJmΝμsό‹β"ΘR…R¦z§›Y³―2Ιύζ₯|r­ fωαsuj­ήAϊ˜ΘœΞ…OCŸ£>/…'DBDTρ~ϊ, ¨`½5 Ιΐ`3^Ζςϊi,­Ηκρ{^χΊϋ7κZ(€Ϋ¬½νMάΌr»7―c{γ v6AĝŒlS@Q•ϋ―X[kφψqUk―<‹Β>¬SfFxτσ³k^ΊΧ+ήό.ώΑΧΟ½%πϊεο«θΣΩ±Pžάχ‘ωTx@1+ NΝρ€)ΧΆX:rKk'°΄v+λ§ΰš7?b'ΦBό‹ˆ°Ώ}ΫWq°³‰έ›ΨίΪ€ο§,°ͺσNCΠ―~.‡ ¦Μΐ“(ξΏ­9ττgύ°'y‹(ϊ$ω)Ə₯>νεΤcΜτλωqξšKk΄ $ζP υ€f4Αdu“Υ£―ΓςΡ“?vHύ\ p+ψ{77°·΅ιώΆobgύt/ΟDfψj ^eΚ"=‚˜£L;f2C%q«_Ληδα*Μb x*λžΒ#θΗΘٍτY™Ν‡’oJΫlBŒ\π ΊO0ZZΓhωFK+―c²r4χ;\¬ΧΆ ΰŽ.B·Ώ‹ύ›˜ξn‘Ÿξγ`oέξΊƒ=߁(*·-7yb0AτK2±ς` ŠNuZQΎ‚ς>’›Ξρ±φJf…\“”PXυχZΰ Λ±n>’έ}~:–Ίϋτš¦…u#ΈΡšρ2l3B;YA»tdaΡοΰZ(€α""τΣ=τ{θφwΠμ#†ύΑ|?E?έGμ;ψ~*ݏŠΠ˜1$ U‘FyBώ6ορœ’₯?@I{j*s…$XρΈ‹ΛΊΦ΅p£ lΣ’-ΑΨ£-Α&p£‰:Φb½žk‘ήD+ψ‘ηιΎΫGθ;€ήw θβ™ŒέΑ.(xΕ"/Λ(|SΤ”©(ՍlυmΣΒXλ$eαšΆ)5κδ5M ׎a\׌ξω›`-ΐb-Φ]Ό*z±λ.^ °X‹u―…X¬ΕΊ‹ΧB,ΦbέΕk‘k±ξβ΅P‹΅XwρZ(€ΕZ¬»x-ΐb-Φ]Ό `±λ.^ °X‹u―…X¬ΕΊ‹ΧB,ΦbέΕk‘k±ξβυΘb¬Κμ%tEXtdate:create2020-02-29T16:14:56+00:00―Π„U%tEXtdate:modify2020-02-29T16:14:56+00:00ލ<ι(tEXtdate:timestamp2024-11-18T17:46:56+00:00FΞ\6IENDB`‚jpype-1.6.0/doc/make.bat000066400000000000000000000150531501674766700150460ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\JPype.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\JPype.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end jpype-1.6.0/doc/quickguide.py000066400000000000000000000465561501674766700161610ustar00rootroot00000000000000import textwrap footnotes = [] footnotecounter = 1 def section(Title=None, Desc=None): print("%s" % Title) print("-" * len(Title)) if Desc: print("%s" % Desc) print() print("+" + ("-" * 27) + "+" + ("-" * 57) + "+" + ("-" * 57) + "+") print("| %-25s | %-55s | %-55s |" % ("Description", "Java", "Python")) print("+" + ("=" * 27) + "+" + ("=" * 57) + "+" + ("=" * 57) + "+") def endSection(): print() global footnotes, footnotecounter for i in range(0, len(footnotes)): print(" .. [%d] %s" % (i + footnotecounter, footnotes[i])) footnotecounter += len(footnotes) footnotes = [] print() print() def java(s): return """ .. code-block:: java %s """ % s def python(s): return """ .. code-block:: python %s """ % s def entry(Desc=None, Java=None, Python=None, Notes=None): global footnotes, footnotecounter if not Java: Java = "" if not Python: Python = "None" if Notes: global footnotes if Notes in footnotes: Desc += " [%d]_" % (footnotes.index(Notes) + footnotecounter) else: Desc += " [%d]_" % (len(footnotes) + footnotecounter) footnotes.append(Notes) DescLines = textwrap.wrap(Desc, 25) DescLines.insert(0, "") JavaLines = Java.split("\n") PythonLines = Python.split("\n") while (len(DescLines) > 0 or len(JavaLines) > 0 or len(PythonLines) > 0): if len(DescLines) > 0: print("| %-25s |" % DescLines.pop(0), end="") else: print("| %-25s |" % "", end="") if len(JavaLines) > 0: print(" %-55s |" % JavaLines.pop(0), end="") else: print(" %-55s |" % "", end="") if len(PythonLines) > 0: print(" %-55s |" % PythonLines.pop(0)) else: print(" %-55s |" % "") print("+" + ("-" * 27) + "+" + ("-" * 57) + "+" + ("-" * 57) + "+") ######################################################## print(""" Java QuickStart Guide ===================== This is a quick start guide to using JPype with Java. This guide will show a series of snippets with the corresponding commands in both Java and Python for using JPype. The :doc:`userguide` and :doc:`api` have additional details on the use of the JPype module. JPype uses two factory classes (``JArray`` and ``JClass``) to produce class wrappers which can be used to create all Java objects. These serve as both the base class for the corresponding hierarchy and as the factory to produce new wrappers. Casting operators are used to construct specify types of Java types (``JObject``, ``JString``, ``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). Two special classes serve as the base classes for exceptions (``JException``) and interfaces (``JInterface``). There are a small number of support methods to help in controlling the JVM. Lastly, there are a few annotations used to create customized wrappers. For the purpose of this guide, we will assume that the following classes were defined in Java. We will also assume the reader knows enough Java and Python to be dangerous. """) print(""" .. code-block:: java package org.pkg; public class BaseClass { public void callMember(int i) {} } public class MyClass extends BaseClass { final public static int CONST_FIELD = 1; public static int staticField = 1; public int memberField = 2; int internalField =3; public MyClass() {} public MyClass(int i) {} public static void callStatic(int i) {} public void callMember(int i) {} // Python name conflict public void pass() {} public void throwsException() throws java.lang.Exception {} // Overloaded methods public void call(int i) {} public void call(double d) {} } """) ##################################################################################### section("Starting JPype", """ The hardest thing about using JPype is getting the jars loaded into the JVM. Java is curiously unfriendly about reporting problems when it is unable to find a jar. Instead, it will be reported as an ``ImportError`` in Python. These patterns will help debug problems with jar loading. Once the JVM is started Java packages that are within a top level domain (TLD) are exposed as Python modules allowing Java to be treated as part of Python. """ ) entry("Start Java Virtual Machine (JVM)", None, """ .. code-block:: python # Import module import jpype # Enable Java imports import jpype.imports # Pull in types from jpype.types import * # Launch the JVM jpype.startJVM() """) entry("Start Java Virtual Machine (JVM) with a classpath", None, """ .. code-block:: python # Launch the JVM jpype.startJVM(classpath = ['jars/*']) """) entry("Import default Java namespace", None, python("import java.lang"), "All ``java.lang.*`` classes are available.") entry("Add a set of jars from a directory", None, python('jpype.addClassPath("/my/path/*")'), "Must happen prior to starting the JVM") entry("Add a specific jar to the classpath", None, python("jpype.addClassPath('/my/path/myJar.jar')"), "Must happen prior to starting the JVM") entry("Print JVM CLASSPATH", None, """ .. code-block:: python from java.lang import System print(System.getProperty("java.class.path")) """, "After JVM is started") endSection() ##################################################################################### section("Classes/Objects", """ Java classes are presented wherever possible similar to Python classes. The only major difference is that Java classes and objects are closed and cannot be modified. As Java is strongly typed, casting operators are used to select specific overloads when calling methods. Classes are either imported using a module, loaded using ``JPackage`` or loaded with the ``JClass`` factory. """) # Importing entry("Import a class", java("import org.pkg.MyClass"), python("from org.pkg import MyClass"), "This will report an error if the class is not found.") entry("Import a class and rename", None, python("from org.pkg import MyClass as OurClass"), "This will report an error if the class is not found.") entry("Import multiple classes from a package", None, python("from org.pkg import MyClass, AnotherClass"), "This will report an error if the classes are not found.") entry("Import a java package for long name access", None, python("import org.pkg"), "Does not report errors if the package is invalid.") entry("Import a class static", java("import org.pkg.MyClass.CONST_FIELD"), python("from org.pkg.MyClass import CONST_FIELD"), 'Constants, static fields, and static methods can be imported.') entry("Import a class without tld", java("import zippy.NonStandard"), python("NonStandard = JClass('zippy.NonStandard')"), "``JClass`` loads any class by name including inner classes.") # Construction entry("Construct an object", java("MyClass myObject = new MyClass(1);"), python("myObject = MyClass(1)")) entry("Constructing a class with full class name", None, """ .. code-block:: python import org.pkg myObject = org.pkg.MyClass(args) """) # Fields entry("Get a static field", java("int var = MyClass.staticField;"), python("var = MyClass.staticField")) entry("Get a member field", java("int var = myObject.memberField;"), python("var = myObject.memberField")) entry("Set a static field", java("MyClass.staticField = 2;"), python("MyClass.staticField = 2"), "This produces an error for final fields.") entry("Set a member field", java("myObject.memberField = 2;"), python("myObject.memberField = 2"), "This produces an error for final fields.") # Methods entry("Call a static method", java("MyClass.callStatic(1);"), python("MyClass.callStatic(1)")) entry("Call a member method", java("myObject.callMember(1);"), python("myObject.callMember(1)")) entry("Access member with Python naming conflict", java("myObject.pass()"), python("myObject.pass_()"), "Underscore is added during wrapping.") entry("Checking inheritance", java("if (obj instanceof MyClass) {...}"), python("if (isinstance(obj, MyClass): ...")) entry("Checking if Java class wrapper", None, python("if (isinstance(obj, JClass): ...")) entry("Checking if Java object wrapper", None, python("if (isinstance(obj, JObject): ...")) # Casting entry("Casting to a specific type", java("BaseClass b = (BaseClass)myObject;"), python("b = (BaseClass) @ myObject"), "Matmul(@) is used as the casting operator.") endSection() ##################################################################################### section("Exceptions", """ Java exceptions extend from Python exceptions and can be dealt with in the same way as Python native exceptions. JException serves as the base class for all Java exceptions. """) entry("Catch an exception", """ .. code-block:: java try { myObject.throwsException(); } catch (java.lang.Exception ex) { ... } """, """ .. code-block:: python try: myObject.throwsException() except java.lang.Exception as ex: ... """) entry("Throw an exception to Java", """ .. code-block:: java throw new java.lang.Exception( "Problem"); """, """ .. code-block:: python raise java.lang.Exception( "Problem") """) entry("Checking if Java exception wrapper", None, python('if (isinstance(obj, JException): ...')) entry("Closeable items", """ .. code-block:: java try (InputStream is = Files.newInputStream(file)) { ... } """, """ .. code-block:: python with Files.newInputStream(file) as is: ... """) endSection() ##################################################################################### section("Primitives", """ Most Python primitives directly map into Java primitives. However, Python does not have the same primitive types, and it is necessary to cast to a specific Java primitive type whenever there are Java overloads that would otherwise be in conflict. Each of the Java types are exposed in JPype (``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). """) entry("Casting to hit an overload", java("myObject.call((int)v);"), python("myObject.call(JInt(v))"), "``JInt`` acts as a casting operator") entry("Create a primitive array", java("int[] array = new int[5]"), python("array = JInt[5]")) entry("Create a rectangular primitive array", java("int[][] array = new int[5][10]"), python("array = JInt[5, 10]")) entry("Create an array of arrays", java("int[][] array = new int[5][]"), python("array = JInt[5, :]")) entry("Create an initialized primitive array", java("int[] array = new int[]{1,2,3}"), python("array = JInt[:]([1,2,3])"), "list, sequences, or np.array can be used to initialize.") entry("Create an initialized boxed array", java("Integer[] array = new Integer[]{1,2,3}"), python("array = java.lang.Integer[:]([1,2,3])"), "list, sequences, or np.array can be used to initialize.") entry("Put a specific primitive type on a list", """ .. code-block:: java List myList = new ArrayList<>(); myList.add(1); """, """ .. code-block:: python from java.util import ArrayList myList = ArrayList() myList.add(JInt(1)) """) entry("Boxing a primitive", java("Integer boxed = 1;"), python("boxed = JObject(JInt(1))"), "``JInt`` specifies the prmitive type. ``JObject`` boxes the primitive.") endSection() ##################################################################################### section("Strings", """ Java strings are similar to Python strings. They are both immutable and produce a new string when altered. Most operations can use Java strings in place of Python strings, with minor exceptions as Python strings are not completely duck typed. When comparing or using as dictionary keys, all JString objects should be converted to Python. """) entry("Create a Java string", java('String javaStr = new String("foo");'), python( 'myStr = JString("foo")'), "``JString`` constructs a ``java.lang.String``") entry("Create a Java string from bytes", ''' .. code-block:: java byte[] b; String javaStr = new String(b, "UTF-8"); ''', ''' .. code-block:: python b= b'foo' myStr = JString(b, "UTF-8") ''', "All ``java.lang.String`` constuctors work.") entry("Converting Java string", None, python("str(javaStr)")) entry("Comparing Python and Java strings", None, python( "str(javaStr) == pyString"), "``str()`` converts the object for comparison") entry("Comparing Java strings", java( 'javaStr.equals("foo")'), python('javaStr == "foo"')) entry("Checking if Java string", None, python( "if (isinstance(obj, JString): ...")) endSection() ##################################################################################### section("Arrays", """ Arrays are create using the JArray class factory. They operate like Python lists, but they are fixed in size. """) entry("Create a single dimension array", java("MyClass[] array = new MyClass[5];"), python("array = MyClass[5]")) entry("Create a multi dimension array (old)", java("MyClass[][] array2 = new MyClass[5][];"), python("array2 = JArray(MyClass, 2)(5)")) entry("Create a multi dimension array (new)", java("MyClass[][] array2 = new MyClass[5][];"), python("array2 = MyClass[5,:]")) entry("Access an element", java("array[0] = new MyClass()"), python("array[0] = MyClass()")) entry("Size of an array", java("array.length"), python("len(array)")) entry("Get last element", java("MyClass a = array[array.length];"), python("a = array[-1]")) entry("Slice an array", None, python("a = array[2:5]"), "A Slice is a view and changes will be reflected on original. Slices passed to Java will clone.") entry("Clone an array", java("MyClass[] a = array.clone();"), python("a = array.clone()")) entry("Convert to Python list", None, python("pylist = list(array)")) entry("Iterate elements", """ .. code-block:: java for (MyClass element: array) {...} """, """ .. code-block:: python for element in array: ... """) entry("Checking if java array wrapper", None, python("if (isinstance(obj, JArray): ...")) endSection() ##################################################################################### section("Collections", """ Java standard containers are available and are overloaded with Python syntax where possible to operate in a similar fashion to Python objects. """) entry("Import list type", java("import java.util.ArrayList;"), python("from java.util import ArrayList")) entry("Construct a list", java("List myList=new ArrayList<>();"), python("myList=ArrayList()")) entry("Get length of list", java("int sz = myList.size();"), python("sz = len(myList)")) entry("Get list item", java('Integer i = myList.get(0)'), python('i = myList[0]')) entry("Set list item", java('myList.set(0, 1)'), python('myList[0]=Jint(1)'), "Casting is required to box primitives to the correct type.") entry("Iterate list elements", """ .. code-block:: java for (Integer element: myList) {...} """, """ .. code-block:: python for element in myList: ... """) entry("Import map type", java("import java.util.HashMap;"), python("from java.util import HashMap")) entry("Construct a map", java("Map myMap = new HashMap<>();"), python("myMap = HashMap()")) entry("Get length of map", java("int sz = myMap.size();"), python("sz = len(myMap)")) entry("Get map item", java('Integer i = myMap.get("foo")'), python('i = myMap["foo"]')) entry("Set map item", java('myMap.set("foo", 1)'), python('myMap["foo"] = Jint(1)'), "Casting is required to box primitives to the correct type.") entry("Iterate map entries", """ .. code-block:: java for (Map.Entry e : myMap.entrySet()) {...} """, """ .. code-block:: python for e in myMap.entrySet(): ... """) endSection() ##################################################################################### section("Reflection", """ Java reflection can be used to access operations that are outside the scope of the JPype syntax. This includes calling a specific overload or even accessing private methods and fields. """) entry("Access Java reflection class", java("MyClass.class"), python("MyClass.class_")) entry("Access a private field by name", None, """ .. code-block:: python cls = myObject.class_ field = cls.getDeclaredField( "internalField") field.setAccessible(True) field.get() """, """This is prohibited after Java 8""") entry("Accessing a specific overload", None, """ .. code-block:: python cls = MyClass.class_ cls.getDeclaredMethod("call", JInt) cls.invoke(myObject, JInt(1)) """, "types must be exactly specified.") entry("Convert a ``java.lang.Class`` into Python wrapper", None, """ .. code-block:: python # Something returned a java.lang.Class MyClassJava = getClassMethod() # Convert to it to Python MyClass = JClass(myClassJava) """, "Rarely required unless the class was supplied external such as generics.") entry("Load a class with a external class loader", """ .. code-block:: java ClassLoader cl = new ExternalClassLoader(); Class cls = Class.forName("External", True, cl) """, """ .. code-block:: python cl = ExternalClassLoader() cls = JClass("External", loader=cl) """) entry("Accessing base method implementation", None, """ .. code-block:: python from org.pkg import \\ BaseClass, MyClass myObject = MyClass(1) BaseClass.callMember(myObject, 2) """) endSection() section("Implements and Extension", """ JPype can implement a Java interface by annotating a Python class. Each method that is required must be implemented. JPype does not support extending a class directly in Python. Where it is necessary to exend a Java class, it is required to create a Java extension with an interface for each methods that are to be accessed from Python. """) entry("Implement an interface", """ .. code-block:: java public class PyImpl implements MyInterface { public void call() {...} } """, """ .. code-block:: python @JImplements(MyInterface) class PyImpl(object): @JOverride def call(self): pass """, "") entry("Extending classes", None, None, """Support for use of Python function as Java 8 lambda is WIP.""") entry("Lambdas", java('DoubleUnaryOperator u = (p->p*2);'), python('u=DoubleUnaryOperator@(lambda x: x*2)'), 'Any Java functional interface can take a lambda or callable.') endSection() print( """ Don't like the formatting? Feel the guide is missing something? Submit a pull request at the project page. """) jpype-1.6.0/doc/quickguide.rst000066400000000000000000002415671501674766700163400ustar00rootroot00000000000000 Java QuickStart Guide ===================== This is a quick start guide to using JPype with Java. This guide will show a series of snippets with the corresponding commands in both Java and Python for using JPype. The :doc:`userguide` and :doc:`api` have additional details on the use of the JPype module. JPype uses two factory classes (``JArray`` and ``JClass``) to produce class wrappers which can be used to create all Java objects. These serve as both the base class for the corresponding hierarchy and as the factory to produce new wrappers. Casting operators are used to construct specify types of Java types (``JObject``, ``JString``, ``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). Two special classes serve as the base classes for exceptions (``JException``) and interfaces (``JInterface``). There are a small number of support methods to help in controlling the JVM. Lastly, there are a few annotations used to create customized wrappers. For the purpose of this guide, we will assume that the following classes were defined in Java. We will also assume the reader knows enough Java and Python to be dangerous. .. code-block:: java package org.pkg; public class BaseClass { public void callMember(int i) {} } public class MyClass extends BaseClass { final public static int CONST_FIELD = 1; public static int staticField = 1; public int memberField = 2; int internalField =3; public MyClass() {} public MyClass(int i) {} public static void callStatic(int i) {} public void callMember(int i) {} // Python name conflict public void pass() {} public void throwsException() throws java.lang.Exception {} // Overloaded methods public void call(int i) {} public void call(double d) {} } Starting JPype -------------- The hardest thing about using JPype is getting the jars loaded into the JVM. Java is curiously unfriendly about reporting problems when it is unable to find a jar. Instead, it will be reported as an ``ImportError`` in Python. These patterns will help debug problems with jar loading. Once the JVM is started Java packages that are within a top level domain (TLD) are exposed as Python modules allowing Java to be treated as part of Python. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Start Java Virtual | | .. code-block:: python | | Machine (JVM) | | | | | | # Import module | | | | import jpype | | | | | | | | # Enable Java imports | | | | import jpype.imports | | | | | | | | # Pull in types | | | | from jpype.types import * | | | | | | | | # Launch the JVM | | | | jpype.startJVM() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Start Java Virtual | | .. code-block:: python | | Machine (JVM) with a | | | | classpath | | # Launch the JVM | | | | jpype.startJVM(classpath = ['jars/*']) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import default Java | | .. code-block:: python | | namespace [1]_ | | | | | | import java.lang | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Add a set of jars from a | | .. code-block:: python | | directory [2]_ | | | | | | jpype.addClassPath("/my/path/*") | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Add a specific jar to the | | .. code-block:: python | | classpath [2]_ | | | | | | jpype.addClassPath('/my/path/myJar.jar') | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Print JVM CLASSPATH [3]_ | | .. code-block:: python | | | | | | | | from java.lang import System | | | | print(System.getProperty("java.class.path")) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [1] All ``java.lang.*`` classes are available. .. [2] Must happen prior to starting the JVM .. [3] After JVM is started Classes/Objects --------------- Java classes are presented wherever possible similar to Python classes. The only major difference is that Java classes and objects are closed and cannot be modified. As Java is strongly typed, casting operators are used to select specific overloads when calling methods. Classes are either imported using a module, loaded using ``JPackage`` or loaded with the ``JClass`` factory. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Import a class [4]_ | .. code-block:: java | .. code-block:: python | | | | | | | import org.pkg.MyClass | from org.pkg import MyClass | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a class and rename | | .. code-block:: python | | [4]_ | | | | | | from org.pkg import MyClass as OurClass | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import multiple classes | | .. code-block:: python | | from a package [5]_ | | | | | | from org.pkg import MyClass, AnotherClass | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a java package for | | .. code-block:: python | | long name access [6]_ | | | | | | import org.pkg | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a class static | .. code-block:: java | .. code-block:: python | | [7]_ | | | | | import org.pkg.MyClass.CONST_FIELD | from org.pkg.MyClass import CONST_FIELD | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a class without | .. code-block:: java | .. code-block:: python | | tld [8]_ | | | | | import zippy.NonStandard | NonStandard = JClass('zippy.NonStandard') | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Construct an object | .. code-block:: java | .. code-block:: python | | | | | | | MyClass myObject = new MyClass(1); | myObject = MyClass(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Constructing a class with | | .. code-block:: python | | full class name | | | | | | import org.pkg | | | | myObject = org.pkg.MyClass(args) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get a static field | .. code-block:: java | .. code-block:: python | | | | | | | int var = MyClass.staticField; | var = MyClass.staticField | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get a member field | .. code-block:: java | .. code-block:: python | | | | | | | int var = myObject.memberField; | var = myObject.memberField | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set a static field [9]_ | .. code-block:: java | .. code-block:: python | | | | | | | MyClass.staticField = 2; | MyClass.staticField = 2 | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set a member field [9]_ | .. code-block:: java | .. code-block:: python | | | | | | | myObject.memberField = 2; | myObject.memberField = 2 | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Call a static method | .. code-block:: java | .. code-block:: python | | | | | | | MyClass.callStatic(1); | MyClass.callStatic(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Call a member method | .. code-block:: java | .. code-block:: python | | | | | | | myObject.callMember(1); | myObject.callMember(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Access member with Python | .. code-block:: java | .. code-block:: python | | naming conflict [10]_ | | | | | myObject.pass() | myObject.pass_() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking inheritance | .. code-block:: java | .. code-block:: python | | | | | | | if (obj instanceof MyClass) {...} | if (isinstance(obj, MyClass): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java class | | .. code-block:: python | | wrapper | | | | | | if (isinstance(obj, JClass): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java object | | .. code-block:: python | | wrapper | | | | | | if (isinstance(obj, JObject): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Casting to a specific | .. code-block:: java | .. code-block:: python | | type [11]_ | | | | | BaseClass b = (BaseClass)myObject; | b = (BaseClass) @ myObject | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [4] This will report an error if the class is not found. .. [5] This will report an error if the classes are not found. .. [6] Does not report errors if the package is invalid. .. [7] Constants, static fields, and static methods can be imported. .. [8] ``JClass`` loads any class by name including inner classes. .. [9] This produces an error for final fields. .. [10] Underscore is added during wrapping. .. [11] Matmul(@) is used as the casting operator. Exceptions ---------- Java exceptions extend from Python exceptions and can be dealt with in the same way as Python native exceptions. JException serves as the base class for all Java exceptions. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Catch an exception | .. code-block:: java | .. code-block:: python | | | | | | | try { | try: | | | myObject.throwsException(); | myObject.throwsException() | | | } catch (java.lang.Exception ex) | except java.lang.Exception as ex: | | | { ... } | ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Throw an exception to | .. code-block:: java | .. code-block:: python | | Java | | | | | throw new java.lang.Exception( | raise java.lang.Exception( | | | "Problem"); | "Problem") | | | | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java | | .. code-block:: python | | exception wrapper | | | | | | if (isinstance(obj, JException): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Closeable items | .. code-block:: java | .. code-block:: python | | | | | | | try (InputStream is | with Files.newInputStream(file) as is: | | | = Files.newInputStream(file)) | ... | | | { ... } | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ Primitives ---------- Most Python primitives directly map into Java primitives. However, Python does not have the same primitive types, and it is necessary to cast to a specific Java primitive type whenever there are Java overloads that would otherwise be in conflict. Each of the Java types are exposed in JPype (``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Casting to hit an | .. code-block:: java | .. code-block:: python | | overload [12]_ | | | | | myObject.call((int)v); | myObject.call(JInt(v)) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a primitive array | .. code-block:: java | .. code-block:: python | | | | | | | int[] array = new int[5] | array = JInt[5] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a rectangular | .. code-block:: java | .. code-block:: python | | primitive array | | | | | int[][] array = new int[5][10] | array = JInt[5, 10] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create an array of arrays | .. code-block:: java | .. code-block:: python | | | | | | | int[][] array = new int[5][] | array = JInt[5, :] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create an initialized | .. code-block:: java | .. code-block:: python | | primitive array [13]_ | | | | | int[] array = new int[]{1,2,3} | array = JInt[:]([1,2,3]) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create an initialized | .. code-block:: java | .. code-block:: python | | boxed array [13]_ | | | | | Integer[] array = new Integer[]{1,2,3} | array = java.lang.Integer[:]([1,2,3]) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Put a specific primitive | .. code-block:: java | .. code-block:: python | | type on a list | | | | | List myList | from java.util import ArrayList | | | = new ArrayList<>(); | myList = ArrayList() | | | myList.add(1); | myList.add(JInt(1)) | | | | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Boxing a primitive [14]_ | .. code-block:: java | .. code-block:: python | | | | | | | Integer boxed = 1; | boxed = JObject(JInt(1)) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [12] ``JInt`` acts as a casting operator .. [13] list, sequences, or np.array can be used to initialize. .. [14] ``JInt`` specifies the prmitive type. ``JObject`` boxes the primitive. Strings ------- Java strings are similar to Python strings. They are both immutable and produce a new string when altered. Most operations can use Java strings in place of Python strings, with minor exceptions as Python strings are not completely duck typed. When comparing or using as dictionary keys, all JString objects should be converted to Python. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Create a Java string | .. code-block:: java | .. code-block:: python | | [15]_ | | | | | String javaStr = new String("foo"); | myStr = JString("foo") | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a Java string from | .. code-block:: java | .. code-block:: python | | bytes [16]_ | | | | | byte[] b; | b= b'foo' | | | String javaStr = new String(b, "UTF-8"); | myStr = JString(b, "UTF-8") | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Converting Java string | | .. code-block:: python | | | | | | | | str(javaStr) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Comparing Python and Java | | .. code-block:: python | | strings [17]_ | | | | | | str(javaStr) == pyString | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Comparing Java strings | .. code-block:: java | .. code-block:: python | | | | | | | javaStr.equals("foo") | javaStr == "foo" | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java string | | .. code-block:: python | | | | | | | | if (isinstance(obj, JString): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [15] ``JString`` constructs a ``java.lang.String`` .. [16] All ``java.lang.String`` constuctors work. .. [17] ``str()`` converts the object for comparison Arrays ------ Arrays are create using the JArray class factory. They operate like Python lists, but they are fixed in size. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Create a single dimension | .. code-block:: java | .. code-block:: python | | array | | | | | MyClass[] array = new MyClass[5]; | array = MyClass[5] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a multi dimension | .. code-block:: java | .. code-block:: python | | array (old) | | | | | MyClass[][] array2 = new MyClass[5][]; | array2 = JArray(MyClass, 2)(5) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a multi dimension | .. code-block:: java | .. code-block:: python | | array (new) | | | | | MyClass[][] array2 = new MyClass[5][]; | array2 = MyClass[5,:] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Access an element | .. code-block:: java | .. code-block:: python | | | | | | | array[0] = new MyClass() | array[0] = MyClass() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Size of an array | .. code-block:: java | .. code-block:: python | | | | | | | array.length | len(array) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get last element | .. code-block:: java | .. code-block:: python | | | | | | | MyClass a = array[array.length]; | a = array[-1] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Slice an array [18]_ | | .. code-block:: python | | | | | | | | a = array[2:5] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Clone an array | .. code-block:: java | .. code-block:: python | | | | | | | MyClass[] a = array.clone(); | a = array.clone() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Convert to Python list | | .. code-block:: python | | | | | | | | pylist = list(array) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Iterate elements | .. code-block:: java | .. code-block:: python | | | | | | | for (MyClass element: array) | for element in array: | | | {...} | ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if java array | | .. code-block:: python | | wrapper | | | | | | if (isinstance(obj, JArray): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [18] A Slice is a view and changes will be reflected on original. Slices passed to Java will clone. Collections ----------- Java standard containers are available and are overloaded with Python syntax where possible to operate in a similar fashion to Python objects. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Import list type | .. code-block:: java | .. code-block:: python | | | | | | | import java.util.ArrayList; | from java.util import ArrayList | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Construct a list | .. code-block:: java | .. code-block:: python | | | | | | | List myList=new ArrayList<>(); | myList=ArrayList() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get length of list | .. code-block:: java | .. code-block:: python | | | | | | | int sz = myList.size(); | sz = len(myList) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get list item | .. code-block:: java | .. code-block:: python | | | | | | | Integer i = myList.get(0) | i = myList[0] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set list item [19]_ | .. code-block:: java | .. code-block:: python | | | | | | | myList.set(0, 1) | myList[0]=Jint(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Iterate list elements | .. code-block:: java | .. code-block:: python | | | | | | | for (Integer element: myList) | for element in myList: | | | {...} | ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import map type | .. code-block:: java | .. code-block:: python | | | | | | | import java.util.HashMap; | from java.util import HashMap | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Construct a map | .. code-block:: java | .. code-block:: python | | | | | | | Map myMap = new HashMap<>(); | myMap = HashMap() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get length of map | .. code-block:: java | .. code-block:: python | | | | | | | int sz = myMap.size(); | sz = len(myMap) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get map item | .. code-block:: java | .. code-block:: python | | | | | | | Integer i = myMap.get("foo") | i = myMap["foo"] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set map item [19]_ | .. code-block:: java | .. code-block:: python | | | | | | | myMap.set("foo", 1) | myMap["foo"] = Jint(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Iterate map entries | .. code-block:: java | .. code-block:: python | | | | | | | for (Map.Entry e | for e in myMap.entrySet(): | | | : myMap.entrySet()) | ... | | | {...} | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [19] Casting is required to box primitives to the correct type. Reflection ---------- Java reflection can be used to access operations that are outside the scope of the JPype syntax. This includes calling a specific overload or even accessing private methods and fields. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Access Java reflection | .. code-block:: java | .. code-block:: python | | class | | | | | MyClass.class | MyClass.class_ | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Access a private field by | | .. code-block:: python | | name [20]_ | | | | | | cls = myObject.class_ | | | | field = cls.getDeclaredField( | | | | "internalField") | | | | field.setAccessible(True) | | | | field.get() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Accessing a specific | | .. code-block:: python | | overload [21]_ | | | | | | cls = MyClass.class_ | | | | cls.getDeclaredMethod("call", JInt) | | | | cls.invoke(myObject, JInt(1)) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Convert a | | .. code-block:: python | | ``java.lang.Class`` into | | | | Python wrapper [22]_ | | # Something returned a java.lang.Class | | | | MyClassJava = getClassMethod() | | | | | | | | # Convert to it to Python | | | | MyClass = JClass(myClassJava) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Load a class with a | .. code-block:: java | .. code-block:: python | | external class loader | | | | | ClassLoader cl | cl = ExternalClassLoader() | | | = new ExternalClassLoader(); | cls = JClass("External", loader=cl) | | | Class cls | | | | = Class.forName("External", | | | | True, cl) | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Accessing base method | | .. code-block:: python | | implementation | | | | | | from org.pkg import \ | | | | BaseClass, MyClass | | | | myObject = MyClass(1) | | | | BaseClass.callMember(myObject, 2) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [20] This is prohibited after Java 8 .. [21] types must be exactly specified. .. [22] Rarely required unless the class was supplied external such as generics. Implements and Extension ------------------------ JPype can implement a Java interface by annotating a Python class. Each method that is required must be implemented. JPype does not support extending a class directly in Python. Where it is necessary to exend a Java class, it is required to create a Java extension with an interface for each methods that are to be accessed from Python. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Implement an interface | .. code-block:: java | .. code-block:: python | | | | | | | public class PyImpl | @JImplements(MyInterface) | | | implements MyInterface | class PyImpl(object): | | | { | @JOverride | | | public void call() | def call(self): | | | {...} | pass | | | } | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | None | | Extending classes [23]_ | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Lambdas [24]_ | .. code-block:: java | .. code-block:: python | | | | | | | DoubleUnaryOperator u = (p->p*2); | u=DoubleUnaryOperator@(lambda x: x*2) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [23] Support for use of Python function as Java 8 lambda is WIP. .. [24] Any Java functional interface can take a lambda or callable. Don't like the formatting? Feel the guide is missing something? Submit a pull request at the project page. jpype-1.6.0/doc/release.rst000066400000000000000000000037471501674766700156220ustar00rootroot00000000000000:orphan: Release cycle docs ================== This project uses bump2version See https://medium.com/@williamhayes/versioning-using-bumpversion-4d13c914e9b8 To start a new cycle use: ``bumpversion patch`` To increment the build number during development: ``bumpversion build`` To release: ``bumpversion release`` Full process: (first copy the checklist to an issue) - [ ] Start from the release branch ``git checkout release`` - [ ] Make a new branch for the release cycle ``git checkout -b releases/{version}`` - [ ] Update release process to current - [ ] Check the .azure scripts to see if they are up to date. Look on https://devguide.python.org/versions/ to see what versions can be dropped Check Python versions for Windows Check Python versions for OSX Check the manylinux image for Linux - [ ] Check patterns in .azure/scripts/build-wheels.sh - [ ] Merge the current master with the release ``git pull origin master`` - [ ] Edit doc/CHANGELOG.rst - [ ] Create a release candidate - [ ] Bump the version to release ``bumpversion release`` - [ ] Send the release to be evaluated ``git push`` - [ ] Verify CI on Azure ([Azure](https://dev.azure.com/jpype-project/jpype/_build?definitionId=1)) - [ ] Manually trigger a ``jpype.release`` on ([Azure](https://dev.azure.com/jpype-project/jpype/_build?definitionId=2)) If successful, download the artifacts for publication. - [ ] Advance the release pointer ``git checkout release`` ``git merge releases/`` - [ ] Publish the release - Add draft release on github - Attach the artifacts to the release. - [ ] Start master on a new cycle - Use a PR to pull release back to master - ``git checkout master`` - ``git checkout -b cycle`` - ``git merge release`` - ``bumpversion patch`` - Use PR to insert the cycle in master **Last, update this document with any changes in process that were required.** jpype-1.6.0/doc/userguide.rst000066400000000000000000007307371501674766700162040ustar00rootroot00000000000000################## JPype User Guide ################## .. toctree:: :maxdepth: 2 .. _introduction: Introduction ************ .. _introduction_jpype_the_python_to_java_bridge: JPype the Python to Java bridge =============================== JPype is a Python module that provides seamless access to Java libraries from Python. Unlike Jython, which reimplements Python on the Java Virtual Machine (JVM), JPype bridges Python and Java at the native level using the Java Native Interface (JNI). This native approach implements CPython classes for each Java type and Java support for type management while communicating at the process level. This approach enables: - Direct interaction between Python and Java objects. - Access to the full range of Java libraries and APIs. - No need to serialize objects when communicating between language. - Unified primitive types. - High speed transfers through shared memory between Python and Java for large primitive array types. JPype is intended for Python developers who need to leverage Java libraries or Java developers who want to use Python for scripting, debugging, or visualization. .. _introduction_why_use_jpype?: Why Use JPype? -------------- JPype makes it easy to integrate Python and Java, enabling developers to: 1. Access Java libraries directly from Python code. 2. Debug Java data structures interactively using Python tools. 3. Use Python's flexibility for scientific computing while leveraging Java's robustness for enterprise applications. .. _introduction_prerequisites: Prerequisites ------------- Before using JPype, ensure the following: 1. **Python**: JPype requires Python 3.8 or later. Check your Python version by running:: python --version 2. **Java**: JPype requires a Java Runtime Environment (JRE) or Java Development Kit (JDK) version 11 or later. Check your Java version by running:: java -version 3. **Architecture Compatibility**: Ensure the Python interpreter and JVM have matching architectures (e.g., both 64-bit or both 32-bit). .. _introduction_installation: Installation ------------ JPype can be installed using either `pip` or `conda`. .. _introduction_using_pip: Using pip ~~~~~~~~~ To install JPype via `pip`, run:: pip install JPype1 .. _introduction_using_conda: Using conda ~~~~~~~~~~~ To install JPype via `conda`, use:: conda install -c conda-forge jpype1 .. _introduction_verifying_installation: Verifying Installation ~~~~~~~~~~~~~~~~~~~~~~ After installation, verify that JPype is installed correctly by running:: import jpype print("JPype installed successfully!") .. _introduction_your_first_jpype_program: Your First JPype Program ------------------------ Follow these steps to write and run your first JPype program: .. _introduction_step_1_start_the_jvm: Step 1: Start the JVM ~~~~~~~~~~~~~~~~~~~~~ JPype requires the JVM to be started before interacting with Java. All calls to the Java prior to the start of the JVM will fail. Use the `jpype.startJVM()` function to start the JVM:: import jpype # Start the JVM jpype.startJVM(classpath=[]) .. _introduction_step_2_access_java_classes: Step 2: Access Java Classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ JPype allows you to import and use Java classes directly. For example, import Java's `java.lang.String` class:: from java.lang import String .. _introduction_step_3_use_java_objects: Step 3: Use Java Objects ~~~~~~~~~~~~~~~~~~~~~~~~ Create and manipulate Java objects just like Python objects:: java_string = String("Hello from Java!") print(java_string.toUpperCase()) # Output: HELLO FROM JAVA! .. _introduction_step_4_shut_down_the_jvm: Step 4: Shut Down the JVM ~~~~~~~~~~~~~~~~~~~~~~~~~ Once the program is complete the JVM will exit when Python does. All termination code is handled automatically. .. _introduction_complete_example: Complete Example ~~~~~~~~~~~~~~~~ Save the following code in a file named `hello_jpype.py`: .. code-block:: python import jpype import jpype.imports # Start the JVM jpype.startJVM(classpath=["../jar/*","../classes","com.amce-1.0.jar"]) # Import Java classes from java.lang import String # Use the Java String class java_string = String("Hello from Java!") print(java_string.toUpperCase()) # Output: HELLO FROM JAVA! Run the script using Python:: python hello_jpype.py You should see the output:: HELLO FROM JAVA! .. _introduction_next_steps: Next Steps ---------- Once you've successfully set up JPype, explore the following topics: 1. **Accessing Java Libraries**: Learn how to use JPype to interact with third- party Java libraries. 2. **Working with Java Collections**: Discover how JPype integrates Java collections with Python's `collections` module. 3. **Implementing Java Interfaces in Python**: Use JPype's proxy functionality to implement Java interfaces in Python. 4. **Debugging Java Code**: Use JPype as an interactive shell for debugging Java programs. .. _introduction_summary_of_jpype: Summary of JPype ---------------- JPype bridges Python and Java, enabling seamless integration between the two languages. With JPype, you can access Java libraries, implement Java interfaces, and debug Java codeβ€”all from the comfort of Python. Happy coding! .. _introduction_jpype_use_cases: JPype Use Cases =============== Here are three typical reasons to use JPype. - Access to a Java library from a Python program (Python oriented) - Visualization of Java data structures via Matplotlib (Java oriented) - Interactive Java and Python development including scientific and mathematical programming using Python as a Java shell with Spyder or Jupyter notebooks. Let's explore each of these options. .. _introduction_case_1_access_to_a_java_library: Case 1: Access to a Java library -------------------------------- Suppose you are a hard core Python programmer. You can easily use lambdas, threading, dictionary hacking, monkey patching, been there, done that. You are hard at work on your latest project but you just need to pip in the database driver for your customers database and you can call it a night. Unfortunately, it appears that your customers database will not connect to the Python database API. The whole thing is custom and the customer isn't going to supply you with a Python version. They did send you a Java driver for the database but fat lot of good that will do for you. Stumbling through the internet you find a module that says it can natively load Java packages as Python modules. Well, it's worth a shot... So first thing the guide says is that you need to install Java and set up a ``JAVA_HOME`` environment variable pointing to the JRE. Then start the JVM with classpath pointed to customers jar file. The customer sent over an example in Java so you just have to port it into Python. .. code-block:: java package com.paying.customer; import com.paying.customer.DataBase public class MyExample { public void main(String[] args) { Database db = new Database("our_records"); try (DatabaseConnection c = db.connect()) { c.runQuery(); while (c.hasRecords()) { Record record = db.nextRecord(); ... } } } } It does not look too horrible to translate. You just need to look past all those pointless type declarations and meaningless braces. Once you do, you can glue this into Python and get back to what you really love, like performing dictionary comprehensions on multiple keys. You glance over the JPype quick start guide. It has a few useful patterns... set the class path, start the JVM, remove all the type declarations, and you are done. .. code-block:: python # Boiler plate stuff to start the module import jpype import jpype.imports from jpype.types import * # Launch the JVM jpype.startJVM(classpath=['jars/database.jar']) # import the Java modules from com.paying.customer import DataBase # Copy in the patterns from the guide to replace the example code db = Database("our_records") with db.connect() as c: c.runQuery() while c.hasRecords(): record = db.nextRecord() ... Launch it in the interactive window. You can get back to programming in Python once you get a good night sleep. .. _introduction_case_2_visualization_of_java_structures: Case 2: Visualization of Java structures ---------------------------------------- Suppose you are a hard core Java programmer. Weakly typed languages are for wimps, if it isn't garbage collected it is garbage. Unfortunately your latest project has suffered a nasty data structure problem in one of the threads. You managed to capture the data structure in a serialized form but if you could just make graph and call a few functions this would be so much easier. But the interactive Java shell that you are using doesn't really have much in the way of visualization and you don't have time to write a whole graphing applet just to display this dataset. So poking around on the internet you find that Python has exactly the visualization that you need for the problem, but it only runs in CPython. So in order to visualize the structure, you need to get it into Python, extract the data structures and, send it to the plotting routine. You install conda, follow the install instructions to connect to conda-forge, pull JPype1, and launch the first Python interactive environment that appear to produce a plot. You get the shell open and paste in the boilerplate start commands, and load in your serialized object. .. code-block:: python import jpype import jpype.imports jpype.startJVM(classpath = ['jars/*', 'test/classes']) from java.nio.file import Files, Paths from java.io import ObjectInputStream with Files.newInputStream(Paths.get("myobject.ser")) as stream: ois = ObjectInputStream(stream) obj = ois.readObject() print(obj) # prints org.bigstuff.MyObject@7382f612 It appears that the structure is loaded. The problematic structure requires you call the getData method with the correct index. .. code-block:: python d = obj.getData(1) > TypeError: No matching overloads found for org.bigstuff.MyObject.getData(int), > options are: public double[] org.bigstuff.MyObject.getData(double) public double[] org.bigstuff.MyObject.getData(int) Looks like you are going to have to pick the right overload as it can't figure out which overload to use. Darn weakly typed language, how to get the right type in so that you can plot the right data. It says that you can use the casting operators. .. code-block:: python from jpype.types import * d = obj.getData(JInt(1)) print(type(d)) # prints double[] Great. Now you just need to figure out how to convert from a Java array into something our visualization code can deal with. As nothing indicates that you need to convert the array, you just copy out of the visualization tool example and watch what happens. .. code-block:: python import matplotlib.pyplot as plt plt.plot(d) plt.show() A graph appears on the screen. Meaning that NumPy has not issue dealing with Java arrays. It looks like ever 4th element in the array is zero. It must be the PR the new guy put in. And off you go back to the wonderful world of Java back to the safety of curly braces and semicolons. .. _introduction_case_3_interactive_java: Case 3: Interactive Java ------------------------ Suppose you are a laboratory intern running experiments at Hawkins National Laboratory. (For the purpose of this exercise we will ignore the fact that Hawkins was shut down in 1984 and Java was created in 1995). You have the test subject strapped in and you just need to start the experiment. So you pull up Jupyter notebook your boss gave you and run through the cells. You need to add some heart wave monitor to the list of graphed results. The relevant section of the API for the Experiment appears to be .. code-block:: java package gov.hnl.experiment; public interface Monitor { public void onMeasurement(Measurement measurement); } public interface Measurement { public double getTime(); public double getHeartRate(); public double getBrainActivity(); public double getDrugFlowRate(); public boolean isNoseBleeding(); } public class Experiment { public void addCondition(Instant t, Condition c); public void addMoniter(Monitor m); public void run(); } The notebook already has all the test conditions for the experiment set up and the JVM is started, so you just need to implement the monitor. Based on the previous examples, you start by defining a monitor class .. code-block:: python from jpype import JImplements, JOverride from gov.hnl.experiment import Monitor @JImplements(Monitor) class HeartMonitor: def __init__(self): self.readings = [] @JOverride def onMeasurement(self, measurement): self.readings.append([measurement.getTime(), measurement.getHeartRate()]) def getResults(self): return np.array(self.readings) There is a bit to unpack here. You have implemented a Java class from within Python. The Java implementation is simply an ordinary Python class which has be decorated with ``@JImplements`` and ``@JOverride``. When you forgot to place the ``@JOverride``, it gave you the response:: NotImplementedError: Interface 'gov.hnl.experiment.Monitor' requires method 'onMeasurement' to be implemented. But once you added the ``@JOverride``, it worked properly. The subject appears to be getting impatient so you hurry up and set up a short run to make sure it is working. .. code-block:: python hm = HeartMonitor() experiment.addMonitor(hm) experiment.run() readings = hm.getResults() plt.plot(readings[:,0], readings[:,1) plt.show() To your surprise, it says unable to find method addMonitor with an error message:: AttributeError: 'gov.hnl.experiment.Experiment' object has no attribute 'addMonitor' You open the cell and type ``experiment.add``. The line completes with ``experiment.addMoniter``. Whoops, looks like there is typo in the interface. You make a quick correction and see a nice plot of the last 30 seconds pop up in a window. Job well done, so you set the runtime back to one hour. Looks like you still have time to make the intern woodlands hike and forest picnic. Though you wonder if maybe next year you should sign up for another laboratory. Maybe next year, you will try to sign up for those orbital lasers the President was talking about back in March. That sounds like real fun. (This advanced demonstration utilized the concept of Proxies_ and `Code completion`_) .. _introduction_the_jpype_philosophy: The JPype Philosophy ===================== JPype is designed to provide seamless integration between Python and Java, allowing developers to use Java libraries and features as naturally as possible within Python. To achieve this, JPype adheres to several core design principles: 1. **Make Java appear Pythonic**: - JPype strives to make Java concepts feel familiar to Python programmers. This involves adapting Java syntax and behaviors to align with Python's conventions wherever possible. - For example, Java methods are mapped to Python methods, and Java collections are customized to behave like Python collections. 2. **Make Python appear like Java**: - JPype ensures that Java developers can work with Python without a steep learning curve. This includes presenting Python constructs in a way that resembles Java syntax and behavior. - For instance, Python classes can implement Java interfaces, and Java objects can be manipulated using Python's object-oriented features. 3. **Expose all of Java to Python**: - JPype aims to provide access to the entirety of the Java ecosystem, including libraries, packages, and features. The goal is to act as a bridge, enabling unrestricted interaction between the two languages. - Whether it's Java threading, reflection, or advanced APIs, JPype ensures that Python developers can leverage Java's full capabilities. 4. **Keep the design simple**: - Mixing two languages is inherently complex, so JPype minimizes additional complexity by maintaining a simple and consistent design. - For example, all Java array types originate from the `JArray` factory, ensuring a unified approach to handling arrays. 5. **Favor clarity over performance**: - While JPype optimizes critical paths for performance, clarity is prioritized to ensure long-term maintainability and usability. - For example, JPype avoids premature optimization that could complicate the codebase or introduce unnecessary constraints. 6. **Introduce familiar methods**: - When new methods are added, JPype ensures they align with established conventions in both Python and Java. - For example, Python's ``memoryview`` is used to access Java-backed memory, while Java's ``Stream.of`` inspired the ``JArray.of`` method for converting NumPy arrays to Java arrays. 7. **Provide obvious solutions for both Python and Java programmers**: - JPype recognizes that "obviousness" varies between Python and Java developers. Therefore, it provides solutions that feel natural to both audiences. - For example, Python programmers can use list comprehensions with Java collections, while Java programmers can use familiar methods like ``contains`` or ``hashCode``. **Balancing Two Worlds** JPype bridges two distinct programming paradigms: Python's dynamic and flexible nature versus Java's strongly-typed and structured approach. This balance requires careful mapping of concepts between the two languages: - **Types**: - Python's weak typing allows variables to change types dynamically, while Java's strong typing enforces strict type declarations. JPype accommodates this difference by providing type factories (``JClass``, ``JArray``) and casting operators (``@``). - **Inheritance**: - Java supports single inheritance with interfaces, while Python allows multiple inheritance. JPype maps Java interfaces to Python classes using decorators (``@JImplements``) to ensure compatibility. - **Collections**: - Java collections (``List``, ``Map``, ``Set``) are customized to behave like Python collections, enabling intuitive interaction for Python developers. - **Error Handling**: - Java exceptions are mapped to Python exceptions, allowing developers to handle errors seamlessly across both languages. **Philosophy in Practice** JPype's design philosophy ensures a small footprint while offering high levels of integration between Python and Java. Developers can use JPype to: - Access Java libraries for tasks that Python lacks native support for (e.g., advanced threading, enterprise APIs). - Use Python's interactive and visualization capabilities to debug or analyze Java data structures. - Combine Python's flexibility with Java's robustness for scientific computing, machine learning, and enterprise applications. By adhering to these principles, JPype provides a powerful yet accessible tool for bridging the Python and Java ecosystems. .. _introduction_languages_other_than_java: Languages Other Than Java ========================= Although JPype is primarily designed to bridge Python with Java, its capabilities extend to other JVM-based languages such as Kotlin, Scala, Groovy, and Clojure. These languages share the same underlying Java Virtual Machine (JVM) infrastructure, allowing JPype to interact with them seamlessly. However, each language introduces unique features and paradigms that may require additional considerations when integrating with Python. .. _introduction_supported_jvmbased_languages: Supported JVM-Based Languages ----------------------------- 1. **Kotlin**: - Kotlin is a modern JVM-based language that emphasizes conciseness and safety. JPype can interact with Kotlin libraries and classes just as it does with Java. - Kotlin's null safety and extension functions are fully compatible with JPype, though developers may need to handle Kotlin's nullable types explicitly when working in Python. - Example: Using Kotlin's ``List`` class in Python via JPype. .. code-block:: python from kotlin.collections import List my_list = List.of("apple", "orange", "banana") print(my_list.size()) # Access Kotlin methods 2. **Scala**: - Scala combines object-oriented and functional programming paradigms, making it a popular choice for big data and distributed systems. - JPype can interact with Scala libraries, including those built on frameworks like Akka or Spark. - Scala's collections and functional constructs (e.g., `map`, `flatMap`) can be accessed directly from Python, though some functional idioms may require adaptation. .. code-block:: python from scala.collection.mutable import ArrayBuffer buffer = ArrayBuffer() buffer.append(1) buffer.append(2) print(buffer.mkString(", ")) # Outputs: "1, 2" 3. **Groovy**: - Groovy is a dynamic language for the JVM, often used for scripting and lightweight application development. - JPype can interact with Groovy scripts and libraries, enabling Python developers to leverage Groovy's concise syntax and dynamic capabilities. - Groovy's dynamic typing aligns well with Python, but accessing from within JPype may cause difficulties. .. code-block:: python from groovy.util import Eval result = Eval.me("3 + 5") print(result) # Outputs: 8 4. **Clojure**: - Clojure is a functional programming language that runs on the JVM. Its emphasis on immutability and concurrency makes it ideal for certain types of applications. - JPype can interact with Clojure libraries, though developers may need to adapt to Clojure's Lisp-like syntax and functional paradigms. .. code-block:: python from clojure.lang import PersistentVector vector = PersistentVector.create([1, 2, 3]) print(vector.nth(1)) # Access elements using Clojure methods .. _introduction_using_jpype_with_other_jvm_languages: Using JPype with Other JVM Languages ------------------------------------ JPype can be used with JVM-based languages, but the following considerations apply: 1. **Language-Specific Features**: - Each language introduces unique features (e.g., Kotlin's null safety, Scala's functional constructs, Groovy's dynamic typing). These may require adaptation when working with Python. 2. **Interoperability**: - JPype relies on the JVM's native interoperability mechanisms, ensuring seamless interaction with JVM-based languages. However, developers should be aware of differences in naming conventions, type systems, and runtime behavior. 3. **Testing and Integration**: - To fully support a JVM-based language, developers should set up a test bench to exercise its features, write language-specific quick-start guides, and ensure compatibility with JPype's existing API. .. _introduction_expanding_jpype_for_other_jvm_languages: Expanding JPype for Other JVM Languages --------------------------------------- If you wish to extend JPype's capabilities for a specific JVM-based language, the following steps are recommended: 1. **Create a Test Bench**: - Set up a test environment for your language under JPype's test directory. Use Ivy or Maven to pull in the required JAR files and exercise the language's unique features. 2. **Write a Language-Specific Guide**: - Document how your language interacts with JPype, highlighting differences from Java and providing examples for common use cases. 3. **Set Up a Test Harness**: - Build a test harness to verify compatibility for each language feature. Place the setup script (e.g., `test_kotlin`, `test_scala`) alongside JPype's existing tests. .. _introduction_conclusion_on_languages: Conclusion on Languages ----------------------- JPype's ability to interact with JVM-based languages opens up exciting possibilities for Python developers. Whether you're working with Kotlin's modern syntax, Scala's functional paradigms, Groovy's dynamic scripting, or Clojure's immutability, JPype provides a powerful bridge to leverage the strengths of these languages within Python. By following the steps outlined above, you can ensure smooth integration and expand JPype's capabilities for your specific needs. .. _introduction_alternatives: Alternatives ============ JPype is not the only Python module of its kind that acts as a bridge to Java. Depending on your programming requirements, one of the alternatives may be a better fit. Specifically, JPype is designed for clarity and high levels of integration between the Python and Java virtual machines. As such, it makes use of JNI and inherits all the benefits and limitations that JNI imposes. With JPype, both virtual machines run in the same process, sharing the same memory space and threads. JPype can intermingle Python and Java threads and exchange memory quickly. However, the JVM cannot be restarted within the same process, and if Python crashes, Java will also terminate since they share the same process. Below is a comparison of JPype with other Python-to-Java bridging technologies. These alternatives may suit different use cases depending on the level of integration, performance requirements, or ease of use. .. _introduction_py4j: Py4J --------------------------- `Py4J `_ is a Python library that enables communication with a JVM through a remote tunnel. Unlike JPype, which embeds the JVM directly into the Python process using JNI, Py4J operates the JVM as a separate process, allowing Python and Java to run independently. This separation introduces several unique advantages: 1. **Cross-Architecture Compatibility**: Py4J allows Python and Java to run on different architectures or platforms. For example, you can run Python on a 64-bit architecture while connecting to a 32-bit JVM, or even run Python and Java on entirely different machines. This flexibility is particularly useful for distributed systems or environments where the Python and Java components have different hardware or software requirements. 2. **Restartable Java Sessions**: Because Py4J operates the JVM as a separate process, it is possible to stop and restart the JVM without restarting the Python process. This is a feature frequently requested by JPype users but is not feasible with JPype due to its use of JNI, which tightly couples the Python and Java memory spaces. Py4J's ability to restart the JVM makes it suitable for applications requiring dynamic lifecycle management of the Java environment. 3. **Memory Isolation**: Since Python and Java run in separate processes, Py4J provides complete memory isolation between the two environments. This ensures that a crash in the JVM does not affect the Python process and vice versa. Such isolation can be critical for applications requiring high reliability and fault tolerance. 4. **RPC-Style Communication**: Py4J operates more like a remote procedure call (RPC) framework, where Python sends commands to the JVM and receives responses. While this approach is less integrated than JPype's direct JNI-based interaction, it is intended for applications where tight coupling between Python and Java is not required. Despite these advantages, Py4J has some limitations compared to JPype: - **Performance**: The remote communication introduces a transfer penalty when moving data between Python and Java, making Py4J less suitable for applications requiring high-performance data exchange. - **Integration**: Py4J does not provide the seamless integration of Java objects into Python syntax that JPype offers. For example, Java collections and arrays do not behave like native Python objects. Py4J is a good choice for applications requiring cross-architecture compatibility, restartable JVM sessions, or memory isolation between Python and Java. However, for applications needing tight integration and high-performance data exchange, JPype may be a better fit. .. _introduction_jep: Jep ------- `Jep `_ stands for Java embedded Python. It is designed to allow Java to access Python as a sub-interpreter. The syntax for accessing Java resources from within the embedded Python is similar to JPype, with support for imports. However, Jep has limitations due to Python's sub-interpreter model, which restricts the use of many Python modules. Additionally, Jep's documentation is sparse, making it difficult to assess its full capabilities without experimentation. Jep is best suited for applications where Java needs to embed Python for scripting purposes. .. _introduction_pyjnius: PyJnius ------- PyJnius _ is another Python-to-Java bridge. Its syntax is somewhat similar to JPype, allowing classes to be loaded and accessed with Java-native syntax. PyJnius supports customization of Java classes to make them appear more Pythonic. However, PyJnius lacks support for primitive arrays, requiring Python lists to be converted manually whenever an array is passed as an argument or return value. This limitation makes PyJnius less suitable for scientific computing or applications requiring efficient array manipulation. PyJnius is actively developed and is particularly focused on Android development, making it a strong choice for mobile applications requiring Python-Java integration. .. _introduction_jython: Jython ------ Jython _ is a reimplementation of Python in Java. It allows Python code to run directly on the JVM, providing seamless access to Java libraries. Jython, while limited to Python 2, played a significant role in bridging Python and Java in earlier development eras. It may still be useful for legacy systems or environments where Python 2 compatibility is required. Its development has largely stalled, and it lacks support for popular Python libraries like NumPy and pandas, making it unsuitable for modern applications. .. _introduction_javabridge: Javabridge ----------- `Javabridge `_ provides direct low-level JNI control from Python. Its integration level is low, offering only the JNI API to Python rather than attempting to wrap Java in a Python-friendly interface. While Javabridge can be useful for advanced users familiar with JNI, it requires significant expertise to use effectively. Javabridge is best suited for applications needing fine-grained control over JNI interactions. .. _introduction_jcc: JCC --- `JCC `_ is a C++ code generator that produces a C++ object interface wrapping a Java library via JNI. JCC also generates C++ wrappers conforming to Python's C type system, making instances of Java classes directly available to a Python interpreter. JCC is actively maintained as part of PyLucene and is useful for exposing specific Java libraries to Python rather than providing general Java access. It is best suited for applications requiring tight integration with libraries like Apache Lucene. It is best suited for applications requiring tight integration with specific Java libraries. .. _introduction_about_this_guide: About this Guide ================ The JPype User Guide is designed for two primary audiences: 1. **Python Programmers**: Those who are proficient in Python and wish to leverage Java libraries or integrate Java functionality into their Python projects. 2. **Java Programmers**: Those who are experienced in Java and want to use Python as a development tool for Java, particularly for tasks like visualization, debugging, or scripting. This guide aims to bridge the gap between these two languages by comparing and contrasting their differences, providing examples that illustrate how to translate concepts from one language to the other. It assumes that readers are proficient in at least one of the two languages. If you lack a strong background in either Python or Java, you may need to consult tutorials or introductory materials for the respective language before proceeding. Key Features of the Guide ------------------------- - **No JNI Knowledge Required**: JPype abstracts away the complexities of the Java Native Interface (JNI). Users do not need to understand JNI concepts or its naming conventions to use JPype effectively. In fact, relying on JNI knowledge may lead to incorrect assumptions about the JPype API. Where JNI imposes limitations, the guide explains the consequences in practical programming terms. - **Python 3 Compatibility**: JPype supports only Python 3. All examples in this guide use Python 3 syntax and assume familiarity with Python's new-style object model. If you're using an older version of Python, you will need to upgrade to Python 3 to use JPype. - **Java Naming Conventions**: JPype adheres to Java's naming conventions for methods and fields to ensure consistency and avoid potential name collisions. While this may differ from Python's conventions, it is a deliberate choice to maintain compatibility with Java libraries and APIs. By following this guide, you’ll learn how to use JPype to seamlessly integrate Python and Java, unlocking the strengths of both languages in your projects. .. _introduction_getting_jpype_started: Getting JPype started --------------------- This document holds numerous JPype examples. For the purposes of clarity the module is assumed to have been started with the following command .. code-block:: python # Import the module import jpype # Allow Java modules to be imported import jpype.imports # Import all standard Java types into the global scope from jpype.types import * # Import each of the decorators into the global scope from jpype import JImplements, JOverride, JImplementationFor # Start JVM with Java types on return jpype.startJVM() # Import default Java packages import java.lang import java.util This is not the only style used by JPype users. Some people feel it is best to limit the number for symbols in the global scope and instead start with a minimalistic approach. .. code-block:: python import jpype as jp # Import the module jp.startJVM() # Start the module Either style is usable and we do not wish to force any particular style on the user. But as the extra ``jp.`` tends to just clutter up the space and implies that JPype should always be used as a namespace due to namespace conflicts, we have favored the global import style. JPype only exposes 40 symbols total including a few deprecated functions and classes. The 13 most commonly used Java types are wrapped in a special module ``jpype.types`` which can be used to import all for the needed factories and types with a single command without worrying about importing potentially problematic symbols. We will detail the starting process more later in the guide. See `Starting the JVM`_. .. _introduction_jpype_concepts: JPype Concepts ============== At its heart, JPype is about providing a bridge to use Java within Python. Depending on your perspective, this can either be a means of accessing Java libraries from within Python or a way to use Java with Python syntax for interactivity and visualization. JPype aims to provide access to the entirety of the Java language from Python, mapping Java concepts to their closest Python equivalents wherever possible. Python and Java share many common concepts, such as types, classes, objects, functions, methods, and members. However, there are significant differences between the two languages. For example, Python lacks features like casting, type declarations, and method overloading, which are central to Java's strongly typed paradigm. JPype introduces these concepts into Python syntax while striving to maintain Pythonic usability. This section breaks down JPype's core concepts into nine distinct categories. These categories define how Java elements are mapped into Python and how they can be used effectively. .. _introduction_core_concepts: Core Concepts ------------- 1. **Type Factories**: - Type factories allow you to declare specific Java types in Python. These factories produce wrapper classes for Java types. - Examples include `JClass` for Java classes and `JArray` for Java arrays. - Factories also exist for implementing Java classes from within Python using proxies (e.g., `JProxy`). 2. **Meta Classes**: - Meta classes describe properties of Java classes, such as whether a class is an interface. - Example: `JInterface` can be used to check if a Java class is an interface. 3. **Base Classes**: - JPype provides base classes for common Java types, such as `Object`, `String`, and `Exception`. - These classes can be used for convenience, such as catching all Java exceptions with `JException`. - Example: `java.lang.Throwable` can be caught using `JException`. 4. **Wrapper Classes**: - Wrapper classes correspond to individual Java classes and are dynamically created by JPype. These wrappers encapsulate Java objects and provide a Pythonic interface for interacting with them. Depending on the context, a wrapper may contain a Java reference, such as a class instance, primitive array, or boxed type or a Java proxy which implements dynamically implements a Java interface. - Wrappers are designed to make Java objects behave like native Python objects, enabling seamless integration between Python and Java. These wrappers provide a Pythonic interface to Java objects, making them behave like native Python objects while retaining their Java functionality. - They allow access to static variables, static methods, constructors, and casting. - Example: `java.lang.Object`, `java.lang.String`. 5. **Object Instances**: - These are Java objects created or accessed within Python. They behave like Python objects, with Java fields mapped to Python attributes and Java methods mapped to Python methods. - Example: A Java `String` object can be accessed and manipulated like a Python string. 6. **Primitive Types**: - JPype maps Java's primitive types (e.g., `boolean`, `int`, `float`) into Python classes. - Example: `JInt`, `JFloat`, `JBoolean`. 7. **Decorators**: - JPype provides decorators to augment Python classes and methods with Java-specific functionality. - Examples include `@JImplements` for implementing Java interfaces and `@JOverride` for overriding Java methods. 8. **Mapping Java Syntax to Python**: - JPype maps Java syntax to Python wherever possible. For example: - Java's `try`, `throw`, and `catch` are mapped to Python's `try`, `raise`, and `except`. - Java's `synchronized` keyword is mapped to Python's `with` statement using `jpype.synchronized`. 9. **JVM Control Functions**: - JPype provides functions for controlling the JVM, such as starting and shutting it down. - Examples: `jpype.startJVM()` and `jpype.shutdownJVM()`. .. _introduction_additional_details: Additional Details ------------------ - **Name Mangling**: - JPype handles naming conflicts between Java and Python by appending an underscore (`_`) to conflicting names. - Example: A Java method named `with` will appear as `with_` in Python. - For details see `Name Mangling`_. - **Lifetime Management**: - Java objects remain alive as long as their corresponding Python handles exist. Once the Python handle is disposed, the Java object is eligible for garbage collection. By understanding these core concepts, you can effectively use JPype to integrate Python and Java, leveraging the strengths of both languages. .. _introduction_best_practices: Best Practices on JVM Startup ----------------------------- Starting the Java Virtual Machine (JVM) correctly is critical for ensuring the smooth operation of JPype-based applications. A well-configured JVM startup process minimizes runtime issues, optimizes performance, and ensures compatibility with the required Java libraries. This section provides a detailed explanation of best practices to guide developers in setting up the JVM effectively. 1. Start the JVM Early The JVM should always be started early in the application lifecycle. By initializing the JVM at the beginning of your program, you can avoid issues related to delayed imports or incomplete initialization. This approach ensures that all Java classes and libraries required by your application are properly loaded and accessible throughout the program's execution. 2. Configure the Classpath Explicitly Classpath configuration is another essential consideration. The ``classpath`` specifies the location of Java classes and JAR files that the JVM needs to load. For optimal results, explicitly define the ``classpath`` when starting the JVM. This can be done using the ``classpath`` argument in the ``startJVM()`` function or dynamically through the ``addClassPath()`` method prior to JVM startup. Explicit configuration prevents errors caused by missing dependencies and ensures that the correct versions of libraries are loaded. 3. Disable Automatic String Conversion When dealing with large-scale data transfers or computationally intensive operations, it is advisable to disable automatic string conversion by setting the ``convertStrings`` argument to ``False``. This prevents unnecessary overhead caused by automatic conversion of Java strings to Python strings, allowing developers to retain control over string handling and improve performance. While enabling automatic string conversion may seem convenient, it is considered a legacy option and should be avoided in modern applications. 4. Avoid Restarting the JVM It is important to note that the JVM cannot be restarted once it has been shut down. Therefore, design your application to start the JVM once and keep it running for the program's lifetime. Attempting to restart the JVM will result in errors due to lingering references and resource conflicts. This limitation underscores the importance of careful planning when initializing the JVM. .. _optimize_data_transfers: 5. Optimize Data Transfers When Python and Java need to exchange large amounts of data, such as arrays or complex structures, the efficiency of these transfers can significantly impact application performance. Without optimization, frequent back-and- forth calls between Python and Java can create bottlenecks, especially in computationally intensive applications like scientific computing or machine learning. To ensure smooth data exchange, consider the following strategies: 1. **Use NumPy Arrays**: NumPy arrays integrate seamlessly with JPype and allow fast, memory-efficient data transfers to Java. For example, a NumPy array can be mapped directly to a Java primitive array, enabling high-speed operations without unnecessary copying. 2. **Leverage Java Buffers**: Java's `nio` buffers provide a mechanism for shared memory between Python and Java. These buffers are particularly useful for large datasets or memory-mapped files, as they eliminate the overhead of repeated conversions and allow both languages to operate on the same memory space. 3. **Cache Java Objects**: If a Java object is used repeatedly in Python, consider caching it to reduce the frequency of cross-language calls. This avoids redundant conversions and improves overall runtime efficiency. 4. **Validate Data Structures**: Ensure that arrays or collections being transferred are rectangular and compatible with the expected Java types. For example, jagged arrays or incompatible data types can lead to errors or performance degradation. By implementing these strategies, you can optimize the interaction between Python and Java, ensuring that your application performs efficiently even when handling large-scale data or computationally intensive tasks. 6. Handle Exceptions Properly Exception handling is another key aspect of JVM startup. Always catch Java exceptions using ``jpype.JException`` or specific Java exception classes to ensure robust error handling. When debugging issues, the ``stacktrace()`` method can provide detailed information about Java exceptions, helping developers identify and resolve problems effectively. 7. Document Your Setup Finally, document the JVM startup process and configuration settings clearly within your codebase. This practice not only aids in debugging but also ensures that other developers working on the project can understand and replicate the setup. By adhering to these best practices, you can maximize the reliability, performance, and maintainability of your JPype-based applications. By adhering to these best practices, you can maximize the performance, reliability, and maintainability of your JPype-based applications. .. _jpype_types: JPype Types *********** Both Java and Python have a concept of a type. Every variable refers to an object which has a defined type. A type defines the data that the variable is currently holding and how that variable can be used. In this chapter we will learn how Java and Python types relate to one another, how to create import types from Java, and how to use types to create Java objects. .. _jpype_types_stay_strong_in_a_weak_language: Stay strong in a weak language ============================== Before we get into the details of the types that JPype provides, we first need to contrast some of the fundamental language differences between Java and Python. Python is inherently a weakly typed language. Any variable can take any type and the type of a particular variable can change over the lifetime of a program. Types themselves can be mutable as you can patch an existing type to add new behaviors. Python methods can in principle take any type of object as an argument, however if the interface is limited it will produce a TypeError to indicate a particular argument requires a specific type. Python objects and classes are open. Each class and object is basically a dictionary storing a set of key-value pairs. Types implemented in native C are often more closed and thus can't have their method dictionaries or data members altered arbitrarily. But subject to a few restrictions based implementation, it is pretty much the wild west. In contrast, Java is a strongly typed language. Each variable can only take a value of the specified class or a class that derives from the specified class. Each Java method takes only a specific number and type of arguments. The type and number are all checked at compile type to ensure there is little possibility of error. As each method requires a specific number and type of arguments, a method can be overloaded by having two different implementations which take a different list of types sharing the same method name. A primitive variable can never hold an object and it can only be converted to or from other primitive types unless it is specifically cast to that type. Java objects and classes are completely closed. The methods and fields for a particular class and object are defined entirely at compile time. Though it is possible create classes with a dictionary allowing expansion, this is not the Java norm and no standard mechanism exists. Thus we need to introduce a few Java terms to the Python vocabulary. These are "conversion" and "cast". .. _jpype_types_java_conversions: Java conversions ---------------- A conversion is a permitted change from an object of one type to another. Conversions have three different degrees. These are: exact, derived, implicit, and explicit. Exact conversions are those in which the type of an object is identical. In Java each class has only one definition thus there is no need for an exact conversion. But when dealing with Python we have objects that are effectively identical for which exact conversion rules apply. For example, a Java string and a Python string both bind equally well to a method which requires a string, thus this is an exact conversion for the purposes of bind types. The next level of conversion is derived. A derived class is one which is a descends from a required type. It is better that implicit but worse than exact. If all of the types in a method match are exact or derived then it will override a method in which one argument is implicit. The next level of conversion is implicit. An implicit conversion is one that Java would perform automatically. Java defines a number of other conversions such as converting a primitive to a boxed type or from a boxed type back to a primitive as implicit conversions. Python conversions defined by the user are also considered to be implicit. Of course not every cast is safe to perform. For example, converting an object whose type is currently viewed as a base type to a derived type is not performed automatically nor is converting from one boxed type to another. For those operations the conversion must be explicitly requested, hence these are explicit conversions. In Java, a cast is requested by placing the type name in parentheses in front of the object to be cast. Python does not directly support Java casting syntax. To request an explicit conversion an object must be "cast" using a cast operator @. Overloaded methods with an explicit argument will not be matched. After applying an explicit cast, the match quality can improve to exact or derived depending on the cast type. Not every conversion is possible between Java types. Types that cannot be converted are considerer to be conversion type "none". Details on how method overloads are resolved are given in `Method Resolution`_. Details on the standard conversions provided by JPype are given in the section `Type Matching`_. .. _cast: Java casting ------------ To access a casting operation we use the casting ``JObject`` wrapper. For example, ``JObject(object, Type)`` would produce a copy with specificed type. The first argument is the object to convert and the second is the type to cast to. The second argument should always be a Java type specified using a class wrapper, a Java class instance, or a string. Casting will also add a hidden class argument to the resulting object such that it is treated as the cast type for the duration of that variable lifespan. Therefore, a variable create by casting is stuck as that type and cannot revert back to its original for the purposes of method resolution. The object construction and casting are sometimes a bit blurry. For example, when one casts a sequence to a Java list, we will end up constructing a new Java list that contains the elements of the original Python sequence. In general JPype constructors only provide access the Java constructor methods that are defined in the Java documentation. Casting on the other hand is entirely the domain of whatever JPype has defined including user defined casts. As ``JObject`` syntax is long and does not look much like Java syntax, the Python matmul operator is overloaded on JPype types such that one can use the ``@`` operator to cast to a specific Java type. In Java, one would write ``(Type)object`` to cast the variable ``object`` to ``Type``. In Python, this would be written as ``Type@object``. This can also be applied to array types ``JLong[:]@[1,2,3]``, collection types ``Iterable@[1,2,3]`` or Java functors ``DoubleUnaryOperator@(lambda x:x*2)``. The result of the casting operator will be a Java object with the desired type or raise a ``TypeError`` if the cast or conversion is not possible. For Python objects, the Java object will generally be a copy as it is not possible to reflect changes in an array back to Python. If one needs to retrieve the resulting changes keep a copy of the converted array before passing it. For an existing Java object, casting changes the resolution type for the object. This can be very useful when trying to call a specific method overload. For example, if we have a Java ``a=String("hello")`` and there were an overload of the method ``foo`` between ``String`` and ``Object`` we would need to select the overload with ``foo(java.lang.Object@a)``. .. _JObject: Casting is performed through the Python class ``JObject``. JObject is called with two arguments which are the object to be cast and the type to cast too. The cast first consults the conversion table to decide if the cast it permitted and produces a ``TypeError`` if the conversion is not possible. ``JObject`` also serves as a abstract base class for testing if an object instance belongs to Java. All objects that belong to Java will return true when tested with ``isinstance``. Like Python's sequence, JObject is an abstract base class. No classes actual derive from ``JObject``. .. _null: Of particular interest is the concept of Java ``null``. In Java, null is a typeless entity which can be placed wherever an object is taken to indicate that the object is not available. The equivalent concept in Python is ``None``. Thus all methods that accept any object type that permit a null will accept None as an augment with implicit conversion. However, sometime it is necessary to pass an explicit type to the method resolution. To achieve this in JPype use ``Type@None`` which will create a null pointer with the desired type. To test if something is null we have to compare the handle to None. This unfortunately trips up some code quality checkers. The idiom in Python is ``obj is None``, but as this only matches things that Python considers identical, we must instead use ``obj==None``. Casting ``None`` is use to specify types when calling between overloads with variadic arguments such as ``foo(Object a)`` and ``foo(Object... many)``. If we want to call ``foo(None)`` is is ambiguous whether we intend to call the first with a null object or the second with a null array. We can resolve the ambiguity with ``foo(java.lang.Object@None)`` or ``foo(java.lang.Object[:]@None)`` Type enforcement appears in three different places within JPype. These are whenever a Java method is called, whenever a Java field is set, and whenever Python returns a value back to Java. Primitive Types =============== Unlike Python, Java makes a distinction between objects and primitive data types. Primitives represent the minimum data that can be manipulated by a computer. These stand in contrast to objects, which have the ability to contain any combination of data types and objects within themselves, and can be inherited from. Java primitives come in three categories: - **Logical**: `boolean` (true/false values). - **Textual**: `char` (single Unicode character). - **Numerical**: Fixed-point or floating-point numbers of varying sizes. JPype maps Java primitives to Python classes. To avoid naming conflicts with Python, JPype prefixes each primitive type with `J` (e.g., `JBoolean`, `JInt`). .. _jpype_types_primitive_types_jboolean: JBoolean -------- Represents a logical value (`True` or `False`). In JPype, `True` and `False` are exact matches for `JBoolean`. Methods returning a `JBoolean` will always return a Python `bool`. .. code-block:: python # Example usage java_boolean = JBoolean(True) print(java_boolean) # Output: True .. _jpype_types_primitive_types_jchar: JChar ----- Represents a single character. Java `char` types are 16-bit Unicode characters, but some Unicode characters require more than 16 bits. JPype maps `JChar` to Python strings of length 1. While `JChar` supports numerical operations, modifying characters numerically can corrupt their encoding. .. code-block:: python # Example usage java_char = JChar('A') print(java_char) # Output: 'A' .. _jpype_types_primitive_types_jbyte,_jshort,_jint,_jlong: JByte, JShort, JInt, JLong -------------------------- These types represent signed integers of varying sizes: - **JByte**: 8 bits - **JShort**: 16 bits - **JInt**: 32 bits - **JLong**: 64 bits JPype maps these types to Python's `int`. Methods returning integer primitives will return Python `int` values. Methods accepting integer primitives will accept Python integers or any object that can be converted into the appropriate range. .. code-block:: python # Example usage java_int = JInt(42) print(java_int) # Output: 42 .. _jpype_types_jfloat_jdouble: JFloat, JDouble --------------- These types represent floating-point numbers: - **JFloat**: 32-bit precision - **JDouble**: 64-bit precision JPype maps these types to Python's `float`. Numbers exceeding the range of `JFloat` or `JDouble` will result in positive or negative infinity. Range checks are performed when converting Python types, and an `OverflowError` will be raised if the value is out of bounds. .. code-block:: python # Example usage java_double = JDouble(3.14) print(java_double) # Output: 3.14 .. _jpype_types_objects__classes: Objects & Classes ================= In contrast to primitive data type, objects can hold any combination of primitives or objects. Thus they represent structured data. Objects can also hold methods which operate on that data. Objects can inherit from one another. However unlike Python, Java objects must have a fixed structure which defines its type. These are referred to the object's class. Here is a point of confusion. Java has two different class concepts: the class definition and the class instance. When you import a class or refer to a method using the class name you are accessing the class definition. When you call ``getClass`` on an object it returns a class instance. The class instance is a object whose structure can be used to access the data and methods that define the class through reflection. The class instance cannot directly access the fields or method within a class but instead provides its own interface for querying the class. For the purposes of this document a "class" will refer to the class definition which corresponds to the Python concept of a class. Wherever the Java reflection class is being referred to we will use the term "class instance". The term "type" is synonymous with a "class" in Java, though often the term "type" is only used when inclusively discussing the type of primitives and objects, while the term "class" generally refers to just the types associated with objects. All objects in Java inherit from the same base class ``java.lang.Object``, but Java does not support multiple inheritance. Thus each class can only inherit from a single parent. Multiple inheritance, mix-ins, and diamond pattern are not possible in Java. Instead Java uses the concept of an interface. Any Java class can inherit as many interfaces as it wants, but these interfaces may not contain any data elements. As they do not contain data elements there can be no ambiguity as to what data a particular lookup. .. _JInterface: The meta class ``JInterface`` is used to check if a class type is an interface using ``isinstance``. Classes that are pure interfaces cannot be instantiated, thus, there is not such thing as an abstract instance. Therefore, every Java object should have Objects cannot actual be pure interfaces. To represent this in Python every interface inherits ``java.lang.Object`` methods even through it does not have ``java.lang.Object`` as a parent. This ensures that anonymous classes and lambdas have full object behavior. .. _jpype_types_classes: Classes ------- In JPype, Java classes are instances of the Python ``type`` and function like any ordinary Python class. However unlike Python types, Java classes are closed and cannot be extended. To enforce extension restrictions, all Java classes are created from a special private meta class called ``_jpype._JClass``. This gatekeeper ensures that the attributes of classes cannot be changed accidentally nor extended. The type tree of Java is fixed and closed. All Java classes have the following functionality. Class constructor The class constructor is accessed by using the Python call syntax ``()``. This special method invokes a dispatch whenever the class is called as a function. If an matching constructor is found a new Java instance is created and a Python handle to that instance is returned. In the case of primitive types, the constructor creates a Java value with the exact type requested. Get attribute The Python ``.`` operator gets an attribute from a class with a specified name. If no method or field exists a ``AttributeError`` will be raised. For public static methods, the getattr will produce a Python descriptor which can be called to invoke the static method. For public static fields, a Python descriptor will be produced that allows the field to be get or set depending on whether the field is final or not. Public instance methods and instance fields will produce a function that can be applied to a Java object to execute that method or access the field. Function accessors are non-virtual and thus they can provide access to behaviors that have been hidden by a derived class. Set attribute In general, JPype only allows the setting of public non-final fields. If you attempt to set any attribute on an object that does not correspond to a settable field it will produce an ``AttributeError``. There is one exception to this rule. Sometime it is necessary to attach addition private meta data to classes and objects. Attributes that begin with an underbar are consider to be Python private attributes. Private attributes handled by the default Python attribute handler allowing these attributes to be attached to to attach data to the Python handle. This data is invisible to Java and it is retained only on the Python instance. If an object with Python meta data is passed to Java and Java returns the object, the new Python handle will not contain any of the attached data as this data was lost when the object was passed to Java. ``class_`` Attribute For Java classes there is a special attribute called ``class``. This is a keyword in Python so `name mangling`_ applies. This is a class instance of type ``java.lang.Class``. It can be used to access fields and methods. Inner classes For methods and fields, public inner classes appear as attributes of the class. These are regular types that can be used to construct objects, create array, or cast. String The Java method ``toString`` is mapped into the Python function ``str(obj)``. Equality The Java method ``equals()`` has been mapped to Python ``==`` with special augmentations for null pointers. Java ``==`` is not exposed directly as it would lead to numerous errors. In principle, Java ``==`` should map to the Python concept of ``is`` but it is not currently possible to overload Python in such a way to achieve the desired effect. Hash The Java method ``hashCode`` is mapped to Python ``hash(obj)`` function. There are special augmentations for strings and nulls. Strings will return the same hash code as returned by Python so that Java strings and Python strings produce the same dictionary lookups. Null pointers produce the same hash value as None. Java defines ``hashCode`` on many objects including mutable ones. Often the ``hashCode`` for a mutable object changes when the object is changed. Only use immutable Java object (String, Instant, Boxed types) as dictionary keys or risk undefined behavior. Java objects are instances of Java classes and have all of the methods defined in the Java class including static members. However, the get attribute method converts public instance members and fields into descriptors which act on the object. Now that we have defined the basics of Java objects and classes, we will define a few special classes that operate a bit differently. .. _jpype_types_array_classes: Array Classes ------------- In Java, all arrays are objects, but they cannot define any methods beyond a limited set of Java array operations. These operations have been mapped into Python to their closest Python equivalent. `JArray` is an abstract base class for all Java array classes. Thus, you can test if something is an array class using ``issubclass``, and check if a Java object is an array using ``isinstance``. Creating Array Types ~~~~~~~~~~~~~~~~~~~~ In principle, you can create an array class using ``JClass``, but the signature required would need to use the proper name as required for the Java method ``java.lang.Class.forName``. Instead, JPype provides two specialized methods to create array types: arrays may be produced through the factory ``JArray`` or through the index operator ``[]`` on any `JClass` instance. .. _JArray: The signature for `JArray` is ``JArray(type, [dims=1])``. The `type` argument accepts any Java type, including primitives, and constructs a new array class. This class can be used to create new instances, cast, or serve as the input to the array factory. The resulting object has a constructor method that takes either: - A number, which specifies the desired size of the array. - A sequence, which provides the elements of the array. If the members of the initializer sequence are not Java objects, each will be converted. If any element cannot be converted, a ``TypeError`` will be raised. As a shortcut, the ``[]`` operator can be used to specify an array type or create a new instance of an array with a specified length. You can also create multidimensional arrays or arrays with unspecified dimensions after a specific point. This applies to both primitive and object types. Because of the number of options, we will walk through each use case. To create a one-dimensional array type, append ``[:]`` to any Java class or primitive type. For example: - ``JInt[:]`` creates a Java array type for integers. - ``java.lang.Object[:]`` creates a Java array type for objects. - ``java.util.List[:]`` creates a Java array type for lists. Once the array type is created, you can use it to construct arrays, cast Python sequences to Java arrays, or define multidimensional arrays. .. code-block:: python # Example: Creating array types int_array_type = JInt[:] object_array_type = java.lang.Object[:] # Creating arrays int_array = int_array_type([1, 2, 3]) object_array = object_array_type([None, "Hello", 42]) print(int_array) # Output: [1, 2, 3] print(object_array) # Output: [null, Hello, 42] Multidimensional Arrays ~~~~~~~~~~~~~~~~~~~~~~~ JPype supports the creation of multi-dimensional arrays by appending additional dimensions using ``[:]``. For example: - ``JInt[:,:]`` creates a two-dimensional array type for integers. - ``java.lang.Object[:,:]`` creates a two-dimensional array type for objects. - ``JDouble[:,:,:]`` creates a three-dimensional array type for double-precision floating-point numbers. When creating multi-dimensional arrays, you can initialize them using nested Python lists. JPype automatically converts nested lists into the appropriate Java array structure. .. code-block:: python # Example: Creating multidimensional arrays int_2d_array_type = JInt[:, :] int_2d_array = int_2d_array_type([[1, 2], [3, 4]]) print(int_2d_array[0][1]) # Output: 2 # Creating a 3D array double_3d_array_type = JDouble[:, :, :] double_3d_array = double_3d_array_type([[[1.1, 2.2], [3.3, 4.4]], [[5.5, 6.6], [7.7, 8.8]]]) print(double_3d_array[1][0][1]) # Output: 6.6 Jagged Arrays ~~~~~~~~~~~~~ Java supports jagged arrays, which are arrays of arrays with varying lengths. To create jagged arrays in JPype, replace the final dimension with `[:]`. For example: - `JInt[5, :]` creates a jagged array of integers with 5 rows. - `java.lang.Object[3, :]` creates a jagged array of objects with 3 rows. Jagged arrays can be initialized using nested Python lists with varying lengths. .. code-block:: python # Example: Creating jagged arrays jagged_int_array_type = JInt[3, :] jagged_int_array = jagged_int_array_type([[1, 2], [3, 4, 5], [6]]) print(jagged_int_array[1][2]) # Output: 5 Use of Java Arrays ~~~~~~~~~~~~~~~~~~ Java arrays provide several Python methods: - **Get Item**: Arrays are collections of elements. Array elements can be accessed using the Python ``[]`` operator. For multidimensional arrays, JPype uses Java-style access with a series of index operations, such as ``jarray[4][2]``. - **Get Slice**: Arrays can be accessed using slices, like Python lists. The slice operator is ``[start:stop:step]``. Note that array slices are views of the original array, so any alteration to the slice will affect the original array. Use the `clone` method to create a copy of the slice if needed. - **Set Item**: Array items can be set using the Python ``[]=`` operator. - **Set Slice**: Multiple array items can be set using a slice assigned with a sequence. The sequence must have the same length as the slice. If the items being transferred are a buffer, a faster buffer transfer assignment will be used. - **Buffer Transfer**: Buffer transfers from Java arrays work for primitive types. Use Python's ``memoryview(jarray)`` function to create a buffer for transferring data. Memory views of Java arrays are not writable. - **Iteration (For Each)**: Java arrays can be used in Python `for` loops and lopp comprehensions. - **Clone**: Java arrays can be duplicated using the `clone()` method. - **Length**: Arrays in Java have a defined, immutable length. Use Python's ``len(array)`` function to get the array length. Character specialization ~~~~~~~~~~~~~~~~~~~~~~~~ - The Java class `JChar[]` has additional customizations to work better with string types. - Java arrays do not support additional mathematical operations at this time. - Creating a Java array is required for pass-by-reference syntax when using Java methods that modify array contents. .. code-block:: python orig = [1, 2, 3] obj = jpype.JInt[:](orig) a.modifies(obj) # Modifies the array by multiplying all elements by 2 orig[:] = obj # Copies all the values back from Java to Python .. _jpype_types_buffer_classes: Buffer classes -------------- In addition to array types, JPype also supports Java ``nio`` buffer types. Buffers in Java come in two flavors. Array backed buffers have no special access. Direct buffers are can converted to Python buffers with both read and write capabilities. Each primitive type in Java has its own buffer type named based on the primitive type. ``java.nio.ByteBuffer`` has the greatest control allowing any type to be read and written to it. Buffers in Java function are like memory mapped files and have a concept of a read and write pointer which is used to traverse the array. They also have direct index access to their specified primitive type. Java buffer provide an additional Python method: Buffer transfer Buffer transfers from a Java buffer works for a direct buffer. Array backed buffers will raise a ``BufferError``. Use the Python ``memoryview(jarray)`` function to create a buffer that can be used to transfer any portion of a Java buffer out. Memory views of Java buffers are readable and writable. Buffers do not currently support element-wise access. .. _jpype_types_boxed_classes: Boxed Classes ------------- Often one wants to be able to place a Java primitive into a method of fields that only takes an object. The process of creating an object from a primitive is referred to as creating a "boxed" object. The resulting object is an immutable object which stores just that one primitive. Java boxed types in JPype are wrapped with classes that inherit from Python ``int`` and ``float`` types as both are immutable in Python. This means that a boxed type regardless of whether produced as a return or created explicitly are treated as Python types. They will obey all the conversion rules corresponding to a Python type as implicit matches. In addition, they produce an exact match with their corresponding Java type. The type conversion for this is somewhat looser than Java. While Java provides automatic unboxing of a Integer to a double primitive, JPype can implicitly convert Integer to a Double boxed. To box a primitive into a specific type such as to place it into a ``java.util.List`` use ``JObject`` on the desired boxed type or call the constructor for the desired boxed type directly. For example: .. code-block:: python lst = java.util.ArrayList() lst.add(JObject(JInt(1))) # Create a Java integer and box it lst.add(java.lang.Integer(1)) # Explicitly create the desired boxed object JPype boxed classes have some additional functionality. As they inherit from a mathematical type in Python they can be used in mathematical operations. But unlike Python numerical types they can take an addition state corresponding to being equal to a null pointer. The Python methods are not aware of this new state and will treat the boxed type as a zero if the value is a null. To test for null, cast the boxed type to a Python type explicitly and the result will be checked. Casting null pointer will raise a ``TypeError``. .. code-block:: python b = JObject(None, java.lang.Integer) a = b+0 # This succeeds and a gets the value of zero a = int(b)+0 # This fails and raises a TypeError Boxed objects have the following additional functionality over a normal object. Convert to index Integer boxed types can be used as Python indices for arrays and other indexing tasks. This method checks that the value of the boxed type is not null. Convert to int Integer and floating point boxed types can be cast into a Python integer using the ``int()`` method. The resulting object is always of type ``int``. Casting a null pointer will raise a ``TypeError``. Convert to float Integer and floating point boxed types can be cast into a Python float using the ``float()`` method. The resulting object is always of type ``float``. Casting a null pointer will raise a ``TypeError``. Comparison Integer and floating point types implement the Python rich comparison API. Comparisons for null pointers only succeed for ``==`` and ``!=`` operations. Non-null boxed types act like ordinary numbers for the purposes of comparison. .. _jpype_types_number_class: Number Class ------------ The Java class ``java.lang.Number`` is a special type in Java. All numerical Java primitives and Python number types can convert implicitly into a Java Number. ========================== ======================== Input Result ========================== ======================== None java.lang.Number(null) Python int, float java.lang.Number Java byte, NumPy int8 java.lang.Byte Java short, NumPy int16 java.lang.Short Java int, NumPy int32 java.lang.Integer Java long, NumPy int64 java.lang.Long Java float, NumPy float32 java.lang.Float Java double, NumPy float64 java.lang.Double ========================== ======================== Additional user defined conversion are also applied. The primitive types boolean and char and their corresponding boxed types are not considered to numbers in Java. .. _java.lang.Object: Object Class ------------ Although all classes inherit from Object, the object class itself has special properties that are not inherited. All Java primitives will implicitly convert to their box type when placed in an Object. In addition, a number of Python types implicitly convert to a Java object. To convert to a different object type, explicitly cast the Python object prior to placing in a Java object. Here a table of the conversions: ================ ======================= Input Result ================ ======================= None java.lang.Object(null) Python str java.lang.String Python bool java.lang.Boolean Python int java.lang.Number Python float java.lang.Number ================ ======================= In addition it inherits the conversions from ``java.lang.Number``. Additional user defined conversion are also applied. .. _java.lang.String: String Class ------------ The String class in Java is a special representation often pointing either to a dynamically created string or to a constant pool item defined in the class. All Java strings are immutable just like Python strings and thus these are considered to be equivalent classes. Because Java strings are in fact just pointers to blob of bytes they are actually slightly less than a full object in some JVM implementation. This is a violation of the Object Orients (OO) principle, never take something away by inheritance. Unfortunately, Java is a frequent violator of that rule, so this is just one of those exceptions you have to trip over. Therefore, certain operations such as using a string as a threading control with ``notify`` or ``wait`` may lead to unexpected results. If you are thinking about using a Java string in synchronized statement then remember it is not a real object. Java strings have a number of additional functions beyond a normal object. Length Java strings have a length measured in the number of characters required to represent the string. Extended Unicode characters count for double for the purpose of counting characters. The string length can be determined using the Python ``len(str)`` function. Indexing Java strings can be used as a sequence of characters in Python and thus each character can be accessed as using the Python indexing operator ``[]``. Hash Java strings use a special hash function which matches the Python hash code. This ensures that they will always match the same dictionary keys as the corresponding string in Python. The Python hash can be determined using the Python ``hash(str)`` function. Null pointers are not currently handled. To get the actually Java hash, use ``s.hashCode()`` Contains Java strings implement the concept of ``in`` when using the Java method ``contains``. The Java implementation is sufficiently similar that it will work fairly well on strings. For example, ``"I" in java.lang.String("team")`` would be equal to False. Testing other types using the ``in`` operator will likely raise a ``TypeError`` if Java is unable to convert the other item into something that can be compared with a string. Concatenation Java strings can be appended to create a new string which contains the concatenation of the two strings. This is mapped to the Python operator ``+``. Comparison Java strings are compared using the Java method ``compareTo``. This method does not currently handle null and will raise an exception. For each Java strings are treated as sequences of characters and can be used with a for-loop construct and with list comprehension. To iterate through all of the characters, use the Python construct ``for c in str:``. Unfortunately, Java strings do not yet implement the complete list of requirements to act as Python sequences for the purposes of ``collections.abc.Sequence``. .. _JString: The somewhat outdated JString factory is a Python class that pretends to be a Java string type. It has the marginal advantage that it can be imported before the JVM is actually started. Once the JVM is started, its class representation is pointed to ``java.lang.String`` and can be used to construct a new string object or to test if an object is actually a Java string using ``isinstance``. It does not implement any of the other string methods and just serves as convenience class. The more capable ``java.lang.String`` can be imported in place of JString, but only after the JVM is started. String objects may optionally convert to Python strings when returned from Java methods, though this option is a performance issue and can lead to other difficulties. This setting is selected when the JVM is started. See `String Conversions`_ for details. Java strings will cache the Python conversion so we only pay the conversion cost once per string. .. _jpype_types_exception_classes: Exception Classes ----------------- Both Python and Java treat exception classes differently from other objects. Only these types may be caught as part of a try block. Therefore, the exceptions have a special wrapper. Most of the mechanics of exceptions happen under the surface. The one difference between Python and Java is the behavior when the argument is queried. Java arguments can either be the string value, the exception itself, or the internal construction key depending on how the exception came into existence. Therefore, the arguments to a Java exception should never be used as their values are not guaranteed. Java exception can report their stacktrace to Python in two different ways. If printed through the Python stack trace routine, Java exceptions are split between the Python code that raised and a phantom Java ``cause`` which contains the Java exception in Python order. If the debugging information for the Java source is enabled, Python may even print the Java source code lines where the error occurred. If you prefer Java style stack traces then print the result from the ``stacktrace()`` method. Unhandled exception that terminate the program will print the Python style stack trace information. .. _JException: The base class ``JException`` is a special type located in ``jpype.types`` that can be imported prior to the start of the JVM. This serves as the equivalent of ``java.lang.Throwable`` and contains no additional methods. It is currently being phased out in favor of catching the Java type directly. Using ``jpype.JException`` with a class name as a string was supported in previous JPype versions but is currently deprecated. For further information on dealing with exception, see the `Exception Handling`_ section. To create a Java exception use JClass or any of the other importing methods. .. _jpype_types_anonymous_classes: Anonymous Classes ----------------- Sometimes Java will produce an anonymous class which does to have any actual class representation. These classes are generated when a method implements a class directly as part of its body and they serve as a closure with access to some of the variables that were used to create it. For the purpose of JPype these classes are treated as their parents. But this is somewhat problematic when the parent is simply an interface and not an actual object type. .. _jpype_types_lambdas: Lambdas ------- The companion of anonymous classes are lambda classes. These are generated dynamically and their parent is always an interface. Lambdas are always Single Abstract Method (SAM) type interfaces. They can implement additional methods in the form of default methods but those are generally not accessible within JPype. .. _jpype_types_inner_classes: Inner Classes ------------- For the most part, inner classes can be used like normal classes, with the following differences: - Inner classes in Java natively use $ to separate the outer class from the inner class. For example, inner class Foo defined inside class Bar is called Bar.Foo in Java, but its real native name is Bar$Foo. - Inner classes appear as member of the containing class. Thus to access them import the outer class and call them as members. - Non-static inner classes cannot be instantiated from Python code. Instances received from Java code can be used without problem. .. _jpype_types_buffer_transfers: Buffer Transfers ---------------- Java arrays provide efficient buffer transfers for primitive types using Python's `memoryview`. This allows seamless integration with libraries like NumPy for numerical operations. For strategies to optimize data exchange, see :ref:`Optimize Data Transfers `. .. code-block:: python # Example: Buffer transfer import numpy as np int_array = JInt[:](5) int_array[:] = [1, 2, 3, 4, 5] # Transfer data to Java array buffer = memoryview(int_array) np_array = np.array(buffer) # Convert to NumPy array print(np_array) # Output: [1, 2, 3, 4, 5] .. _import: Importing Java classes ====================== As Java classes are remote from Python and can neither be created nor extended within Python, they must be imported. JPype provides three different methods for creating classes. The highest level API is the use of the import system. To import a Java class, one must first import the optional module ``jpype.imports`` which has the effect of binding the Java package system to the Python module lookup. Once this is completed package or class can be imported using the standard Python import system. The import system offers a very rich error reporting system. All failed imports produce an ``ImportError`` with diagnostics as to what went wrong. Errors include unable to find the class, unable to find a required dependency, and incorrect Java version. One important caveat when dealing with importing Java modules. Python always imports local directories as modules before calling the Java importer. So any directory named ``java``, ``com``, or ``org`` will hide corresponding Java package. We recommend against naming directories as ``java`` or top level domain. .. _JPackage: The older method of importing a class is with the ``JPackage`` factory. This factory automatically loads classes as attributes as requested. If a class cannot be found it will produce an ``AttributeError``. The symbols ``java`` and ``javax`` in the ``jpype`` module are both ``JPackage`` instances. Only public classes appear on ``JPackage`` but protected and even private classes can be accessed by name. Though most private classes don't have any methods or fields that can be accessed. .. _JClass: The last mechanism for looking up a class is through the use of the ``JClass`` factory. This is a low level API allowing the loading of any class available using the forName mechanism in Java. The JClass method can take up to three arguments corresponding to arguments of the forName method and can be used with alternative class loaders. The majority of the JPype test bench uses JClass so that the tests are only evaluating the desired functionality and not the import system. But this does not imply that JClass is the preferred mechanic for importing classes. The first argument can be a string or a Java class instance. There are two keyword arguments ``loader`` and ``initialize``. The loader can point to an alternative ClassLoader which is handy when loading custom classes through mechanisms such as over the web. A False ``initialize`` argument loads a class without loading dependencies nor populating static fields. This option is likely not useful for ordinary users. It was provided when calling forName was problematic due to `caller sensitive`_ issues. .. _name_mangling: Name mangling ============= When providing Java package, classes, methods, and fields to Python, there are occasionally naming conflicts. For example, if one has a method called ``with`` then it would conflict with the Python keyword ``with``. Wherever this occurs, JPype renames the offending symbol with a trailing under bar. Java symbols with a leading or trailing under bars are consider to be privates and may not appear in the JPype wrapper entirely with the exception of package names. The following Python words will trigger name mangling of a Java name: =========== =========== ============= =========== ========== ``False`` ``None`` ``True`` ``and`` ``as`` ``async`` ``await`` ``def`` ``del`` ``elif`` ``except`` ``exec`` ``from`` ``global`` ``in`` ``is`` ``lambda`` ``nonlocal`` ``not`` ``or`` ``pass`` ``print`` ``raise`` ``with`` ``yield`` =========== =========== ============= =========== ========== .. _methods: .. _jpype_types_method_resolution: Method Resolution ================= Because Java supports method overloading and Python does not, JPype wraps Java methods as a "method dispatch". The dispatch is a collection of all of the methods from the class and all of its parents which share the same name. The job of the dispatch is to choose the method to call. Enforcement of the strong typing of Java must be performed at runtime within Python. Each time a method is invoked, JPype must match against the list of all possible methods that the class implements and choose the best possible overload. For this reason, the methods that appear in a JPype class will not be the actual Java methods, but rather a "dispatch" whose job is deciding which method should be called based on the type of the provided arguments. If no method is found that matches the provided arguments, the method dispatch will produce a ``TypeError``. This is the exact same outcome that Python uses when enforcing type safety within a function. If a type doesn't match, a ``TypeError`` will be produced. Dispatch Example ---------------- When JPype is unable to decide which overload of a method to call, the user must resolve the ambiguity. This is where casting comes in. Take for example the ``java.io.PrintStream`` class. This class has a variant of the print and println methods! So for the following code: .. code-block:: python java.lang.System.out.println(1) JPype will automatically choose the ``println(long)`` method, because the Python ``int`` matches exactly with the Java ``long``, while all the other numerical types are only "implicit" matches. However, if that is not the version you wanted to call, you must cast it. In this case, we will use a primitive type to construct the correct type. Changing the line thus: .. code-block:: python java.lang.System.out.println(JByte(1)) # <--- wrap the 1 in a JByte This tells JPype to choose the byte version. When dealing with Java types, JPype follows the standard Java matching rules. Types can implicitly grow to larger types but will not shrink without an explicit cast. Caching Optimization for Method Resolution ------------------------------------------ JPype optimizes method resolution by caching the results of previous matches. If the same method is called repeatedly with the same argument types (e.g., inside a loop or list comprehension), JPype reuses the cached resolution, avoiding the overhead of re-evaluating all overloads. This greatly improves performance for repetitive calls. For example, consider the following code: .. code-block:: python fruits = ["apple", "orange", "banana"] jlist = java.util.ArrayList() [jlist.add(fruit) for fruit in fruits] # Cached resolution for each iteration In this case, JPype caches the resolution for ``add(str)`` to ``add(String)`` method after the first call, and subsequent calls reuse the cached result. This optimization is particularly beneficial in loops and list comprehensions. A call to ``add(int)`` would trigger a new resolution. The next call to ``add(str)`` will once again trigger a resolution request. **Note**: For an in-depth discussion on how this caching mechanism improves loop performance, particularly in list comprehensions, see the :ref:`Performance ` section. Interactions of Custom Converters and Caching --------------------------------------------- It is unwise to define very broad conversions as it can interact poorly with caching. Suppose that one defined a convertion from all Python strings to the Java class for date under some condition. Or perhaps an even broader convserion was defined such as all Python classes that inherit from object. If such overly broad conversions are applied to a function for which both date and string were acceptable it were prefer the date conversion when method resolution starts. As the type for the cache was string it would attempt the out of order resolution of date first. If the condition yield a fail it will fall back to normal method resolution, but an overly broad conversion specialization may end up being dispatched to the previously defined conversion. Under normal operation of JPype the type conversions are narrowly defined such that the cache will always yield the proper resolution. But user defined conversions may cause unexpected results. In such a case, a cast operation to the Java type would be required to resolve the ambiguity. .. _jpype_types_type_matching: Type Matching ============= This section provides tables documenting the JPype conversion rules. JPype defines different levels of "match" between Python objects and Java types. These levels are: - **none**, There is no way to convert. - **explicit (E)**, JPype can convert the desired type, but only explicitly via casting. Explicit conversions are only execute automatically in the case of a return from a proxy. - **implicit (I)**, JPype will convert as needed. - **exact (X)**, Like implicit, but when deciding with method overload to use, one where all the parameters match "exact" will take precedence over "implicit" matches. See the previous section on `Java Conversions`_ for details. There are special conversion rules for ``java.lang.Object`` and ``java.lang.Number``. (`Object Class`_ and `Number Class`_) ============== ========== ========= =========== ========= ========== ========== =========== ========= ========== =========== ========= ================== ================= Python\\Java byte short int long float double boolean char String Array Object java.lang.Object java.lang.Class ============== ========== ========= =========== ========= ========== ========== =========== ========= ========== =========== ========= ================== ================= int I [1]_ I [1]_ X I I [3]_ I [3]_ X [8]_ I [11]_ long I [1]_ I [1]_ I [1]_ X I [3]_ I [3]_ I [11]_ float I [1]_ X I [11]_ sequence dictionary string I [2]_ X I unicode I [2]_ X I JByte X I [9]_ JShort X I [9]_ JInt X I [9]_ JLong X I [9]_ JFloat X I [9]_ JDouble X I [9]_ JBoolean X I [9]_ JChar X I [9]_ JString X I JArray I/X [4]_ I JObject I/X [6]_ I/X [7]_ I/X [7]_ JClass I X "Boxed"[10]_ I I I I I I I I ============== ========== ========= =========== ========= ========== ========== =========== ========= ========== =========== ========= ================== ================= .. [1] Conversion will occur if the Python value fits in the Java native type. .. [2] Conversion occurs if the Python string or unicode is of length 1. .. [3] Java defines conversions from integer types to floating point types as implicit conversion. Java's conversion rules are based on the range and can be lossy. See (http://stackoverflow.com/questions/11908429/java-allows-implicit-conversion-of-int-to-float-why) .. [4] Number of dimensions must match and the types must be compatible. .. [6] Only if the specified type is a compatible array class. .. [7] The object class is an exact match, otherwise implicit. .. [8] Only the values `True` and `False` are implicitly converted to booleans. .. [9] Primitives are boxed as per Java rules. .. [10] Java boxed types are mapped to Python primitives, but will produce an implicit conversion even if the Python type is an exact match. This is to allow for resolution between methods that take both a Java primitve and a Java boxed type. .. [11] Boxed to ``java.lang.Number`` Exception Handling ================== Error handling is an important part of any non-trivial program. All Java exceptions occurring within Java code raise a `jpype.JException`, which derives from Python's `Exception`. These can be caught either using a specific Java exception or generically as a `jpype.JException` or `java.lang.Throwable`. You can then use the `stacktrace()`, `str()`, and `args` to access extended information. .. _jpype_types_catching_a_specific_java_exception: Catching a Specific Java Exception ---------------------------------- The following example demonstrates catching a specific Java exception: .. code-block:: python try: # Code that throws a java.lang.RuntimeException except java.lang.RuntimeException as ex: print("Caught the runtime exception:", str(ex)) print(ex.stacktrace()) .. _jpype_types_catching_multiple_java_exceptions: Catching Multiple Java Exceptions --------------------------------- Multiple Java exceptions can be caught together or separately: .. code-block:: python try: # Code that may throw various exceptions except (java.lang.ClassCastException, java.lang.NullPointerException) as ex: print("Caught multiple exceptions:", str(ex)) print(ex.stacktrace()) except java.lang.RuntimeException as ex: print("Caught runtime exception:", str(ex)) print(ex.stacktrace()) except jpype.JException as ex: print("Caught base exception:", str(ex)) print(ex.stacktrace()) except Exception as ex: print("Caught Python exception:", str(ex)) .. _jpype_types_raising_exceptions_from_python_to_java: Raising Exceptions from Python to Java -------------------------------------- Exceptions can be raised in proxies to throw an exception back to Java. Exceptions within the JPype core are issued with the most appropriate Python exception type, such as `TypeError`, `ValueError`, `AttributeError`, or `OSError`. .. _jpype_types_raising_exceptions_in_proxies: Raising Exceptions in Proxies ----------------------------- JPype allows Python proxies to raise exceptions that are propagated back to Java. This is particularly useful when implementing Java interfaces in Python and handling invalid inputs or unexpected conditions. When an exception is raised in Python, it is wrapped in a `RuntimeException` in Java. If the exception propagates back to Python, it is unpacked to return the original Python exception. .. _jpype_types_example: Example ~~~~~~~ The following example demonstrates raising a Python exception from a proxy: .. code-block:: python import jpype import jpype.imports jpype.startJVM() from java.util.function import Function @jpype.JImplements(Function) class MyFunction: @jpype.JOverride def apply(self, value): if value is None: raise ValueError("Invalid input: None is not allowed") return value.upper() try: func = MyFunction() result = func.apply(None) # This will raise a ValueError except ValueError as ex: print("Caught Python exception:", str(ex)) .. _jpype_types_exception_aliasing: Exception Aliasing ------------------ Certain exceptions in Java have a direct correspondence with existing Python exceptions. Rather than forcing JPype to translate these exceptions or requiring the user to handle Java exception types throughout the code, these exceptions are "derived" from their Python counterparts. This allows the user to catch them using standard Python exception types. +---------------------------------------+------------------+ | Java Exception | Python Exception | +---------------------------------------+------------------+ | `java.lang.IndexOutOfBoundsException` | `IndexError` | | `java.lang.NullPointerException` | `ValueError` | +---------------------------------------+------------------+ .. _jpype_types_aliasing_example: Aliasing Example ~~~~~~~~~~~~~~~~ The following example demonstrates catching an aliased exception: .. code-block:: python try: # Code that throws a java.lang.IndexOutOfBoundsException except IndexError as ex: print("Caught IndexError:", str(ex)) By deriving these exceptions from Python, the user is free to catch the exception either as a Java exception or as the more general Python exception. Remember that Python exceptions are evaluated in order from most specific to least. .. _controlling_the_jvm: Controlling the JVM ******************* In this chapter, we will discuss how to control the JVM from within Python. For the most part, the JVM is invisible to Python. The only user controls needed are to start up and shutdown the JVM. .. _startJVM: Starting the JVM ================ JPype requires the Java Virtual Machine (JVM) to be started before interacting with Java. This section explains how to start the JVM, configure its options, and troubleshoot common issues. .. _controlling_the_jvm_key_requirements: Key Requirements ---------------- Before starting the JVM, ensure the following prerequisites are met: 1. **Java Installation**: A Java Runtime Environment (JRE) or Java Development Kit (JDK) must be installed. JPype supports Java versions 11 and later. 2. **Architecture Match**: The architecture of the Python interpreter (e.g., 64-bit or 32-bit) must match the architecture of the installed JVM. 3. **Classpath Configuration**: Specify the paths to Java classes or JAR files required by your application. 4. **Environment Variable**: Ensure the `JAVA_HOME` environment variable is set to the directory containing the Java installation. How to Start the JVM -------------------- To start the JVM, use the ``jpype.startJVM()`` function. This function initializes the JVM with the specified options. The key arguments are: - **``classpath``**: A list of paths to JAR files or directories containing Java classes. - **``convertStrings``**: A boolean flag controlling whether Java strings are automatically converted to Python strings. - **``ignoreUnrecognized``**: A flag that suppresses errors for unrecognized JVM options. - **Additional JVM options**: Any valid JVM arguments (e.g., ``-Xmx`` for memory allocation). Example: Starting the JVM ~~~~~~~~~~~~~~~~~~~~~~~~~ Here is a typical example of starting the JVM: .. code-block:: python import jpype # Start the JVM with classpath and options jpype.startJVM( classpath=['lib/*', 'classes'], jvmOptions=["-ea"] # Enable assertions ) Classpath Configuration ----------------------- JPype supports two methods for specifying the classpath: 1. **``classpath`` Argument**: Pass a list of paths directly to the ``startJVM()`` function. Wildcards (``*``) are supported for JAR files in a directory. .. code-block:: python jpype.startJVM(classpath=['lib/*', 'classes']) 2. **``addClassPath()`` Function**: Use ``jpype.addClassPath()`` to add paths dynamically before starting the JVM. .. code-block:: python jpype.addClassPath('lib/*') jpype.addClassPath('classes') jpype.startJVM() To debug classpath issues, print the effective classpath after starting the JVM: .. code-block:: python print(java.lang.System.getProperty('java.class.path')) .. _controlling_the_jvm_handling_jar_files_compiled_for_newer_java_versions: Handling JAR Files Compiled for Newer Java Versions --------------------------------------------------- If a JAR file is compiled for a newer version of Java than the JVM being used, JPype will fail to load the classes from the JAR file, and the JVM will throw an ``UnsupportedClassVersionError``. This occurs because the JVM cannot interpret class files compiled for a newer version. .. _controlling_the_jvm_behavior: Behavior ~~~~~~~~ When attempting to load a JAR file compiled for a newer version of Java, the JVM will throw an error similar to the following:: java.lang.UnsupportedClassVersionError: has been compiled by a more recent version of the Java Runtime (class file version X), this version of the Java Runtime only recognizes class file versions up to Y. For example: - Java 11 corresponds to class file version 55. - Java 17 corresponds to class file version 61. If the JAR file contains class files compiled with a newer version than the JVM supports, the JVM cannot interpret them. .. _controlling_the_jvm_starting_the_jvm_handling_jar_files_compiled_for_newer_java_versions_steps_to_resolve: Steps to Resolve ~~~~~~~~~~~~~~~~ 1. **Upgrade the JVM**: Ensure the JVM version matches or exceeds the version used to compile the JAR file. Use the following command to check the JVM version:: java -version 2. **Recompile the JAR**: If you have access to the source code, recompile the JAR with an older version of Java using the ``--release`` flag. For example:: javac --release 11 -d output_directory source_files This ensures compatibility with Java 11. 3. **Check the Class File Version**: Use the ``javap`` command to verify the class file version of the JAR:: javap -verbose Look for the ``major version`` field in the output. .. _controlling_the_jvm_best_practices: Best Practices ~~~~~~~~~~~~~~ - Always ensure the JVM version matches the requirements of the JAR files being loaded. - If possible, use JAR files compiled for long-term support (LTS) versions of Java, such as Java 11 or Java 17, to maximize compatibility. .. _controlling_the_jvm_automatic_jvm_path_detection: Automatic JVM Path Detection ---------------------------- JPype automatically detects the path to the JVM shared library using the ``JAVA_HOME`` environment variable. If ``JAVA_HOME`` is not set, JPype searches common directories based on the platform. You can retrieve the detected path using: .. code-block:: python print(jpype.getDefaultJVMPath()) If the automatic detection fails, specify the JVM path manually as the first argument to ``startJVM()``: .. code-block:: python jpype.startJVM('/path/to/libjvm.so', classpath=['lib/*']) .. _controlling_the_jvm_handling_nonascii_characters_in_the_jvm_path: Handling Non-ASCII Characters in the JVM Path ---------------------------------------------- JPype has been revised to handle JVM paths containing non-ASCII characters. Due to restrictions in Java, JPype must make a copy of the JVM shared library when the path includes non-ASCII characters. This ensures compatibility with the Java Virtual Machine. **Windows-Specific Behavior**: On Windows, the copied JVM shared library cannot be deleted after use due to file locking restrictions imposed by the operating system. As a result, the temporary file will remain on disk after the JVM is shut down. **Implications**: - The copied JVM shared library will occupy disk space until manually removed. - This behavior is specific to Windows and does not affect Linux or macOS. **Best Practices**: - Avoid using non-ASCII characters in the JVM path when running JPype on Windows to prevent unnecessary file duplication. - If non-ASCII characters are unavoidable, ensure sufficient disk space is available for temporary files. **Troubleshooting**: To locate the copied JVM shared library, check the directory where the JVM path is specified. The copied file will have the same name as the original shared library but may include additional identifiers. **Example**: If the original JVM path is: C:\Program Files\Java\jdk-11.0.7\bin\server\jvm.dll And it contains non-ASCII characters, JPype will create a copy in a temporary directory. This behavior is necessary to ensure compatibility with Java's handling of non-ASCII paths. .. _controlling_the_jvm_additional_flags_for_startjvm: Additional Flags for `startJVM()` --------------------------------- JPype provides several optional flags for `startJVM()` to customize the JVM startup process: 1. **`jvmOptions`**: A list of JVM options for memory, debugging, or garbage collection tuning. Example: ``jpype.startJVM(jvmOptions=["-Xmx512m", "-XX:+UseG1GC"])`` 2. **`ignoreUnrecognized`**: Suppresses errors for unrecognized JVM options. Example: ``jpype.startJVM(ignoreUnrecognized=True)`` 3. **`convertStrings`**: Controls automatic conversion of Java strings to Python strings. Example: ``jpype.startJVM(convertStrings=False)`` 4. **`classpath`**: Specifies paths to JAR files and Java classes. Example: ``jpype.startJVM(classpath=["lib/*", "classes"])`` 5. **`jvmPath`**: Specifies the path to the JVM shared library. Example: ``jpype.startJVM(jvmPath="/path/to/libjvm.so")`` 6. **`attachThread`**: Automatically attaches Python threads to the JVM. Example: ``jpype.startJVM(attachThread=True)`` 7. **`disableGC`**: Disables JPype's garbage collection hooks. Example: ``jpype.startJVM(disableGC=True)`` 8. **`stackTrace`**: Enables detailed stack traces for Java exceptions. Example: ``jpype.startJVM(stackTrace=True)`` 9. **`initializers`**: A list of Python functions executed during JVM startup. Example: ``jpype.startJVM(initializers=[setup])`` 10. **`modulePath`**: Specifies the module path for Java modular applications. Example: ``jpype.startJVM(modulePath=["modules/*"])`` .. _string_conversions: String Conversions ------------------ The ``convertStrings`` argument controls whether Java strings are automatically converted to Python strings. By default, this behavior is disabled (``convertStrings=False``) to preserve Java string methods and avoid unnecessary conversions. If enabled (``convertStrings=True``), Java strings are returned as Python strings, but this can impact performance and chaining of Java string methods. This option is consisted a legacy option as it will result in unncessary calls to ``str()`` every time a String is passed from Java. Best practice: Set ``convertStrings=False`` unless your application explicitly requires automatic conversion. .. _controlling_the_jvm_checking_jvm_state: Checking JVM State ------------------ Use the following functions to check the status of the JVM: - **``jpype.isJVMStarted()``**: Returns ``True`` if the JVM is running. - **``jpype.getJVMVersion()``**: Retrieves the version of the running JVM. Example: .. code-block:: python if not jpype.isJVMStarted(): print("JVM is not running!") else: print("JVM version:", jpype.getJVMVersion()) .. _controlling_the_jvm_common_issues_and_troubleshooting: Common Issues and Troubleshooting --------------------------------- 1. **Classpath Errors**: Ensure that all required JAR files and directories are included in the classpath. Use ``java.lang.System.getProperty('java.class.path')`` to verify the effective classpath. 2. **Architecture Mismatch**: Ensure the Python interpreter and JVM have matching architectures (e.g., both 64-bit or both 32-bit). Running a 64-bit Python interpreter with a 32-bit JVM will cause startup failures. 3. **Environment Variable Issues**: Verify that the ``JAVA_HOME`` environment variable is set correctly. If necessary, set it manually: - **Windows**: ``set JAVA_HOME=C:\Program Files\Java\jdk-`` - **Linux/Mac**: ``export JAVA_HOME=/usr/lib/jvm/java-`` 4. **Unrecognized JVM Options**: If you encounter errors for unrecognized JVM options, use the ``ignoreUnrecognized=True`` flag to suppress them. 5. **Memory Allocation Errors**: Ensure sufficient memory is allocated to the JVM using the ``-Xmx`` option. 6. **Debugging Startup Failures**: Enable stack traces for additional diagnostics: .. code-block:: python import _jpype _jpype.enableStacktraces(True) .. _controlling_the_jvm_best_practices_for_jvm_starting: Best Practices for JVM starting ------------------------------- - **Start Early**: Start the JVM at the beginning of your program to avoid issues with imports and initialization. - **Specify Classpath Explicitly**: Use the ``classpath`` argument to ensure all required JAR files and directories are loaded. - **Disable String Conversion**: Set ``convertStrings=False`` for better control and performance. - **Avoid Restarting the JVM**: JPype does not support restarting the JVM after it has been shut down. Design your application to start the JVM once and keep it running for the program's lifetime. - **Monitor Resource Usage**: If your application uses large Java objects, monitor memory usage to avoid out-of-memory errors. .. _controlling_the_jvm_starting_the_jvm_summary: Summary of JVM starting ----------------------- Starting the JVM is a critical step in using JPype to integrate Python with Java. By following the guidelines in this section, you can ensure a smooth startup process, avoid common pitfalls, and configure the JVM to meet your application's needs. Proper classpath configuration, architecture matching, and memory allocation are key to successful integration. Debugging tools and best practices are available to help troubleshoot issues and optimize performance. .. _shutdownJVM: Shutting Down the JVM ====================== At the end of your program, you may want to shut down the JVM to terminate the Java environment explicitly. While this is possible, it is generally not recommended unless absolutely necessary. JPype automatically shuts down the JVM when the Python process terminates, ensuring a clean exit without manual intervention. .. _controlling_the_jvm_risks_of_shutting_down_the_jvm: Risks of Shutting Down the JVM ------------------------------ Shutting down the JVM manually can lead to serious risks and instability, especially if there are lingering Java references or shared resources. Once the JVM is shut down, all Java objects become invalid, and any attempt to access them will result in errors. This includes: - **Lingering Java References**: Any Java objects held by Python will become invalid after the JVM is shut down. Accessing these objects will raise exceptions and could result in undefined behavior. - **Shared Resources**: Shared resources such as buffers (e.g., memory mapped from Java to NumPy) will become unstable. Accessing these buffers after the JVM is shut down may cause crashes or memory corruption. - **Proxies and Threads**: If Java threads or proxies are active when the JVM is shut down, they will be terminated abruptly, potentially leaving the system in an inconsistent state. - **Non-Daemon Threads**: All threads must be attached as daemon threads before shutting down the JVM. Non-daemon threads will block the shutdown process, causing it to hang indefinitely. Python threads that interact with Java are automatically attached as daemon threads by JPype, but any custom threads created in Java must also be marked as daemon. For most applications, it is safer to allow the JVM to shut down automatically when the Python process exits. This ensures that all resources are cleaned up properly and avoids the risks associated with manual shutdown. .. _controlling_the_jvm_how_jpype_shuts_down_the_jvm: How JPype Shuts Down the JVM ---------------------------- JPype performs the following steps during JVM shutdown to ensure proper cleanup: 1. **Request JVM Shutdown**: JPype requests the JVM to shut down gracefully. 2. **Wait for Non-Daemon Threads**: The JVM waits for all non-daemon threads to terminate. If you have active Java threads, ensure they are properly terminated or marked as daemon before shutting down the JVM. 3. **Execute Shutdown Hooks**: The JVM executes any registered shutdown hooks. These hooks can be used to clean up resources before the JVM terminates. 4. **Release JPype Reference Queue**: JPype shuts down its internal reference queue, which is responsible for dereferencing Python resources tied to Java objects. 5. **Release JPype Type Manager**: JPype releases its type manager, which handles mappings between Python and Java types. 6. **Unload JVM Shared Library**: The JVM shared library is unloaded, freeing memory used by the JVM. 7. **Finalize Python Resources**: JPype cleans up any remaining Python handles tied to Java objects, ensuring that no invalid references remain. Once the JVM is shut down, all Java objects are considered dead and cannot be reactivated. Any attempt to access their data field will raise an exception. .. _controlling_the_jvm_managing_threads_during_jvm_shutdown: Managing Threads During JVM Shutdown ------------------------------------ The JVM requires all threads to be attached as daemon threads during shutdown. Daemon threads are background threads that do not prevent the JVM from terminating. Non-daemon threads, on the other hand, will block the shutdown process, causing it to hang indefinitely until those threads terminate. JPype automatically attaches Python threads that interact with Java as daemon threads. However, if you create custom threads in Java, you must explicitly mark them as daemon threads to ensure they do not block the JVM shutdown. To mark a Java thread as a daemon, use the following pattern: .. code-block:: python import java.lang.Thread # Create a Java thread thread = java.lang.Thread() # Mark the thread as daemon thread.setDaemon(True) # Start the thread thread.start() If you need to check whether a thread is a daemon, use the `isDaemon()` method: .. code-block:: python print(f"Thread is daemon: {thread.isDaemon()}") Ensure that all non-daemon threads are properly terminated or marked as daemon before shutting down the JVM. Failure to do so may cause the shutdown process to hang indefinitely. .. _controlling_the_jvm_how_to_shut_down_the_jvm: How to Shut Down the JVM ------------------------ If you must shut down the JVM manually, you can use the `jpype.shutdownJVM()` function. This should only be called from the main Python thread. Calling it from any other thread will raise an exception. .. code-block:: python import jpype # Shut down the JVM jpype.shutdownJVM() Numerous examples found on the internet explicity state that `shutdownJVM` is a good practice. These examples are legecy from early developement. At the time shutdownJVM brutally closed the JVM and bypassed all for the JVM shutdown routines thus causing the program to skip over errors in the JPype module resulting from mishandled race conditions. While it is still acceptable to shutdown the JVM and may be desireable to do so if a module needs a particular order to shutdown cleanly, the use of an explicit shutdown is discouraged. .. _controlling_the_jvm_debugging_jvm_shutdown: Debugging JVM Shutdown ---------------------- If the JVM shutdown process hangs or fails, it is often due to lingering threads or resources that were not properly terminated. Use the following techniques to debug shutdown issues: 1. **Check Active Threads**: Before shutting down the JVM, check for active non-daemon threads that may be preventing the shutdown. You can use the following Java code to list all active threads: .. code-block:: python import java.lang.Thread # Get all active threads threads = java.lang.Thread.getAllStackTraces().keySet() for thread in threads: print(f"Thread: {thread.getName()}, Daemon: {thread.isDaemon()}") Ensure that all non-daemon threads are terminated or marked as daemon before calling `jpype.shutdownJVM()`. 2. **Inspect Shutdown Hooks**: If you have attached shutdown hooks, verify that they complete quickly and do not hang. Long-running shutdown hooks can delay or block JVM termination. 3. **Monitor Resource Usage**: If shared resources such as buffers are in use, ensure that they are properly released before shutting down the JVM. For example, copy buffer contents to a Python object to preserve data. 4. **Enable Debugging Logs**: JPype can provide additional diagnostics during the shutdown process. Use the following command to enable debugging logs: .. code-block:: python import _jpype _jpype.enableStacktraces(True) This will print detailed stack traces for exceptions that occur during the shutdown process. 5. **Handle Hanging Threads**: If the JVM shutdown hangs due to threads that cannot terminate, you can forcefully terminate the Python process using `os._exit()` or `java.lang.Runtime.exit()`. **However, note that calling `exit` will bypass normal `atexit` routines in both Python and Java.** This means that any cleanup tasks, such as writing logs (e.g., Jacoco coverage reports) or flushing buffers, will not be executed. Use this approach only as a last resort when all other debugging techniques fail. .. _controlling_the_jvm_best_practices_for_jvm_shutdown: Best Practices for JVM Shutdown ------------------------------- - **Avoid Manual Shutdown**: Whenever possible, allow the JVM to shut down automatically when the Python process exits. This avoids the risks of lingering references and shared resource instability. - **Terminate Threads Properly**: Ensure all non-daemon Java threads are terminated or marked as daemon before shutting down the JVM. Failure to do so may cause the shutdown process to hang indefinitely. - **Handle Buffers Carefully**: If you are using shared buffers (e.g., Java direct buffers with NumPy), avoid accessing them after the JVM is shut down. If you need to preserve data, copy the buffer contents to a Python object before shutting down the JVM. - **Use Shutdown Hooks**: Attach shutdown hooks only when necessary to clean up resources. Ensure that the hooks complete quickly to avoid delaying JVM termination. - **Avoid Forceful Termination**: Avoid using `os._exit()` or `java.lang.Runtime.exit()` unless absolutely necessary. These methods prevent normal cleanup routines from executing, which can result in missing logs, incomplete resource cleanup, or other unintended consequences. .. _controlling_the_jvm_summary_of_jvm_shutdown: Summary of JVM Shutdown ------------------------ JPype's shutdown process is designed to ensure that resources are cleaned up properly and the JVM terminates gracefully. While shutting down the JVM manually is possible, it introduces risks that can lead to instability and crashes. For most applications, the JVM should be allowed to shut down automatically when the Python process exits. If manual shutdown is required, take precautions to ensure that all Java references and shared resources are properly cleaned up before shutting down the JVM. Avoid forceful termination unless absolutely necessary, as it bypasses critical cleanup routines in both Python and Java. .. _customization: Customization ************* JPype supports customization to enhance the integration between Java and Python. This allows users to modify Java classes and type conversions to better suit their needs, making Java APIs more Pythonic or enabling seamless interaction with Python data structures. There are two primary types of customizations available: 1. **Class Customizers**: Add Python methods and properties to Java classes to make them behave like native Python classes. 2. **Type Conversion Customizers**: Define implicit conversions between Python types and Java types for seamless interoperability. .. _customization_class_customizers: Class Customizers ================= Customizers are applied to JPype wrapper classes to enhance their Pythonic interface. By adding Python methods and properties to Java classes, customizers make Java objects behave like native Python objects. These customizations are applied to wrappers, whether they encapsulate a proxy or a Java reference. Java wrappers can be customized to better match the expected behavior in Python. Customizers are defined using decorators. Applying the annotations ``@JImplementationFor`` and ``@JOverride`` to a regular Python class will transfer methods and properties to a Java class. ``@JImplementationFor`` requires the class name as a string, a Java class wrapper, or a Java class instance. Only a string can be used prior to starting the JVM. ``@JOverride``, when applied to a Python method, will hide the Java implementation, allowing the Python method to replace the Java implementation. When a Java method is overridden, it is renamed with a preceding underscore to appear as a private method. Optional arguments to ``@JOverride`` can be used to control the renaming and force the method override to apply to all classes that derive from a base class ("sticky"). Generally speaking, a customizer should be defined before the first instance of a given class is created so that the class wrapper and all instances will have the customization. .. _customization_example_customizing_javautilmap: Example: Customizing ``java.util.Map`` -------------------------------------- The following example demonstrates how to customize the ``java.util.Map`` class to behave like a Python dictionary: .. code-block:: python @_jcustomizer.JImplementationFor('java.util.Map') class _JMap: def __jclass_init__(self): Mapping.register(self) def __len__(self): return self.size() def __iter__(self): return self.keySet().iterator() def __delitem__(self, i): return self.remove(i) The name of the class does not matter for the purposes of the customizer, though it should be a private class so that it does not get used accidentally. The customizer code will steal from the prototype class rather than acting as a base class, ensuring that the methods will appear on the most derived Python class and are not hidden by the Java implementations. The customizer copies methods, callable objects, ``__new__``, class member strings, and properties. .. _customization_type_conversion_customizers: Type Conversion Customizers =========================== JPype allows users to define custom conversion methods that are called whenever a specified Python type is passed to a particular Java type. To specify a conversion method, add ``@JConversion`` to an ordinary Python function with the name of the Java class to be converted to and one keyword of ``exact`` or ``instanceof``. The keyword controls how strictly the conversion will be applied: - ``exact``: Restricted to Python objects whose type exactly matches the specified type. - ``instanceof``: Accepts anything that matches ``isinstance`` to the specified type or protocol. In some cases, the existing protocol definition will be overly broad. Adding the keyword argument ``excludes`` with a type or tuple of types can be used to prevent the conversion from being applied. Exclusions always apply first. User-supplied conversions are tested after all internal conversions have been exhausted and are always considered to be an implicit conversion. .. _customization_example_converting_python_sequences_to_java_collections: Example: Converting Python Sequences to Java Collections -------------------------------------------------------- The following example demonstrates how to convert Python sequences into Java collections: .. code-block:: python @JConversion("java.util.Collection", instanceof=Sequence, excludes=str) def _JSequenceConvert(jcls, obj): return _jclass.JClass('java.util.Arrays').asList(obj) JPype supplies customizers for certain Python classes by default. These include: ========================= ============================== Python class Implicit Java Class ========================= ============================== pathlib.Path java.io.File pathlib.Path java.nio.file.Path datetime.datetime java.time.Instant collections.abc.Sequence java.util.Collection collections.abc.Mapping java.util.Map ========================= ============================== .. _customization_jpype_beans_module: JPype Beans Module ================== .. _customization_overview_of_jpype_beans: Overview of JPype Beans ----------------------- The `jpype.beans` module is an optional feature that converts Java Bean-style getter and setter methods into Python properties. This customization is particularly useful for interactive programming or when working with Java classes that follow the Bean pattern. However, this behavior is not enabled by default because it can lead to confusion about whether a class is exposing a variable or a property added by JPype. Additionally, it violates Python's principle of *"There should be one-- and preferably only one --obvious way to do it."* and the C++ principle of *"You only pay for what you use."* If you find this feature useful, you can enable it explicitly by importing the `jpype.beans` module. .. _customization_enabling_beans_as_properties: Enabling Beans as Properties ---------------------------- To enable the `jpype.beans` module, simply import it into your Python program: .. code-block:: python import jpype.beans Once enabled, the module applies globally to all Java classes that have already been loaded, as well as any classes loaded afterward. This behavior cannot be undone after the module is imported. .. _customization_how_it_jpype_beans_works: How It JPype beans Works ------------------------ The `jpype.beans` module scans Java classes for methods that follow the Bean naming conventions: - **Getter methods**: Methods prefixed with `get` (e.g., `getName`) are treated as property accessors. - **Setter methods**: Methods prefixed with `set` (e.g., `setName`) are treated as property mutators. For example, a Java class with the following methods: .. code-block:: java public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } Will automatically expose the `name` field as a Python property: .. code-block:: python import jpype import jpype.beans jpype.startJVM() Person = jpype.JClass("Person") person = Person() person.name = "Alice" # Calls setName("Alice") print(person.name) # Calls getName(), Output: Alice .. _customization_implementation_details_of_jpype_beans: Implementation Details of JPype beans ------------------------------------- The module works by: 1. Identifying getter and setter methods in Java classes using the `_isBeanAccessor()` and `_isBeanMutator()` methods. 2. Creating Python properties for these methods. 3. Adding the properties to the class dynamically. The customization applies retroactively to all classes currently loaded and globally to all future classes. .. _customization_limitations_of_jpype_beans: Limitations of JPype beans -------------------------- 1. **Global Behavior**: Once enabled, the customization applies to all Java classes globally. It cannot be undone. 2. **Confusion with Existing Members**: If a Java class already has a Python member with the same name as a property, the property will not be added to avoid conflicts. 3. **Ambiguity**: This feature can make it unclear whether a field is a true Java variable or a property added by JPype. .. _customization_best_practices_for_jpype_beans: Best Practices for JPype beans ------------------------------ - Use this module only when working with Java classes that heavily rely on the Bean pattern. - Avoid enabling this module in large projects unless absolutely necessary, as the global behavior may lead to unintended consequences. - Document its usage clearly in your codebase to avoid confusion for other developers. .. _customization_summary_of_jpype_beans: Summary of JPype beans ---------------------- The `jpype.beans` module provides a convenient way to work with Java Bean-style classes in Python by exposing getter and setter methods as Python properties. While useful in certain scenarios, it is an optional feature that must be explicitly enabled and should be used with caution due to its global and irreversible behavior. .. _customization_resolving_method_name_conflicts_with_customizers: Resolving Method Name Conflicts with Customizers ================================================ .. _customization_overview_of_conflict_resolution: Overview of conflict resolution ------------------------------- When working with Java classes in Python, conflicts can arise between public fields and methods that share the same name. JPype provides tools to resolve these conflicts using customizers, allowing you to rename fields or methods dynamically and expose them in a Pythonic way. This section demonstrates how to use a customizer to resolve such conflicts by renaming fields or methods and exposing them as Python properties. .. _customization_example_renaming_conflicting_fields_and_methods: Example: Renaming Conflicting Fields and Methods ------------------------------------------------ Consider a Java class with a field and a method that share the same name. Without customization, JPype will expose the method, and the field will be hidden. To resolve this, you can use a customizer to rename the conflicting field or method and expose it as a Python property. Here’s an example: .. code-block:: python def asProperty(field): def get(E): return field.get(E) def set(E, V): field.set(E, V) return property(get, set) @jpype.JImplementationFor("java.lang.Object") # Use your base class. class MyCustomizer(object): # This is applied to every class that derives from the type def __jclass_init__(cls): # Traverse the fields for field in cls.class_.getDeclaredFields(): name = str(field.getName()) tp = type(cls.__dict__.get(str(field.getName()), None)) # Watch for private methods if tp is type(None): continue # Resolve conflicts between public fields and methods if tp is jpype.JMethod: cls._customize("%s_" % name, asProperty(field)) .. _customization_how_it_conflict_resolution_works: How It Conflict Resolution Works -------------------------------- 1. **Field Traversal**: The customizer iterates over all declared fields in the class using `getDeclaredFields()`. 2. **Conflict Detection**: For each field, it checks whether a public method with the same name exists. 3. **Renaming**: If a conflict is detected, the field is renamed by appending an underscore (`_`) to its name. 4. **Property Creation**: The renamed field is exposed as a Python property using the `property()` function. .. _customization_example_usage_of_conflict_resolution: Example Usage of Conflict Resolution ------------------------------------ Suppose you have a Java class `A` with a field `mean` and a method `mean`. Without customization, the field would be inaccessible. Using the customizer above, you can expose the field as `mean_`: .. code-block:: python A = jpype.JClass("A") a = A() print(a.mean_) # Access the renamed field a.mean_ = 2 # Modify the field print(a.mean_) # Verify the updated value .. _customization_notes_on_global_customizers: Notes on Global Customizers --------------------------- - The customizer is applied globally to all classes that derive from the specified base class (`java.lang.Object` in this example). You can replace the base class with a more specific class to limit the scope of the customization. - This approach is particularly useful for resolving conflicts in large Java libraries or frameworks where method and field names overlap frequently. .. _customization_best_practices_regarding_name_resolution_customizers: Best Practices Regarding Name Resolution Customizers ---------------------------------------------------- - Use meaningful naming conventions when renaming fields or methods to avoid confusion. - Document customizations clearly in your codebase to help other developers understand the changes. - Test the customizer thoroughly to ensure it behaves as expected across all relevant classes. .. _customization_summary_of_naming_conflict_resolution: Summary of Naming Conflict Resolution ------------------------------------- This example demonstrates how to use JPype customizers to resolve conflicts between fields and methods in Java classes. By renaming conflicting fields or methods and exposing them as Python properties, you can create a more Pythonic interface for interacting with Java classes. .. _customization_best_practices_for_class_customization: Best Practices For Class Customization ====================================== To ensure effective use of customizations, follow these best practices: 1. **Define Customizers Early**: Always define customizers before the first instance of the class is created to ensure proper initialization. 2. **Test Customizations Thoroughly**: Verify that the customized behavior works as expected, especially for complex or heavily-used classes. 3. **Avoid Conflicts**: Ensure that customizers do not introduce conflicting methods or properties, especially when customizing multiple interfaces. 4. **Monitor Performance**: Be mindful of performance implications when adding extensive customizations. 5. **Document Customizations**: Clearly document the purpose and behavior of customizations to assist other developers working on the codebase. By leveraging class and type conversion customizers, JPype users can create seamless integrations between Python and Java, making Java APIs feel native to Python programmers. .. _collections: Collections *********** JPype uses customizers to augment Java collection classes to operate like Python collections. Enhanced objects include ``java.util.List``, ``java.util.Set``, ``java.util.Map``, and ``java.util.Iterator``. These classes generally comply with the Python API except in cases where there is a significant name conflict. This section details the integration of Java collections with Python constructs. .. _collections_specialized_collection_wrappers: Specialized Collection Wrappers =============================== JPype customizes Java collection classes to behave like Python collections, making them intuitive for Python developers. This includes support for iteration, indexing, and key-value access. Below are the key behaviors of specific Java collection types. .. _collections_iterable: Iterable -------- Java classes that implement ``java.util.Iterable`` are customized to support Python's iteration constructs. This allows seamless use in Python `for` loops and list comprehensions. For example, a Java ``ArrayList`` can be iterated directly: .. code-block:: python from java.util import ArrayList jlist = ArrayList() jlist.add("apple") jlist.add("orange") jlist.add("banana") for item in jlist: print(item) This integration ensures that Java collections behave like Python sequences, providing a natural experience for Python developers. .. _collections_iterators: Iterators --------- Java classes that implement ``java.util.Iterator`` act as Python iterators. This means they can be used in Python `for` loops and list comprehensions without requiring additional conversion. For example: .. code-block:: python from java.util import Vector jvector = Vector() jvector.add("apple") jvector.add("orange") iterator = jvector.iterator() for item in iterator: print(item) .. _collections_collection: Collection ---------- Java classes that inherit from ``java.util.Collection`` integrate seamlessly with Python's collection constructs. They support operations such as length retrieval, iteration, and implicit conversion of Python sequences into Java collections. For example: .. code-block:: python from java.util import ArrayList pylist = ["apple", "orange", "banana"] jlist = ArrayList(pylist) # Convert Python list to Java collection print(len(jlist)) # Output: 3 for item in jlist: print(item) Methods that accept Java collections can automatically convert Python sequences if all elements are compatible with Java types. Otherwise, a ``TypeError`` is raised. .. _java.util.List: Lists ----- Java `List` classes, such as ``ArrayList`` and ``LinkedList``, can be used in Python `for` loops and list comprehensions. They also support indexing and deletion, making them behave like Python lists. For example: .. code-block:: python from java.util import ArrayList jlist = ArrayList() jlist.add("apple") jlist.add("orange") jlist.add("banana") print(jlist[0]) # Output: apple del jlist[1] # Remove "orange" print(jlist) # Output: [apple, banana] Java lists can also be converted to Python lists and vice versa using the copy constructor. For example: .. code-block:: python pylist = ["apple", "orange", "banana"] jlist = ArrayList(pylist) # Convert Python list to Java list pylist2 = list(jlist) # Convert Java list back to Python list Note that individual elements remain Java objects when converted to Python. Converting to Java will attempt to convert each argument individually to Java. If there is no conversion it will produce a ``TypeError``. The conversion can be forced by casting to the appropriate Java type with a list comprehension or by defining a new conversion customizer. Lists also have iterable, length, item deletion, and indexing. Note that indexing of ``java.util.LinkedList`` is supported but can have a large performance penalty for large lists. Use of iteration is much for efficient. .. _java.util.Map: Maps ---- Java classes that implement ``java.util.Map`` behave like Python dictionaries. They support key-value access, iteration, and deletion. Here is a summary of their capabilities: =========================== ================================ Action Python =========================== ================================ Place a value in the map ``jmap[key]=value`` Delete an entry ``del jmap[key]`` Get the length ``len(jmap)`` Lookup the value ``v=jmap[key]`` Get the entries ``jmap.items()`` Fetch the keys ``jmap.key()`` Check for a key ``key in jmap`` =========================== ================================ Example using Java HashMap with Pythonic interface: .. code-block:: python from java.util import HashMap jmap = HashMap() jmap.put("key1", "value1") jmap.put("key2", "value2") print(jmap["key1"]) # Output: value1 del jmap["key2"] # Remove "key2" print(len(jmap)) # Output: 1 Maps also support iteration over keys and values: .. code-block:: python for key, value in jmap.items(): print(f"{key}: {value}") Methods that accept Java maps can implicitly convert Python dictionaries if all keys and values are compatible with Java types. Otherwise, a ``TypeError`` is raised. .. _collections_map_entries: Map Entries ----------- Java map entries unpack into key-value pairs, allowing easy iteration in Python loops. For example: .. code-block:: python for key, value in jmap.items(): print(f"{key}: {value}") .. _collections_sets: Sets ---- Java classes that implement ``java.util.Set`` behave like Python sets. They support operations such as item deletion, iteration, and length retrieval. For example: .. code-block:: python from java.util import HashSet jset = HashSet() jset.add("apple") jset.add("orange") print(len(jset)) # Output: 2 jset.remove("orange") print(jset) # Output: [apple] .. _collections_enumeration: Enumeration ----------- Java classes that implement ``java.util.Enumeration`` act as Python iterators. This allows them to be used in Python `for` loops and list comprehensions. For example: .. code-block:: python from java.util import Vector jvector = Vector() jvector.add("apple") jvector.add("orange") enumeration = jvector.elements() for item in enumeration: print(item) .. _collections_integrating_pythonic_constructs_with_java_collections: Integrating Pythonic Constructs with Java Collections ====================================================== JPype enables Python developers to interact with Java collections and streams while leveraging Python's idiomatic constructs, such as list comprehensions and generator expressions. This section explores how Pythonic constructs and Java methods can be used interchangeably or combined for efficient manipulation of data structures. .. _collections_using_pythonic_constructs_with_java_collections: Using Pythonic Constructs with Java Collections ------------------------------------------------ JPype enables Python developers to interact with Java collections while leveraging Python's idiomatic constructs, such as list comprehensions and generator expressions. For example: .. code-block:: python from java.util import ArrayList jlist = ArrayList() jlist.add("apple") jlist.add("orange") jlist.add("banana") filtered = [item.upper() for item in jlist if item.startswith("a")] print(filtered) # Output: ['APPLE'] Combining Pythonic constructs with Java methods allows developers to use the best tools for the task, whether they prefer Python's simplicity or Java's robustness. .. _collections_using_java_streams_for_functional_operations: Using Java Streams for Functional Operations --------------------------------------------- Java's `Stream` API provides powerful functional programming constructs, such as `filter`, `map`, and `reduce`. JPype allows Python developers to use these methods with Java collections, enabling them to leverage Java's robust libraries. .. _collections_example_filtering_and_mapping_with_java_streams: Example: Filtering and Mapping with Java Streams ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from java.util.stream import Collectors # Use Java Stream API for filtering and mapping filtered = jlist.stream().filter(lambda s: s.startswith("a")).map( lambda s: s.upper()).collect(Collectors.toList()) print(filtered) # Output: [APPLE] Advantages of Java Streams: - Integration with Java's enterprise libraries. - Parallel processing capabilities (e.g., `.parallelStream()`). - Type-safe operations with Java's generics. .. _collections_comparison_pythonic_constructs_vs_java_methods: Comparison: Pythonic Constructs vs Java Methods ------------------------------------------------ +---------------------------+---------------------------------------+ | **Feature** | **Pythonic Constructs** | | | **(List Comprehensions)** | +---------------------------+---------------------------------------+ | Syntax | Concise and readable | +---------------------------+---------------------------------------+ | Performance | Python interpreter overhead | +---------------------------+---------------------------------------+ | Parallel Processing | Requires external libraries | | | (e.g., `multiprocessing`) | +---------------------------+---------------------------------------+ | Type Safety | Dynamic typing | +---------------------------+---------------------------------------+ | Ease of Use | Familiar to Python developers | +---------------------------+---------------------------------------+ .. _collections_combining_pythonic_constructs_and_java_methods: Combining Pythonic Constructs and Java Methods ---------------------------------------------- JPype allows developers to mix Pythonic constructs and Java methods for maximum flexibility. For example, you can use Java streams for complex operations and Pythonic constructs for post-processing. .. _collections_example_combining_streams_and_list_comprehensions: Example: Combining Streams and List Comprehensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python # Use Java Stream API for filtering filtered_stream = jlist.stream().filter(lambda s: s.startswith("a")).collect( Collectors.toList()) # Use Pythonic list comprehension for further processing final_result = [item.lower() for item in filtered_stream] print(final_result) # Output: ['apple'] .. _collections_when_to_use_each_approach: When to Use Each Approach ------------------------- +-------------------------------------------+---------------------------+ | **Scenario** | **Recommended Approach** | +-------------------------------------------+---------------------------+ | Simple filtering or mapping | Pythonic constructs | | | (list comprehensions) | +-------------------------------------------+---------------------------+ | Complex operations (e.g., grouping, | Java Streams | | reducing) | | +-------------------------------------------+---------------------------+ | Integration with Java enterprise | Java Streams | | libraries | | +-------------------------------------------+---------------------------+ | Quick prototyping or debugging | Pythonic constructs | +-------------------------------------------+---------------------------+ | Parallel processing | Java Streams | | | (`parallelStream`) | +-------------------------------------------+---------------------------+ .. _collections_best_practices_for_collection_processing: Best Practices for Collection Processing ---------------------------------------- 1. **Choose the Right Tool for the Job**: - Use Pythonic constructs for simplicity and readability. - Use Java streams for performance-critical or enterprise applications. 2. **Leverage JPype's Seamlessness**: - Combine Pythonic constructs and Java methods to get the best of both worlds. 3. **Optimize for Performance**: - Avoid frequent back-and-forth calls between Python and Java. Cache results when possible. .. _collections_conclusion_on_collection_processing: Conclusion on Collection Processing ----------------------------------- JPype enables Python developers to work with Java collections using both Pythonic constructs and Java methods. Whether you prefer Python's simplicity or Java's robustness, JPype provides the flexibility to choose the paradigm that best fits your workflow. .. _serialization_with_jpickler: Serialization with JPickler *************************** JPype provides the **JPickler** utility for serializing (`pickling`) Java objects into Python-compatible byte streams. This is particularly useful for saving Java objects to disk, transferring them between systems, or debugging their state. .. _serialization_with_jpickler_why_use_jpickler: Why Use JPickler? ================= When working with Java objects in Python, serialization is often required for: 1. **Persistence**: Saving Java objects to files for later use. 2. **Data Exchange**: Transferring Java objects between Python applications or systems. 3. **Debugging**: Capturing the state of Java objects during execution for offline analysis. However, Python's default `pickle` module does not support Java objects. JPickler bridges this gap by encoding Java objects into a format compatible with Python's serialization tools. .. _serialization_with_jpickler_how_jpickler_works: How JPickler Works ------------------ JPickler uses Java's `Serializable` interface to serialize Java objects into a byte stream that can be stored or transferred. It also provides a companion utility, **JUnpickler**, for deserializing these byte streams back into Java objects. .. _serialization_with_jpickler_example_1_basic_serialization: Example 1: Basic Serialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following example demonstrates how to serialize and deserialize Java objects using JPickler and JUnpickler. .. code-block:: python import jpype import jpype.imports from jpype.pickle import JPickler, JUnpickler # Start the JVM jpype.startJVM() # Create a Java object java_list = jpype.java.util.ArrayList() java_list.add("Hello") java_list.add("World") # Serialize the Java object to a file with open("serialized_java_list.pkl", "wb") as f: JPickler(f).dump(java_list) print("Java object serialized successfully!") # Deserialize the Java object from the file with open("serialized_java_list.pkl", "rb") as f: deserialized_list = JUnpickler(f).load() print("Deserialized Java object:", deserialized_list) # Output: [Hello, World] .. _serialization_with_jpickler_example_2_serializing_complex_java_objects: Example 2: Serializing Complex Java Objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JPickler can handle any Java object that implements `java.io.Serializable`. Here's an example with a custom Java class: .. code-block:: java // Save this as MySerializableClass.java and compile it import java.io.Serializable; public class MySerializableClass implements Serializable { private String name; private int value; public MySerializableClass(String name, int value) { this.name = name; this.value = value; } @Override public String toString() { return "MySerializableClass{name='" + name + "', value=" + value + "}"; } } .. code-block:: python import jpype import jpype.imports from jpype.pickle import JPickler, JUnpickler # Start the JVM jpype.startJVM(classpath=["."]) # Create an instance of the custom Java class MySerializableClass = jpype.JClass("MySerializableClass") java_object = MySerializableClass("TestObject", 42) # Serialize the Java object to a file with open("serialized_java_object.pkl", "wb") as f: JPickler(f).dump(java_object) print("Custom Java object serialized successfully!") # Deserialize the Java object from the file with open("serialized_java_object.pkl", "rb") as f: deserialized_object = JUnpickler(f).load() print("Deserialized Java object:", deserialized_object) # Output: MySerializableClass{name='TestObject', value=42} .. _serialization_with_jpickler_best_practices_with_jpicker: Best Practices with JPicker =========================== 1. **Ensure Objects Are Serializable**: - Only Java objects that implement `java.io.Serializable` can be serialized. Ensure your custom Java classes implement this interface. 2. **Validate Serialization**: - Test serialization and deserialization to ensure data integrity. 3. **Handle Non-Serializable Fields**: - If a Java object contains non-serializable fields, mark them as `transient` to exclude them during serialization. 4. **Avoid Reference Loops**: - Break reference loops between Python and Java objects to prevent memory leaks. .. _serialization_with_jpickler_limitations_of_jpickler: Limitations of JPickler ======================= 1. **Non-Serializable Objects**: - Java objects that do not implement `java.io.Serializable` cannot be serialized with JPickler. 2. **Cross-Version Compatibility**: - Serialized Java objects may not be compatible across different JVM versions. 3. **Performance**: - Serialization and deserialization can be resource-intensive for large or complex objects. .. _serialization_with_jpickler_use_cases_of_jpickler: Use Cases of JPickler ===================== 1. **Persistence**: - Save Java objects to disk for later use. - Example: Storing application state or configuration. 2. **Data Exchange**: - Transfer Java objects between Python applications or systems. - Example: Network communication or distributed systems. 3. **Debugging**: - Capture the state of Java objects during execution for offline analysis. - Example: Serialize problematic objects for inspection after a crash. .. _serialization_with_jpickler_conclusion_for_jpicker: Conclusion for JPicker ====================== JPickler simplifies serialization of Java objects in Python, enabling seamless integration between the two ecosystems. By following best practices and understanding its limitations, you can use JPickler effectively for persistence, data exchange, and debugging tasks. .. _working_with_numpy: Working with NumPy ****************** JPype provides seamless integration between Python's NumPy library and Java, enabling efficient data exchange and manipulation across both ecosystems. By leveraging JPype's ability to transfer arrays bidirectionally, users can combine NumPy's powerful numerical computing capabilities with Java's robust libraries for machine learning, scientific computing, and enterprise applications. Whether transferring data to NumPy for analysis or sending arrays to Java for processing, JPype ensures high performance and compatibility with minimal overhead. This integration is particularly useful for applications requiring large-scale numerical computations or interoperability between Python and Java-based systems. .. _working_with_numpy_transferring_arrays_between_python_and_java: Transferring Arrays Between Python and Java =========================================== JPype supports bidirectional transfers of arrays between Python (NumPy) and Java. This allows seamless integration of numerical libraries with Java's ecosystem. .. _working_with_numpy_transferring_arrays_to_numpy: Transferring Arrays to NumPy ---------------------------- Java arrays can be transferred into NumPy arrays using Python's `memoryview`. This enables efficient bulk data transfer for rectangular arrays. **Example: Transferring a Java Array to NumPy** .. code-block:: python import jpype import numpy as np # Start the JVM jpype.startJVM() # Create a Java array java_array = jpype.JDouble[:]([1.1, 2.2, 3.3]) # Transfer the Java array to NumPy numpy_array = np.array(memoryview(java_array)) print(numpy_array) # Output: [1.1 2.2 3.3] **Constraints**: - The Java array must be rectangular. Jagged arrays are not supported. - Only primitive types (e.g., `double`, `int`) are supported for direct transfer. .. _working_with_numpy_transferring_arrays_to_java: Transferring Arrays to Java --------------------------- NumPy arrays can be transferred to Java using the `JArray.of` function. This maps the structure of a NumPy array to a Java multidimensional array. **Example: Transferring a NumPy Array to Java** .. code-block:: python import jpype import numpy as np # Start the JVM jpype.startJVM() # Create a NumPy array numpy_array = np.zeros((5, 10, 20)) # 5x10x20 array filled with zeros # Transfer the array to Java java_array = jpype.JArray.of(numpy_array) print(java_array[0][0][0]) # Output: 0.0 **Constraints**: - The NumPy array must be rectangular. Jagged arrays are not supported. - Data types must be compatible with Java primitives (e.g., `np.float64` β†’ `double`). .. _working_with_numpy_requirements_and_constraints: Requirements and Constraints ---------------------------- 1. **Rectangular Arrays**: - Both NumPy and Java arrays must be rectangular for direct transfer. 2. **Data Type Compatibility**: - NumPy types must map to Java primitives (e.g., `np.int32` β†’ `int`). 3. **Error Handling**: - Jagged arrays or incompatible types will raise a `TypeError`. .. _working_with_numpy_best_practices_with_numpy: Best Practices with NumPy ------------------------- 1. **Validate Array Structure**: - Ensure arrays are rectangular before transferring. 2. **Optimize Data Types**: - Use NumPy types that map directly to Java primitives for efficiency. 3. **Monitor Memory Usage**: - Large arrays can consume significant memory. Monitor resources carefully. .. _working_with_numpy_summary_of_numpy: Summary of NumPy ---------------- JPype provides efficient bidirectional array transfers between Python and Java. By following the outlined constraints and best practices, users can achieve seamless integration for numerical and scientific applications. .. _working_with_numpy_buffer_backed_numpy_arrays: Buffer Backed NumPy Arrays ========================== Java direct buffers provide a mechanism for shared memory between Java and Python, enabling high-speed data exchange by bypassing the JNI layer. These buffers are particularly useful for applications requiring efficient handling of large datasets, such as scientific computing or memory-mapped files. Direct buffers are part of the Java ``nio`` package and can be accessed using the ``jpype.nio`` module. NumPy arrays can be backed by Java direct buffers, allowing Python and Java to operate on the same memory space. However, direct buffers are not managed by the garbage collector, so improper use may lead to memory leaks or crashes. .. _working_with_numpy_creating_buffer_backed_arrays: Creating Buffer Backed Arrays ----------------------------- To create a buffer-backed NumPy array, you can either originate the buffer in Java or Python. The following examples demonstrate both approaches: **Example 1: Creating a Buffer in Java** .. code-block:: python import jpype import numpy as np # Start the JVM jpype.startJVM() # Allocate a direct buffer in Java jb = java.nio.ByteBuffer.allocateDirect(80) # Allocates 80 bytes db = jb.asDoubleBuffer() # Converts to a double buffer # Convert the buffer to a NumPy array np_array = np.asarray(db) # NumPy array backed by Java buffer print(np_array) **Example 2: Creating a Buffer in Python** .. code-block:: python import jpype import numpy as np # Start the JVM jpype.startJVM() # Create a Python bytearray py_buffer = bytearray(80) # Allocates 80 bytes jb = jpype.nio.convertToDirectBuffer(py_buffer) # Maps the bytearray to Java db = jb.asDoubleBuffer() # Converts to a double buffer # Convert the buffer to a NumPy array np_array = np.asarray(db) # NumPy array backed by Python buffer print(np_array) .. _working_with_numpy_important_considerations_for_buffer_backed_arrays: Important Considerations for Buffer Backed Arrays ------------------------------------------------- 1. **Buffer Lifetime**: - Python and NumPy cannot detect when a Java buffer becomes invalid. Once the JVM is shut down, all buffers originating from Java become invalid, and any access to them may result in crashes. - To avoid this, create buffers in Python and pass them to Java, ensuring Python retains control over the memory. 2. **JVM Shutdown**: - If buffers are created in Java, consider using ``java.lang.Runtime.exit`` to terminate both the Java and Python processes simultaneously. This prevents accidental access to dangling buffers. 3. **Applications**: - Buffer-backed memory is not limited to NumPy. It can be used for shared memory between processes, memory-mapped files, or any application requiring efficient data exchange. .. _working_with_numpy_summary_of_buffer_backed_arrays: Summary of Buffer Backed Arrays ------------------------------- Buffer-backed NumPy arrays provide a powerful mechanism for high-speed data exchange between Python and Java. However, users must carefully manage buffer lifetimes and ensure proper handling during JVM shutdown to avoid crashes or memory leaks. .. _working_with_numpy_numpy_primitives: Both Python and Java have a notion of readonly memory (bytes vs bytearray in Python). ConvertToDirectBuffer will honor the the writability of the passed object and return a readonly Java ByteBuffer if the source object is readonly. NumPy Primitives ================ JPype provides seamless integration with NumPy, allowing efficient data transfers between Python and Java. NumPy arrays can be mapped to Java boxed types or primitive arrays. However, certain types, such as `np.float16`, are converted to compatible Java types during transfer. .. _working_with_numpy_supported_numpy_types: Supported NumPy Types --------------------- The following table summarizes how NumPy types are mapped to Java boxed types and primitive arrays: ================= ============================ NumPy Type Java Type (Boxed/Primitive) ================= ============================ np.int8 java.lang.Byte / byte[] np.int16 java.lang.Short / short[] np.int32 java.lang.Integer / int[] np.int64 java.lang.Long / long[] np.float16 java.lang.Float / float[] (*) np.float32 java.lang.Float / float[] np.float64 java.lang.Double / double[] ================= ============================ (*) `np.float16` will be automatically converted to `float32` (`java.lang.Float` or `float[]`) during the transfer to Java. .. note:: `np.float16` can be transferred to Java, but it will be automatically converted to `float32` (`java.lang.Float` for boxed types or `float[]` for primitive arrays) on the Java side. This is because Java does not natively support `float16`. If precise handling of `float16` is required, consider converting the data to `float32` or `float64` explicitly in Python before transferring it. .. _working_with_numpy_examples: Examples -------- The following examples demonstrate how to transfer `np.float16` data to Java as boxed types or primitive arrays. .. _working_with_numpy_example_1_transferring_float16_to_a_boxed_type: Example 1: Transferring `float16` to a Boxed Type ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python import jpype import numpy as np # Start the JVM jpype.startJVM() # Create a NumPy array with float16 data float16_array = np.array([1.1, 2.2, 3.3], dtype=np.float16) # Transfer the array to a Java boxed type (java.util.ArrayList) java_list = jpype.java.util.ArrayList() for value in float16_array: java_list.add(jpype.JFloat(value)) # Automatically converted to float32 print(java_list) # Output: [1.1, 2.2, 3.3] (as float32) .. _working_with_numpy_example_2_transferring_float16_to_a_primitive_array: Example 2: Transferring `float16` to a Primitive Array ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python import jpype import numpy as np # Start the JVM jpype.startJVM() # Create a NumPy array with float16 data float16_array = np.array([1.1, 2.2, 3.3], dtype=np.float16) # Transfer the array to a Java primitive array java_primitive_array = jpype.JArray(jpype.JFloat)(float16_array) # Converted # to float32[] print(java_primitive_array) # Output: [1.1, 2.2, 3.3] (as float32[]) .. _working_with_numpy_summary_of_numpy_automatic_converstions: Summary of NumPy Automatic Converstions --------------------------------------- JPype supports transferring NumPy arrays to Java, with automatic conversions for certain types. While `np.float16` can be transferred, it is converted to `float32` on the Java side for compatibility. Users should be aware of this behavior and plan accordingly when working with `float16` data. .. _Proxies: Calling Python Code from Java ***************************** Proxies in JPype enable Python objects to implement Java interfaces directly, allowing seamless interaction between Python and Java. These proxies are specifically designed to implement Java interfaces, acting as wrapper classes that disguise the Python nature of the object in a Java type-safe manner. In Java, proxies are foreign elements that pretend to implement a Java interface. JPype leverages this proxy API to allow Python code to implement any Java interface. While proxies allow Python objects to fulfill the contract of a Java interface, they are not equivalent to subclassing Java classes in Python. Fortunately, many Java APIs are designed to minimize the need for subclassing. For example, frameworks like AWT and SWING allow developers to create complete user interfaces without requiring a single subclass. Subclassing is typically reserved for more advanced features or specialized use cases. For those cases where sub-classing is absolutely necessary (i.e. using Java's SAXP classes), it is necessary to create an interface and a simple subclass in Java that delegates the calls to that interface. The interface can then be implemented in Python using a proxy. There are three APIs for supporting of Java proxies. The direct method is to pass a Python function, method, bound method, or lambda to any Java method that accepts a FunctionInterface or other SAM. If more complex behaviors need to be exchanged Python can implement a Java interface. Implementation of an interface uses decorators which features strong error checking and easy notation. The older low-level interface allows any Python object or dictionary to act as a proxy even if it does not provide the required methods for the interface. .. _calling_python_code_from_java_passing_python_callables_to_java_functional_interfaces: Passing Python Callables to Java Functional Interfaces ======================================================= JPype supports passing Python functions, methods, and bound methods directly to Java methods or fields that implement `FunctionalInterface`. This allows Python code to seamlessly integrate with Java's functional programming constructs, such as lambdas and method references, without requiring a proxy or explicit implementation of the interface. ### Supported Use Cases This feature works with any Java method or field that expects a `FunctionalInterface`. Common examples include: - Java Streams (`java.util.stream`) - Java Executors (`java.util.concurrent`) - Custom functional interfaces defined in Java code ### Example: Passing a Python Function to a Java Method Suppose you have a Java method that expects a `java.util.function.Function`: .. code-block:: java import java.util.function.Function; public class Example { public static String applyFunction(Function func, String input) { return func.apply(input); } } You can pass a Python function directly to this method: .. code-block:: python import jpype import jpype.imports jpype.startJVM() from java.util.function import Function from Example import Example # Define a Python function def to_uppercase(s): return s.upper() # Pass the Python function to the Java method result = Example.applyFunction(to_uppercase, "hello") print(result) #Output: HELLO ### Example: Using a Lambda Expression Python lambdas can also be passed to Java methods: .. code-block:: python # Pass a lambda expression result = Example.applyFunction(lambda s: s[::-1], "hello") print(result) #Output: olleh ### Example: Using a Bound Method Bound methods of Python objects can be passed as well: .. code-block:: python class StringManipulator: def reverse(self, s): return s[::-1] manipulator = StringManipulator() result = Example.applyFunction(manipulator.reverse, "hello") print(result) # Output: olleh ### Notes and Best Practices 1. **Performance**: While using Python callables is convenient, it may not be as performant as implementing a full Java proxy for high-frequency calls. Use proxies for performance-critical applications. 2. **Error Handling**: If an exception occurs within the Python callable, it will be wrapped in a `RuntimeException` when passed back to Java. 3. **Type Matching**: Ensure that the Python callable returns a type compatible with the expected Java return type. Implicit conversions will be applied where possible. By leveraging this feature, you can simplify integration between Python and Java, especially when working with Java's functional programming APIs. .. _@JImplements: Implements ========== The newer style of proxy works by decorating any ordinary Python class to designate it as a proxy. This is most effective when you control the Python class definition. If you don't control the class definition you either need to encapsulate the Python object in another object or use the older style. Implementing a proxy is simple. First construct an ordinary Python class with method names that match the Java interface to be implemented. Then add the ``@JImplements`` decorator to the class definition. The first argument to the decorator is the interface to implement. Then mark each method corresponding to a Java method in the interface with ``@JOverride``. When the proxy class is declared, the methods will be checked against the Java interface. Any missing method will result in JPype raising an exception. High-level proxies have one other important behavior. When a proxy created using the high-level API returns from Java it unpacks back to the original Python object complete with all of its attributes. This occurs whether the proxy is the ``self`` argument for a method or proxy is returned from a Java container such as a list. This is accomplished because the actually proxy is a temporary Java object with no substance, thus rather than returning a useless object, JPype unpacks the proxy to its original Python object. .. _calling_python_code_from_java_proxy_method_overloading: Proxy Method Overloading ------------------------ Overloaded methods will issue to a single method with the matching name. If they take different numbers of arguments then it is best to implement a method dispatch: .. code-block:: python @JImplements(JavaInterface) class MyImpl: @JOverride def callOverloaded(self, *args): # always use the wild card args when implementing a dispatch if len(args)==2: return self.callMethod1(*args) if len(args)==1 and isinstance(args[0], JString): return self.callMethod2(*args) raise RuntimeError("Incorrect arguments") def callMethod1(self, a1, a2): # ... def callMethod2(self, jstr): # ... .. _calling_python_code_from_java_multiple_interfaces: Multiple interfaces ------------------- Proxies can implement multiple interfaces as long as none of those interfaces have conflicting methods. To implement more than one interface, use a list as the argument to the JImplements decorator. Each interface must be implemented completely. .. _calling_python_code_from_java_deferred_realization: Deferred realization -------------------- Sometimes it is useful to implement proxies before the JVM is started. To achieve this, specify the interface using a string and add the keyword argument ``deferred`` with a value of ``True`` to the decorator. .. code-block:: python @JImplements("org.foo.JavaInterface", deferred=True) class MyImpl: # ... Deferred proxies are not checked at declaration time, but instead at the time for the first usage. Because of this, when uses an deferred proxy the code must be able to handle initialization errors wherever the proxy is created. Other than the raising of exceptions on creation, there is no penalty to deferring a proxy class. The implementation is checked once on the first usage and cached for the remaining life of the class. .. _calling_python_code_from_java_proxy_factory: Proxy Factory ============= When a foreign object from another module for which you do not control the class implementation needs to be passed into Java, the low level API is appropriate. In this API you manually create a JProxy object. The proxy object must either be a Python object instance or a Python dictionary. Low-level proxies use the JProxy API. .. _calling_python_code_from_java_jproxy: JProxy ------ The ``JProxy`` allows Python code to "implement" any number of Java interfaces, so as to receive callbacks through them. The JProxy factory has the signature:: JProxy(intr, [dict=obj | inst=obj] [, deferred=False]) The first argument is the interface to be implemented. This may be either a string with the name of the interface, a Java class, or a Java class instance. If multiple interfaces are to be implemented the first argument is replaced by a Python sequence. The next argument is a keyword argument specifying the object to receive methods. This can either be a dictionary ``dict`` which names the methods as keys or an object instance ``inst`` which will receive method calls. If more than one option is selected, a ``TypeError`` is raised. When Java calls the proxy the method is looked up in either the dictionary or the instance and the resulting method is called. Any exceptions generated in the proxy will be wrapped as a ``RuntimeException`` in Java. If that exception reaches back to Python it is unpacked to return the original Python exception. Assume a Java interface like: .. code-block:: java public interface ITestInterface2 { int testMethod(); String testMethod2(); } You can create a proxy *implementing* this interface in two ways. First, with an object: .. code-block:: python class C: def testMethod(self): return 42 def testMethod2(self): return "Bar" c = C() # create an instance proxy = JProxy("ITestInterface2", inst=c) # Convert it into a proxy or you can use a dictionary. .. code-block:: python def _testMethod(): return 32 def _testMethod2(): return "Fooo!" d = { 'testMethod': _testMethod, 'testMethod2': _testMethod2, } proxy = JProxy("ITestInterface2", dict=d) .. _calling_python_code_from_java_wrapping_a_python_instance_with_new_behaviors_for_java: Wrapping a Python instance with new behaviors for Java ====================================================== JPype allows a JProxy to implement Java interfaces using a combination of a dictionary (dict) and an object instance (inst). This feature enables arbitrary Python objects to dynamically define methods via a dictionary while also providing methods from the object's class. The combined approach is particularly useful for cases where some methods are predefined in a Python class and others need to be dynamically added or overridden. This is useful when the names and functionality of a Python object need to be made to conform to Java's expected behaviors. .. _calling_python_code_from_java_syntax: Syntax ------ The JProxy factory supports both dict and inst as keyword arguments. When both are provided: * Methods in the dictionary take precedence. * The inst object is passed as the self argument to methods defined in the dictionary. * If a method is not found in the dictionary, JPype will fall back to the default method implementation in Java. .. code-block:: python JProxy(interface, dict=my_dict, inst=my_instance) Example: Combining dict and inst Suppose you have a Java interface: .. code-block:: java public interface MyInterface { String method1(); String method2(); } You can implement this interface using both a dictionary and an object instance: .. code-block:: python public interface MyInterface { String method1(); default String method2() { return "hello"; } } You can implement this interface using both a dictionary and an object instance: .. code-block:: python from jpype import JProxy class MyClass: def __init__(self, name): self.name = name # Define a dictionary with methods my_dict = { "method1": lambda self: f"Hello, {self.name} from method1" } # Create an instance of the class my_instance = MyClass("Alice") # Combine the dictionary and instance in a JProxy proxy = JProxy("MyInterface", dict=my_dict, inst=my_instance) # Use the proxy in Java print(proxy.method1()) # Output: Hello, Alice from method1 print(proxy.method2()) # Falls back to Java's default method implementation .. _calling_python_code_from_java_notes_and_best_practices: Notes and Best Practices ------------------------ Method Resolution: * Methods in the dictionary take precedence over methods in the instance. * If a method is not found in the dictionary, JPype will attempt to resolve it in the instance. Error Handling: * If neither the dictionary nor the instance provides the required method, a NotImplementedError will be raised. Flexibility: This approach allows dynamic addition or overriding of methods via the dictionary while retaining the benefits of object-oriented programming with the instance. Example: Dynamic Overrides You can dynamically override methods in the instance using the dictionary: .. code-block:: python class MyClass: def __init__(self, name): self.name = name # Define a dictionary to override methods my_dict = { "method1": lambda self: f"Overridden method1 for {self.name}" } my_instance = MyClass("Bob") proxy = JProxy("MyInterface", dict=my_dict, inst=my_instance) print(proxy.method1()) # Output: Overridden method1 for Bob print(proxy.method2()) # Falls back to Java's default method implementation .. _calling_python_code_from_java_proxying_python_objects: Proxying Python objects ======================= Sometimes it is necessary to push a Python object into Java memory space as an opaque object. This can be achieved using be implementing a proxy for an interface which has no methods. For example, ``java.io.Serializable`` has no arguments and little functionality beyond declaring that an object can be serialized. As low-level proxies to not automatically convert back to Python upon returning to Java, the special keyword argument ``convert`` should be set to True. For example, let's place a generic Python object such as NumPy array into Java. .. code-block:: python import numpy as np u = np.array([[1,2],[3,4]]) ls = java.util.ArrayList() ls.add(jpype.JProxy(java.io.Serializable, inst=u, convert=True)) u2 = ls.get(0) print(u is u2) # True! We get the expected result of ``True``. The Python has passed through Java unharmed. In future versions of JPype, this method will be extended to provide access to Python methods from within Java by implementing a Java interface that points to back to Python objects. .. _calling_python_code_from_java_reference_loops: Reference Loops =============== It is strongly recommended that object used in proxies must never hold a reference to a Java container. If a Java container is asked to hold a Python object and the Python object holds a reference to the container, then a reference loop is formed. Both the Python and Java garbage collectors are aware of reference loops within themselves and have appropriate handling for them. But the memory space of the other machine is opaque and neither Java nor Python is aware of the reference loop. Therefore, unless you manually break the loop by either clearing the container, or removing the Java reference from Python these objects can never be collected. Once you lose the handle they will both become immortal. Ordinarily the proxy by itself would form a reference loop. The Python object points to a Java invocation handler and the invocation handler points back to Python object to prevent the Python object from going away as long as Java is holding onto the proxy. This is resolved internally by making the Python weak reference the Java portion. If Java ever garbage collects the Java half, it is recreated again when the proxy is next used. This does have some consequences for the use of proxies. Proxies must never be used as synchronization objects. Whenever they are garbage collected, they lose their identity. In addition, their hashCode and system id both are reissued whenever they are refreshed. Therefore, using a proxy as a Java map key can be problematic. So long as it remains in the Java map, it will maintain the same identify. But once it is removed, it is free to switch identities every time it is garbage collected. .. _awtswing: AWT/Swing ********* Java GUI elements can be used from Python. To use Swing elements the event loop in Java must be started from a user thread. This will prevent the JVM from shutting down until the user thread is completed. Here is a simple example which creates a hello world frame and launches it from within Python. .. code-block:: python import jpype import jpype.imports jpype.startJVM() import java import javax from javax.swing import * def createAndShowGUI(): frame = JFrame("HelloWorldSwing") frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) label = JLabel("Hello World") frame.getContentPane().add(label) frame.pack() frame.setVisible(True) # Start an event loop thread to handling gui events @jpype.JImplements(java.lang.Runnable) class Launch: @jpype.JOverride def run(self): createAndShowGUI() javax.swing.SwingUtilities.invokeLater(Launch()) .. _concurrent_processing: Concurrent Processing ********************* This chapter covers the topic of threading, synchronization, and multiprocess. Much of this material depends on the use of Proxies_ covered in the prior chapter. .. _concurrent_processing_threading: Threading ========= JPype supports all types of threading subject to the restrictions placed by Python. Java is inherently threaded and support a vast number of threading styles such as execution pools, futures, and ordinary thread. Python is somewhat more limited. At its heart Python is inherently single threaded and requires a master lock known as the GIL (Global Interpreter Lock) to be held every time a Python call is made. Python threads are thus more cooperative that Java threads. To deal with this behavior, JPype releases the GIL every time it leaves from Python into Java to any user defined method. Shorter defined calls such as to get a string name from from a class may not release the GIL. Every time the GIL is released it is another opportunity for Python to switch to a different cooperative thread. .. _concurrent_processing_threading_python_threads: Python Threads -------------- For the most part, Python threads based on OS level threads (i.e. POSIX threads) will work without problem. The only challenge is how Java sees threads. In order to operate on a Java method, the calling thread must be attached to Java. Failure to attach a thread will result in a segmentation fault. It used to be a requirement that users manually attach their thread to call a Java function, but as the user has no control over the spawning of threads by other applications such as an IDE, this inevitably lead to unexpected segmentation faults. Rather that crashing randomly, JPype automatically attachs any thread that invokes a Java method. These threads are attached automatically as daemon threads so that will not prevent the JVM from shutting down properly upon request. If a thread must be attached as a non-daemon, use the method ``java.lang.Thread.attach()`` from within the thread context. Once this is done the JVM will not shut down until that thread is completed. There is a function called ``java.lang.Thread.isAttached()`` which will check if a thread is attached. As threads automatically attach to Java, the only way that a thread would not be attached is if it has never called a Java method. The downside of automatic attachment is that each attachment allocates a small amount of resources in the JVM. For applications that spawn frequent dynamically allocated threads, these threads will need to be detached prior to completing the thread with ``java.lang.Thread.detach()``. When implementing dynamic threading, one can detach the thread whenever Java is no longer needed. The thread will automatically reattach if Java is needed again. There is a performance penalty each time a thread is attached and detached. .. _concurrent_processing_threading_java_threads: Java Threads ------------ To use Java threads, create a Java proxy implementins ``java.lang.Runnable``. The Runnable can then be passed any Java threading mechanism to be executed. Each time that Java threads transfer control back to Python, the GIL is reacquired. .. _concurrent_processing_threading_other_threads: Other Threads ------------- Some Python libraries offer other kinds of thread, (i.e. microthreads). How they interact with Java depends on their nature. As stated earlier, any OS- level threads will work without problem. Emulated threads, like microthreads, will appear as a single thread to Java, so special care will have to be taken for synchronization. .. _concurrent_processing_customizing_javalangthread: Customizing java.lang.Thread ============================ .. _concurrent_processing_overview: Overview -------- JPype automatically attaches Python threads to the JVM when they interact with Java resources. Threads are attached as daemon threads to ensure that they do not block JVM shutdown. While this behavior simplifies integration, it can lead to resource leaks in thread-heavy applications if threads are not properly detached when they terminate. To address this, JPype customizes `java.lang.Thread` with additional methods for managing thread attachment and detachment. These methods allow developers to explicitly detach threads, freeing resources in the JVM and preventing leaks. .. _concurrent_processing_customized_methods: Customized Methods ------------------ The following methods are added to `java.lang.Thread` by JPype: .. method:: java.lang.Thread.attach() Attaches the current thread to the JVM as a user thread. User threads prevent the JVM from shutting down until they are terminated or detached. This method can be used to convert a daemon thread to a user thread. **Raises**: - `RuntimeError`: If the JVM is not running. .. method:: java.lang.Thread.attachAsDaemon() Attaches the current thread to the JVM as a daemon thread. Daemon threads act as background tasks and do not prevent the JVM from shutting down. JPype automatically attaches threads as daemon threads when they interact with Java resources. Use this method to explicitly attach a thread as a daemon. **Raises**: - `RuntimeError`: If the JVM is not running. .. method:: java.lang.Thread.detach() Detaches the current thread from the JVM. This method frees the associated resources in the JVM for the current thread. It is particularly important for thread-heavy applications to prevent leaks. Detaching a thread does not interfere with its ability to reattach later. **Notes**: - This method cannot fail and is safe to call even if the JVM is not running. - There is no harm in calling this method multiple times or detaching threads early. .. _concurrent_processing_examples_with_java_thread: Examples with Java Thread ------------------------- Here are examples of how to use the customized methods for `java.lang.Thread`: .. code-block:: python import jpype import jpype.imports jpype.startJVM() # Attach the thread as a user thread java.lang.Thread.attach() print("Thread attached as a user thread.") # Perform Java operations here... # Detach the thread after completing Java operations java.lang.Thread.detach() print("Thread detached from the JVM.") # Attach the thread as a daemon thread java.lang.Thread.attachAsDaemon() print("Thread attached as a daemon thread.") .. _concurrent_processing_best_practices_for_java_thread: Best Practices for Java Thread ------------------------------ - **Detach Threads When They End**: For thread-heavy applications, ensure that Python threads detach themselves from the JVM before they terminate. This prevents resource leaks and ensures efficient memory usage. - **Avoid Excessive Attachments**: While JPype automatically attaches threads, excessive thread creation without proper detachment can lead to resource exhaustion in the JVM. - **Detach Early**: Detaching threads early, after completing all Java operations, is safe and does not interfere with reattachment later. This is especially important for applications that spawn many short-lived threads. - **Monitor Resource Usage**: Regularly monitor JVM memory usage in thread-heavy applications to identify potential leaks caused by lingering thread attachments. .. _concurrent_processing_summary_of_java_thread: Summary of Java Thread ---------------------- JPype customizes `java.lang.Thread` to provide additional methods for managing thread attachment and detachment to/from the JVM. While JPype automatically attaches threads as daemon threads, it is crucial to detach threads explicitly in thread-heavy applications to prevent resource leaks. By following best practices, developers can ensure efficient memory usage and smooth integration between Python and Java. .. _synchronized: Synchronization =============== Java synchronization support can be split into two categories. The first is the ``synchronized`` keyword, both as prefix on a method and as a block inside a method. The second are the three methods available on the Object class (``notify, notifyAll, wait``). To support the ``synchronized`` functionality, JPype defines a method called ``synchronized(obj)`` to be used with the Python ``with`` statement, where obj has to be a Java object. The return value is a monitor object that will keep the synchronization on as long as the object is kept alive. For example, .. code-block:: python from jpype import synchronized mySharedList = java.util.ArrayList() # Give the list to another thread that will be adding items otherThread.setList(mySharedList) # Lock the list so that we can access it without interference with synchronized(mySharedList): if not mySharedList.isEmpty(): ... # process elements # Resource is unlocked once we leave the block The Python ``with`` statement is used to control the scope. Do not hold onto the monitor without a ``with`` statement. Monitors held outside of a ``with`` statement will not be released until they are broken when the monitor is garbage collected. The other synchronization methods are available as-is on any Java object. However, as general rule one should not use synchronization methods on Java String as internal string representations may not be complete objects. For synchronization that does not have to be shared with Java code, use Python's support directly rather than Java's synchronization to avoid unnecessary overhead. .. _concurrent_processing_threading_examples: Threading examples ================== Java provides a very rich set of threading tools. This can be used in Python code to extend many of the benefits of Java into Python. However, as Python has a global lock, the performance of Java threads while using Python is not as good as native Java code. .. _concurrent_processing_limiting_execution_time: Limiting execution time ----------------------- We can combine proxies and threads to produce achieve a number of interesting results. For example: .. code-block:: python def limit(method, timeout): """ Convert a Java method to asynchronous call with a specified timeout. """ def f(*args): @jpype.JImplements(java.util.concurrent.Callable) class g: @jpype.JOverride def call(self): return method(*args) future = java.util.concurrent.FutureTask(g()) java.lang.Thread(future).start() try: timeunit = java.util.concurrent.TimeUnit.MILLISECONDS return future.get(int(timeout*1000), timeunit) except java.util.concurrent.TimeoutException as ex: future.cancel(True) raise RuntimeError("canceled", ex) return f print(limit(java.lang.Thread.sleep, timeout=1)(200)) print(limit(java.lang.Thread.sleep, timeout=1)(20000)) Here we have limited the execution time of a Java call. .. _concurrent_processing_multiprocessing: Multiprocessing =============== Because only one JVM can be started per process, JPype cannot be used with processes created with ``fork``. Forks copy all memory including the JVM. The copied JVM usually will not function properly thus JPype cannot support multiprocessing using fork. To use multiprocessing with JPype, processes must be created with "spawn". As the multiprocessing context is usually selected at the start and the default for Unix is fork, this requires the creating the appropriate spawn context. To launch multiprocessing properly the following recipe can be used. .. code-block:: python import multiprocessing as mp ctx = mp.get_context("spawn") process = ctx.Process(...) queue = ctx.Queue() # ... When using multiprocessing, Java objects cannot be sent through the default Python ``Queue`` methods as calls pickle without any Java support. This can be overcome by wrapping Python ``Queue`` to first encode to a byte stream using the JPickle package. By wrapping a ``Queue`` with the Java pickler any serializable Java object can be transferred between processes. In addition, a standard Queue will not produce an error if is unable to pickle a Java object. This can cause deadlocks when using multiprocessing IPC, thus wrapping any Queue is required. .. _managing_crossplatform_gui_environments: Managing Cross-Platform GUI Environments **************************************** JPype provides utility functions, `setupGuiEnvironment` and `shutdownGuiEnvironment`, to manage GUI environments across platforms, ensuring compatibility with macOS, Linux, and Windows. These functions are particularly useful for Swing and JavaFX-based applications, where macOS imposes specific requirements for GUI event loops. Even on Linux and Windows, using `setupGuiEnvironment` ensures consistent behavior and avoids potential issues with threading and event loops. .. _managing_crossplatform_gui_environments_setupguienvironmentcb: setupGuiEnvironment(cb) ======================= **Description**: `setupGuiEnvironment` ensures that GUI applications can run correctly across all platforms. It is specifically designed to address macOS's requirement for the main thread to run the event loop, but it is also recommended for Swing and JavaFX applications on Linux and Windows to maintain cross-platform compatibility and proper threading behavior. **Parameters**: - **cb**: A callback function that initializes and launches the GUI application. **Behavior**: - **macOS**: - Creates a Java thread using a `Runnable` proxy. - Starts the macOS event loop using `PyObjCTools.AppHelper.runConsoleEventLoop()`. - **Other Platforms (Linux, Windows)**: - Executes the callback function directly. **Why Use This Function for Swing and JavaFX Applications?** Swing and JavaFX applications often rely on proper threading and event loop management to function correctly. While macOS has strict requirements for running the event loop on the main thread, using `setupGuiEnvironment` on Linux and Windows ensures consistent behavior and avoids potential threading issues, such as race conditions or improper GUI updates. **Example**: .. code-block:: python from jpype import setupGuiEnvironment from javafx.application import Platform def say_hello_later(): """Test function for scheduling a task on the JavaFX Application Thread.""" print("Hello from JavaFX!") def launch_gui(): """Launch the GUI application.""" # Example: Schedule a task on the JavaFX Application Thread Platform.runLater(say_hello_later) print("GUI launched") # Use setupGuiEnvironment to ensure cross-platform compatibility setupGuiEnvironment(launch_gui) .. _managing_crossplatform_gui_environments_reestablishing_an_interactive_shell_on_another_thread: Reestablishing an Interactive Shell on Another Thread ===================================================== When using `setupGuiEnvironment`, the main thread may be occupied by the GUI event loop (particularly on macOS). To allow interactive debugging in Python, you can launch an interactive shell (e.g., IPython) on a separate thread. **Steps**: 1. Use `setupGuiEnvironment` to start the GUI application. 2. Launch an interactive shell on a separate thread using Python's `threading` module. **Example**: .. code-block:: python import threading import IPython def launch_interactive_shell(): """Launch an interactive shell on a separate thread.""" IPython.embed() # Start the interactive shell on another thread thread = threading.Thread(target=launch_interactive_shell) thread.start() By combining this approach with `setupGuiEnvironment`, you can interact with the Python environment while the GUI application is running. .. _managing_crossplatform_gui_environments_shutdownguienvironment: shutdownGuiEnvironment() ======================== **Description**: `shutdownGuiEnvironment` is used to cleanly terminate the macOS event loop. On other platforms, it performs no action. **Behavior**: - **macOS**: - Stops the macOS event loop using `PyObjCTools.AppHelper.stopEventLoop()`. - **Other Platforms (Linux, Windows)**: - No action is taken. **Example**: .. code-block:: python from jpype import shutdownGuiEnvironment # Shutdown the GUI environment (macOS-specific) shutdownGuiEnvironment() .. _managing_crossplatform_gui_environments_best_practices_on_guis: Best Practices on GUIs -------------------------- - **Use `setupGuiEnvironment` for All Platforms**: Even though macOS has specific requirements, using `setupGuiEnvironment` ensures consistent behavior across all platforms, particularly for Swing and JavaFX applications. - **Thread Safety**: Always schedule GUI updates using JavaFX's `Platform.runLater` or Swing's `SwingUtilities.invokeLater` to ensure they occur on the appropriate thread. - **Interactive Debugging**: Launch an interactive shell on a separate thread for debugging while the GUI application is running. - **Exception Handling**: Wrap callback functions in `try-except` blocks to prevent unhandled exceptions from disrupting the GUI. - **Cross-Platform Testing**: Test the application on macOS, Linux, and Windows to ensure compatibility. .. _managing_crossplatform_gui_environments_summary_of_guis: Summary of GUIs =============== The `setupGuiEnvironment` function is a critical tool for managing GUI environments across platforms, particularly for Swing and JavaFX-based applications. It ensures compatibility with macOS's event loop requirements while maintaining simplicity on other platforms. Combined with the ability to launch an interactive shell on a separate thread, this approach provides a robust solution for developing and debugging GUI applications in Python. .. _miscellaneous_topics: Miscellaneous topics ******************** This chapter contains all the stuff that did not fit nicely into the narrative about JPype. Topics include database interfacing, code completion, performance, debugging Java within JPype, debugging JNI and other JPype failures, how caller sensitive methods are dealt with, and finally limitations of JPype. .. _miscellaneous_topics_database_access_with_jpypedbapi2: Database Access with `jpype.dbapi2` =================================== JPype provides the `jpype.dbapi2` module, which allows Python applications to interact with Java-based database drivers using the Python Database API Specification (PEP 249). This module bridges Python and Java, enabling seamless access to databases that lack native Python drivers but provide JDBC drivers. .. _miscellaneous_topics_key_features_of_dbapi2: Key Features of dbapi2 ---------------------- - **PEP 249 Compliance**: Implements the Python Database API Specification for standardized database interaction. - **JDBC Integration**: Uses Java's JDBC (Java Database Connectivity) to connect to databases. - **Cross-Platform**: Supports any database with a JDBC driver, including enterprise databases like Oracle, DB2, and SQL Server. .. _miscellaneous_topics_prerequisites_for_dbapi2: Prerequisites for dbapi2 ------------------------ - Ensure the JVM is started with the appropriate classpath for the JDBC driver. - Obtain the JDBC driver for the target database and include its path in the `classpath`. .. _miscellaneous_topics_example_usage_of_dbapi2: Example Usage of dbapi2 ----------------------- .. code-block:: python import jpype import jpype.dbapi2 as dbapi2 # Start the JVM with the JDBC driver jpype.startJVM(classpath=["path/to/jdbc/driver.jar"]) # Connect to the database connection = dbapi2.connect( "jdbc:database_url", # JDBC URL for the database {"user": "username", "password": "password"} # Connection properties ) # Create a cursor and execute a query cursor = connection.cursor() cursor.execute("SELECT * FROM my_table") # Fetch and process results results = cursor.fetchall() for row in results: print(row) # Close the cursor and connection cursor.close() connection.close() # Shut down the JVM jpype.shutdownJVM() .. _miscellaneous_topics_benefits_of_dbapi2: Benefits of dbapi2 ------------------ - Access databases that lack native Python drivers but provide JDBC drivers. - Leverage advanced features of Java-based database drivers. - Maintain compatibility with Python's standard database API. .. _miscellaneous_topics_limitations_of_dbapi2: Limitations of dbapi2 ---------------------- - Requires a running JVM, which may introduce overhead compared to native Python database drivers. - Performance may be slightly impacted due to Python-Java interaction. .. _miscellaneous_topics_use_cases_of_dbapi2: Use Cases of dbapi2 ------------------- - Connecting to enterprise databases like Oracle, SQL Server, or DB2. - Utilizing advanced capabilities of JDBC drivers within Python applications. .. _miscellaneous_topics_javadoc: Javadoc ======= JPype can display javadoc in ReStructured Text as part of the Python documentation. To access the javadoc, the javadoc package must be located on the classpath. This includes the JDK package documentation. For example to get the documentation for ``java.lang.Class``, we start the JVM with the JDK documentation zip file on the classpath. .. code-block: java import jpype jpype.startJVM(classpath='jdk-11.0.7_doc-all.zip') We can then access the java docs for the String with ``help(java.lang.String)`` or for the methods with ``help(java.lang.String.trim)``. To use the javadoc supplied by a third party include the both the jar and javadoc in the classpath. .. code-block: java import jpype jpype.startJVM(classpath=['gson-2.8.5.jar', 'gson-2.8.5-javadoc.jar']) The parser will ignore any javadoc which cannot be extracted. It has some robustness against tags that are not properly closed or closed twice. Javadoc with custom page layouts will likely not be extracted. If javadoc for a class cannot be located or extracted properly, default documentation will be generated using Java reflection. .. _miscellaneous_topics_autopep8: Autopep8 ======== When Autopep8 is applied a Python script, it reorganizes the imports to conform to E402_. This has the unfortunate side effect of moving the Java imports above the startJVM statement. This can be avoided by either passing in ``--ignore E402`` or setting the ignore in ``.pep8``. .. _E402: https://www.flake8rules.com/rules/E402.html Example: .. code-block:: python import jpype import jpype.imports jpype.startJVM() from gov.llnl.math import DoubleArray Result without ``--ignore E402`` .. code-block:: python from gov.llnl.math import DoubleArray # Fails, no JVM running import jpype import jpype.imports jpype.startJVM() .. _miscellaneous_topics_performance: Performance =========== JPype uses JNI, which is well known in the Java world as not being the most efficient of interfaces. Further, JPype bridges two very different runtime environments, performing conversion back and forth as needed. Both of these can impose performance bottlenecks. JNI is the standard native interface for most, if not all, JVMs, so there is no getting around it. Down the road, it is possible that interfacing with CNI (GCC's Java native interface) may be used. Right now, the best way to reduce the JNI cost is to move time critical code over to Java. Follow the regular Python philosophy : **Write it all in Python, then write only those parts that need it in C.** Except this time, it's write the parts that need it in Java. Everytime an object is passed back and forth, it will incure a conversion cost.. In cases where a given object (be it a string, an object, an array, etc ...) is passed often into Java, the object should be converted once and cached. For most situations, this will address speed issues. To improve speed issues, JPype has converted all of the base classes into CPython. This is a very significant speed up over the previous versions of the module. In addition, JPype provides a number of fast buffer transfer methods. These routines are triggered automatically working with any buffer aware class such as those in NumPy. As a final note, while a JPype program will likely be slower than its pure Java counterpart, it has a good chance of being faster than the pure Python version of it. The JVM is a memory hog, but does a good job of optimizing code execution speeds. .. _miscellaneous_topics_code_completion: Code completion =============== Python supports a number of different code completion engines that are integrated in different Python IDEs. JPype has been tested with both the IPython greedy completion engine and Jedi. Greedy has the disadvantage that is will execute code resulting potentially resulting in an undesirable result in Java. JPype is Jedi aware and attempts to provide whatever type information that is available to Jedi to help with completion tasks. Overloaded methods are opaque to Jedi as the return type cannot be determined externally. If all of the overloads have the same return type, the JPype will add the return type annotation permitting Jedi to autocomplete through a method return. For example: .. code-block:: python JString("hello").substring.__annotations__ # Returns {'return': } Jedi can manually be tested using the following code. .. code-block:: python js = JString("hello") src = 'js.s' script = jedi.Interpreter(src, [locals()]) compl = [i.name for i in script.completions()] This will produce a list containing all method and field that begin with the letter "s". JPype has not been tested with other autocompletion engines such as Kite. .. _miscellaneous_topics_garbage_collection: Garbage collection ================== Garbage collection (GC) is supposed to make life easier for the programmer by removing the need to manually handle memory. For the most part it is a good thing. However, just like running a kitchen with two chiefs is a bad idea, running with two garbage collections is also bad. In JPype we have to contend with the fact that both Java and Python provide garbage collection for their memory and neither provided hooks for interacting with an external garbage collector. For example, Python is creating a bunch a handles to Java memory for a period of time but they are in a structure with a reference loop internal to Python. The structures and handles are small so Python doesn't see an issue, but each of those handles is holding 1M of memory in Java space. As the heap fills up Java begins garbage collecting, but the resources can't be freed because Python hasn't cleanup up these structures. The reverse occurs if a proxy has any large NumPy arrays. Java doesn't see a problem as it has plenty of space to work in but Python is running its GC like mad trying to free up space to work. To deal with this issue, JPype links the two garbage collectors. Python is more aggressive in calling GC than Java and Java is much more costly than Python in terms of clean up costs. So JPype manages the balance. JPype installs a sentinel object in Java. Whenever that sentinel is collected Java is running out of space and Python is asked to clean up its space as well. The reverse case is more complicated as Python can't just call Java's expensive routine any time it wants. Instead JPype maintains a low-water and high-water mark on Python owned memory. Each time it nears a high-water mark during a Python collection, Java GC gets called. If the water level shrinks than Java was holding up Python memory and the low-water mark is reset. Depending on the amount of memory being exchanged the Java GC may trigger as few as once every 50 Python GC cycles or as often as every other. The sizing on this is dynamic so it should scale to the memory use of a process. .. _miscellaneous_topics_using_jpype_for_debugging_java_code: Using JPype for debugging Java code =================================== One common use of JPype is to function as a Read-Eval-Print Loop for Java. When operating Java though Python as a method of developing or debugging Java there are a few tricks that can be used to simplify the job. Beyond being able to probe and plot the Java data structures interactively, these methods include: 1) Attaching a debugger to the Java JVM being run under JPype. 2) Attaching debugging information to a Java exception. 3) Serializing the state of a Java process to be evaluated at a later point. We will briefly discuss each of these methods. .. _miscellaneous_topics_using_jpype_for_debugging_java_code_attaching_a_debugger: Attaching a Debugger -------------------- Interacting with Java through a shell is great, but sometimes it is necessary to drop down to a debugger. To make this happen we need to start the JVM with options to support remote debugging. We start the JVM with an agent which will provide a remote debugging port which can be used to attach your favorite Java debugging tool. As the agent is altering the Java code to create additional debugging hooks, this process can introduce additional errors or alter the flow of the code. Usually this is used by starting the JVM with the agent, placing a pause marker in the Python code so that developer can attach the Java debugger, executing the Python code until it hits the pause, attaching the debugger, setting break point in Java, and then asking Python to proceed. So lets flesh out the details of how to accomplish this... .. code-block:: python jpype.startJVM("-Xint", "-Xdebug", "-Xnoagent", "-Xrunjdwp:transport=dt_socket,server=y,address=12999,suspend=n") Next, add a marker in the form of a pause statement at the location where the debugger should be attached. .. code-block:: python input("pause to attach debugger") myobj.callProblematicMethod() When Python reaches that point during execution, switch to a Java IDE such as NetBeans and select Debug : Attach Debugger. This brings up a window (see example below). After attaching (and setting desired break points) go back to Python and hit enter to continue. NetBeans should come to the foreground when a breakpoint is hit. .. image:: attach_debugger.png Attach data to an Exception --------------------------- Sometimes getting to the level of a debugger is challenging especially if the code is large and error occurs rarely. In this case, it is often beneficial to attach data to an exception. To achieve this, we need to write a small utility class. Java exceptions are not strictly speaking expandable, but they can be chained. Thus, it we create a dummy exception holding a ``java.util.Map`` and attach it to as the cause of the exception, it will be passed back down the call stack until it reaches Python. We can then use ``getCause()`` to retrieve the map containing the relevant data. .. _miscellaneous_topics_capturing_the_state: Capturing the state ------------------- If the program is not running in an interactive shell or the program run time is long, we may not want to deal with the problem during execution. In this case, we can serialize the state of the relevant classes and variables. To use this option, we mus make sure all of the classes in Java that we are using are ``Serializable``. Then add a condition that detects the faulty algorithm state. When the fault occurs, create a ``java.util.HashMap`` and populate it with the values to be examined from within Python. Use serialization to write the entire structure to a file. Execute the program and collect all of the state files. Once the state files have been collected, start Python with an interactive shell and launch JPype with a classpath for the jars. Finally, deserialize the state files to access the Java structures that have been recorded. .. _miscellaneous_topics_getting_additional_diagnostics: Getting Additional Diagnostics ============================== For the most part, JPype operates as intended, but that does not mean there are no bugs or edge cases. Given the complexity of interactions between Python and Java, untested scenarios may occasionally arise. JPype provides several diagnostic tools to assist in debugging these issues. These tools require accessing private JPype symbols, which may change in future releases. As such, they should not be used in production code. .. _checking-type-cast: Checking the Type of a Cast --------------------------- Sometimes it is difficult to understand why a particular method overload is selected by the method dispatch. To check the match type for a conversion, use the private method ``Class._canConvertToJava``. This will return a string indicating the type of conversion performed: ``none``, ``explicit``, ``implicit``, or ``exact``. To test the result of the conversion process, call ``Class._convertToJava``. Unlike an explicit cast, this method attempts to perform the conversion without bypassing the logic involved in casting. It replicates the exact process used when a method is called or a field is set. .. _cpp-exceptions: C++ Exceptions in JPype ------------------------ Internally, JPype can generate C++ exceptions, which are converted into Python exceptions for the user. To trace an error back to its C++ source, you can enable stack traces for C++ exceptions. Use the following command: .. code-block:: python import _jpype _jpype.enableStacktraces(True) Once enabled, all C++ exceptions that fall through a C++ exception handling block will produce an augmented stack trace. If the JPype source code is available, the stack trace can even include the corresponding lines of code where the exceptions occurred. This can help identify the source of errors that originate in C++ code but propagate to Python as exceptions. .. _tracing: Tracing ------- Tracing mode logs every JNI call, along with object addresses and exceptions, to the console. To enable tracing, JPype must be recompiled with the ``--enable-tracing`` option. Tracing is useful for identifying failures that originate in one JNI call but manifest later. However, this mode produces verbose logs and is recommended only for advanced debugging. .. _instrumentation: Instrumentation --------------- JPype supports an instrumentation mode for testing error-handling paths. This mode allows you to simulate faults at designated points in JPype's execution flow. To enable instrumentation, recompile JPype with the ``--enable-coverage`` option. Once instrumentation is enabled, use the private module command ``_jpype.fault`` to trigger an error. The argument to the fault command must be the name of a function or a predefined fault point. When the fault point is encountered, a ``SystemError`` is raised. Instrumentation is primarily useful for verifying the robustness of JPype's exception handling mechanisms. .. _using-debugger: Using a Debugger ---------------- If JPype crashes, it may be necessary to use a debugger to obtain a backtrace. However, debugging JPype can be challenging due to the JVM's handling of segmentation faults. The JVM intercepts segmentation faults to allocate memory or handle internal operations, which can corrupt stack frames. To debug JPype using tools such as ``gdb`, you must configure the debugger to ignore segmentation faults intentionally triggered by the JVM. For example, use the following command to start ``gdb`` and ignore the first fault: .. code-block:: shell gdb --args python script.py (gdb) handle SIGSEGV nostop noprint pass This configuration allows the debugger to bypass JVM-related faults while capturing legitimate errors. Additionally, disable Python's fault handler to avoid interference with segmentation fault reporting. .. _caller sensitive: Caller-Sensitive Methods ------------------------- Java's security model tracks the caller of certain methods to determine the level of access. These methods, known as "caller-sensitive methods," require special handling in JPype. Examples of caller-sensitive methods include ``Class.forName``, ``java.lang.ClassLoader`` methods, and certain methods in ``java.sql.DriverManager``. To handle caller-sensitive methods, JPype routes calls through an internal Java package, ``org.jpype``, which executes the method within the JVM. This ensures proper security context and avoids access errors. Although this mechanism introduces slight overhead, it is necessary for compatibility with Java's security model. .. _limitations_jvm: JPype Known limitations ======================= This section lists those limitations that are unlikely to change, as they come from external sources. .. _miscellaneous_topics_jpype_known_limitations_annotations: Annotations ----------- Some frameworks such as Spring use Java annotations to indicate specific actions. These may be either runtime annotations or compile time annotations. Occasionally while using JPype someone would like to add a Java annotation to a JProxy method so that a framework like Spring can pick up that annotation. JPype uses the Java supplied ``Proxy`` to implement an interface. That API does not support addition of a runtime annotation to a method or class. Thus, all methods and classes when probed with reflection that are implemented in Python will come back with no annotations. Further, the majority of annotation magic within Java is actually performed at compile time. This is accomplished using an annotation processor. When a class or method is annotated, the compiler checks to see if there is an annotation processor which then can produce new code or modify the class annotations. As this is a compile time process, even if annotations were added by Python to a class they would still not be active as the corresponding compilation phase would not have been executed. This is a limitation of the implementation of annotations by the Java virtual machine. It is technically possible though the use of specialized code generation with the ASM library or other code generation to add a runtime annotation. Or through exploits of the Java virtual machine annotation implementation one can add annotation to existing Java classes. But these annotations are unlikely to be useful. As such JPype will not be able to support class or method annotations. .. _miscellaneous_topics_restarting_the_jvm: Restarting the JVM ------------------- JPype caches many resources to the JVM. Those resource are still allocated after the JVM is shutdown as there are still Python objects that point to those resources. If the JVM is restarted, those stale Python objects will be in a broken state and the new JVM instance will obtain the references to these resulting in a memory leak. Thus it is not possible to start the JVM after it has been shut down with the current implementation. .. _miscellaneous_topics_running_multiple_jvm: Running multiple JVM -------------------- JPype uses the Python global import module dictionary, a global Python to Java class map, and global JNI TypeManager map. These resources are all tied to the JVM that is started or attached. Thus operating more than one JVM does not appear to be possible under the current implementation. Further, as of Java 1.2 there is no support for creating more than one JVM in the same process. Difficulties that would need to be overcome to remove this limitation include: - Finding a JVM that supports multiple JVMs running in the same process. This can be achieved on some architectures by loading the same shared library multiple times with different names. - Alternatively as all available JVM implementations support on one JVM instance per process, a communication layer would have to proxy JNI class from JPype to another process. But this has the distinct problem that remote JVMs cannot register native methods nor share memory without considerable effort. - Which JVM would a static class method call. The class types would need to be JVM specific (ie. ``JClass('org.MyObject', jvm=JVM1)``) - How would a wrapper from two different JVM coexist in the ``jpype._jclass`` module with the same name if different class is required for each JVM. - How would the user specify which JVM a class resource is created in when importing a module. - How would objects in one JVM be passed to another. - How can boxed and String types hold which JVM they will box to on type conversion. Thus it appears prohibitive to support multiple JVMs in the JPype class model. .. _miscellaneous_topics_jpype_known_limitations_errors_reported_by_python_fault_handler: Errors reported by Python fault handler --------------------------------------- The JVM takes over the standard fault handlers resulting in unusual behavior if Python handlers are installed. As part of normal operations the JVM will trigger a segmentation fault when starting and when interrupting threads. Pythons fault handler can intercept these operations and interpret these as real faults. The Python fault handler with then reporting extraneous fault messages or prevent normal JVM operations. When operating with JPype, Python fault handler module should be disabled. This is particularly a problem for running under pytest as the first action it performs is to take over the error handlers. This can be disabled by adding this block as a fixture at the start of the test suite. .. code-block:: python try: import faulthandler faulthandler.enable() faulthandler.disable() except: pass This code enables fault handling and then returns the default handlers which will point back to those set by Java. Python faulthandlers can also interfer with the JIT compiler. The Java Just-In-Time (JIT) compiler determines when a portion of code needs to be compiled into machine code to improve performance. When it does, it triggers either a SEGSEGV or SEGBUS depending on the machine architecture which breaks out any threads which are currently executing the existing code. Because the JIT compiler self triggers, this will cause a failure to appear in a call which worked earlier in the execution without an issue. If the Python fault handler interrupts this process, it will produce a "Fatal Python Error:" followed by either SIGSEGV or SEGBUS. This error message then fails to hit the needed Java handler resulting in a crash. This message will disappear if the JIT compiler is disabled with the option "-Xint". Running without the JIT compiler creates a severe performance penalty so disabling the fault handler should be the preferred solution. Some modules such as Abseil Python Common Libraries (absl) automatically and unconditionally install faulthandlers as part of their normal operation. To prevent an issue simply insert a call to disable the faulthandler after the module has enabled it, using .. code-block:: python import faulthandler faulthandler.disable() For example, absl installs faulthandlers in ``app.run``, thus the first call to main routine would need to disable faulthandlers to avoid potential crashes. .. _miscellaneous_topics_unsupported_java_versions: Unsupported Java Versions ------------------------- JPype now requires the use of the module API, which was introduced in **Java 9**. As a result, the earliest version of Java supported by JPype is **Java 11**, which is part of the Long-Term Support (LTS) release. If you need to use **Java 8**, you must use JPype version **1.5.2 or earlier**, as newer versions of JPype no longer support Java 8. .. _miscellaneous_topics_unsupported_python_versions: Unsupported Python versions --------------------------- .. _miscellaneous_topics_python_38_and_earlier: Python 3.8 and earlier ~~~~~~~~~~~~~~~~~~~~~~ The oldest version of Python that we currently support is Python 3.5. Before Python 3.5 there were a number of structural difficulties in the object model and the buffering API. In principle, those features could be excised from JPype to extend support to older Python 3 series version, but that is unlikely to happen without a significant effort. Recent changes in memory models require Python 3.8 or later. .. _miscellaneous_topics_python_2: Python 2 ~~~~~~~~ CPython 2 support was removed starting in 2020. Please do not report to us that Python 2 is not supported. Python 2 was a major drag on this project for years. Its object model is grossly outdated and thus providing for it greatly impeded progress. When the life support was finally pulled on that beast, I like many others breathed a great sigh of relief and gladly cut out the Python 2 code. Since that time JPype operating speed has improved anywhere from 300% to 10000% as we can now implement everything back in CPython rather than band-aiding it with interpreted Python code. .. _miscellaneous_topics_pypy: PyPy ~~~~ The GC routine in PyPy 3 does not play well with Java. It runs when it thinks that Python is running out of resources. Thus a code that allocates a lot of Java memory and deletes the Python objects will still be holding the Java memory until Python is garbage collected. This means that out of memory failures can be issued during heavy operation. We have addressed linking the garbage collectors between CPython and Java, but PyPy would require a modified strategy. Further, when we moved to a completely Python 3 object model we unfortunately broke some of the features that are different between CPython and PyPy. The errors make absolutely no sense to me. So unless a PyPy developer generously volunteering time for this project, this one is unlikely to happen. .. _miscellaneous_topics_jython_python: Jython Python ~~~~~~~~~~~~~ If for some reason you wandered here to figure out how to use Java from Jython using JPype, you are clearly in the wrong place. On the other hand, if you happen to be a Jython developer who is looking for inspiration on how to support a more JPype like API that perhaps we can assist you. Jython aware Python modules often mistake JPype for Jython at least up until the point that differences in the API triggers an error. .. _miscellaneous_topics_unsupported_java_virtual_machines: Unsupported Java virtual machines --------------------------------- The open JVM implementations *Cacao* and *JamVM* are known not to work with JPype. .. _miscellaneous_topics_unsupported_platforms: Unsupported Platforms --------------------- Some platforms are problematic for JPype due to interactions between the Python libraries and the JVM implementation. .. _miscellaneous_topics_cygwin: Cygwin ~~~~~~ Cygwin was usable with previous versions of JPype, but there were numerous issues for which there is was not good solution solution. Cygwin does not appear to pass environment variables to the JVM properly resulting in unusual behavior with certain windows calls. The path separator for Cygwin does not match that of the Java DLL, thus specification of class paths must account for this. Threading between the Cygwin libraries and the JVM was often unstable. .. _freezing: Freezing ======== JPype supports freezing and deployment with `PyInstaller `_. The hook is included with JPype installations and no extra configuration should be needed. .. _miscellaneous_topics_glossary: Useless Trivia ============== Thrameos, the primary developer, maintains the correct pronounciation of JPype is Jay-Pie-Pee like the word recipe. His position is that application of a silient e is inappropriate for this made up name. 1) Jay-Pie is more emblematic of the project goal which is connection of Java and Python. 2) Rules in English regarding a silent e following a p are poor and depend mostly on the origin of the word. Is it like type from the Greek word typus or French rΓ©cipΓ©? As the y in not being modified as it would otherwise be pronounced as Pie like NumPy and SciPy, that means the only logical conclusion is that it was intended to be voiced. 3) Jay-pipe or gh-pipe implies that this project is built on pipes (also known as series of tubes such as the internet). JPype is based on JNI (Jay-eN-Ey). If you are looking for a pipe based Java connection use Py4J. If you are looking for solution based on JNI use JPypΓ©. Glossary ======== .. _miscellaneous_topics_glossary_b: B - **Boxed Types** Immutable Java objects that wrap primitive types (e.g., `java.lang.Integer` for `int`). Used when primitives need to be treated as objects. .. _miscellaneous_topics_glossary_c: C - **Caller Sensitive Methods** Java methods that determine the caller's access level based on the call stack. JPype uses special mechanisms to handle these methods safely. **Classpath** A parameter specifying the location of Java classes or JAR files required by the JVM. It is essential for loading Java libraries. .. _miscellaneous_topics_glossary_d: D - **Deferred Proxy** A proxy that is created before the JVM is started by specifying the interface as a string and using the `deferred=True` argument. The implementation is checked only when the proxy is first used. .. _miscellaneous_topics_glossary_e: E - **Exact Conversion** A type conversion in JPype where the Python type matches the Java type exactly. Example: Python `int` to Java `int`. .. _miscellaneous_topics_glossary_f: F - **Functional Interface** A Java interface with a single abstract method (SAM). JPype allows Python callables (e.g., functions, lambdas) to be passed directly to Java methods expecting a functional interface. .. _miscellaneous_topics_glossary_g: G - **Garbage Collection (GC)** The process of automatically reclaiming memory occupied by unused objects. JPype links Python's and Java's garbage collectors to avoid memory issues. .. _miscellaneous_topics_glossary_i: I - **Implicit Conversion** A type conversion in JPype where Python types are automatically converted to compatible Java types. Example: Python `int` to Java `long`. **Interface** A Java construct that defines a set of methods without implementations. JPype allows Python classes to implement Java interfaces using proxies. .. _miscellaneous_topics_glossary_m: M - **Mapping** A Python concept for key-value pairs. JPype customizes Java `Map` classes to behave like Python dictionaries. **Multidimensional Arrays** Java arrays with multiple dimensions. JPype supports creating and working with these arrays using nested lists. .. _miscellaneous_topics_glossary_n: N - **NumPy Integration** JPype's ability to efficiently transfer data between Java arrays and NumPy arrays using memory buffers. .. _miscellaneous_topics_glossary_o: O - **Object Instance** A Java object created from a class. Example: `obj = MyClass()` creates an instance of `MyClass`. **Overloaded Methods** Java methods with the same name but different parameter types or counts. JPype selects the appropriate overload based on the Python arguments provided. .. _miscellaneous_topics_glossary_p: P - **Primitive Types** Basic data types in Java (e.g., `int`, `float`, `boolean`). JPype maps these types to Python equivalents (e.g., `JInt`, `JFloat`, `JBoolean`). **Proxy** A proxy is an intermediary that allows Python objects to implement Java interfaces. In the context of JPype, Proxies focus solely on providing the required Java interface functionality. .. _miscellaneous_topics_glossary_s: S - **SAM (Single Abstract Method)** A Java interface with a single abstract method. Used in functional programming and supported by JPype for Python callables. **Synchronized** A Java keyword for thread-safe operations. JPype provides the `jpype.synchronized()` method for similar functionality in Python. .. _miscellaneous_topics_glossary_t: T - **Type Factory** A JPype mechanism for creating Java types in Python. Examples include `JClass` for classes and `JArray` for arrays. .. _miscellaneous_topics_glossary_u: U - **UnsupportedClassVersionError** A JVM error indicating that a JAR file was compiled for a newer Java version than the JVM being used. .. _miscellaneous_topics_glossary_w: W - **Wrapper** A wrapper is an object used to represent an object from one programming language in another programming language, providing a native look and feel. In JPype, wrappers are used to present Java objects to Python, making them behave like native Python objects while retaining their underlying Java functionality. Wrappers encapsulate Java references, such as class instances, arrays, or boxed types, and may also include proxies when implementing Java interfaces. Future versions of JPype aim to introduce the ability to manipulate Python objects from Java. In this case, wrappers will represent Python objects from the perspective of Java, providing a seamless interface for Java code to interact with Python objects. jpype-1.6.0/examples/000077500000000000000000000000001501674766700145065ustar00rootroot00000000000000jpype-1.6.0/examples/jms/000077500000000000000000000000001501674766700152775ustar00rootroot00000000000000jpype-1.6.0/examples/jms/README.TXT000066400000000000000000000036761501674766700166510ustar00rootroot00000000000000This example of using JPype and JMS contributed by David Morgenthaler Here is the message that accompanied it when he posted it on comp.lang.python : ============================================================================= Your question intrigued me, so I gave it a try. And the anwer (at least for JMS Publish/Subscribe TextMessages) is YES. It includes: - Java classes for a publisher (src/messaging/JpypePublisher) and a subscriber (src/messaging/JpypeSubscriber). These Java classes, which do most of the dirty work of connecting to the JMS server, are used from python via jpype. - Java classes that test the publisher and subscriber (src/messaging/testJpepePublisher, src/messaging/testJpypeSubscriber) - A Java interface that is used by python for the JProxy - python scripts for publishing and subscribing - An build.xml for ant to build and run the Java classes The Java and python publishers and subscribers work in any reasonable combination (J pub/J sub, J pub/p sub, p pub/p sub, p pub/J sub, and even with multiple subscribers) Copy conveniently located folder. You'll find a build.xml for ant to build the Java. You'll have to edit (in the testers and in the python scripts) the naming and connection factory stuff for your situation. You'll also have to edit the python scripts to correctly locate your jvm and other classpath information. Once you're correctly configured, you can use ant to build and/or execute the Java (type "ant help"). And once the Java is built, you can try the python versions. I can't imagine that this approach wouldn't work for JMS Queues. And while I haven't tested it, I don't see anything preventing the use of other message types (BinaryMessage, ObjectMessage,...). BTW, I don't detect any noticable slowdown in the python versions from the Java versions (probably because of the strightforward conversion of python strings to Java Strings). ============================================================================= jpype-1.6.0/examples/jms/testJpypePublisher.py000066400000000000000000000022411501674766700215150ustar00rootroot00000000000000from jpype import * import time NUMMSGS = 10 def pyPublisher(javaNamingFactory="weblogic.jndi.WLInitialContextFactory", javaNamingProvider="t3://158.188.40.21:7001", connectionFactory="weblogic.jms.ConnectionFactory", topicName="defaultTopic"): return messaging.JpypePublisher(javaNamingFactory, javaNamingProvider, connectionFactory, topicName) # Startup Jpype and import the messaging java package startJVM("C:\\program files\\Java\\j2re1.4.2_02\\bin\\client\\jvm.dll", "-Djava.class.path=D:/jIRAD/JpypeJMS/src;D:/jIRAD/JpypeJMS/classes;C:/bea/weblogic81/server/lib/weblogic.jar") messaging = JPackage('messaging') # Get a publisher publisher = pyPublisher() # Timing test # The "Start" message signals the subscriber to start timing message receipts publisher.publish("Start") t0 = time.time() for i in range(NUMMSGS): publisher.publish("Hello World! %s" % i) print("MessageRate =", float(NUMMSGS) / (time.time() - t0)) # The "Stop" message signals the subscriber to stop timing message receipts publisher.publish("Stop") # Close and quit publisher.close() shutdownJVM() jpype-1.6.0/examples/jms/testJpypeSubscriber.py000066400000000000000000000026171501674766700216720ustar00rootroot00000000000000from jpype import * import time def pySubscriber(proxy, javaNamingFactory="weblogic.jndi.WLInitialContextFactory", javaNamingProvider="t3://158.188.40.21:7001", connectionFactory="weblogic.jms.ConnectionFactory", topicName="defaultTopic"): return messaging.JpypeSubscriber(proxy, javaNamingFactory, javaNamingProvider, connectionFactory, topicName) # Startup Jpype and import the messaging java package startJVM("C:\\program files\\Java\\j2re1.4.2_02\\bin\\client\\jvm.dll", "-Djava.class.path=D:/jIRAD/JpypeJMS/src;D:/jIRAD/JpypeJMS/classes;C:/bea/weblogic81/server/lib/weblogic.jar") messaging = JPackage('messaging') # Setup the JProxy for the messaging.JpypeSubscriberCallback interface class pyCallback: startTime = 0 count = 0 def onMessage(self, text): print(text) if text == 'Start': pyCallback.startTime = time.time() pyCallback.count = 0 elif text == 'Stop': print("Message Rate =", float(pyCallback.count) / (time.time() - pyCallback.startTime)) else: pyCallback.count += 1 c = pyCallback() proxy = JProxy(messaging.JpypeSubscriberCallback, inst=c) # Get a subscriber sub = pySubscriber(proxy) print("Listening...") # Prevent this thread from exiting time.sleep(1000) # exit shutdownJVM() jpype-1.6.0/examples/linux/000077500000000000000000000000001501674766700156455ustar00rootroot00000000000000jpype-1.6.0/examples/linux/README.TXT000066400000000000000000000016061501674766700172060ustar00rootroot00000000000000The 2 scripts in this directory are here to resolve a weakness in the linux implementation. Since the JVM is loaded dynamically, the LD_LIBRARY_PATH may not contain the proper directories. Unfortunately, dlopen() will ignore any changes made to the LD_LIBRARY_PATH in the current process. The jpype.sh is meant as an example for people redistributing apps using jpype. Instead of having users launch python script.py directly, have then launch script.sh. The companion findjvm.py can be delivered along your app, but is also present in the python distribution of jpype. Developpers are encouraged to make their lives simpler by adding the correct directories to their LD_LIBRARY_PATH directly. To learn more about the dlopen problem, you can look at : http://forum.java.sun.com/thread.jsp?forum=52&thread=303583&message=1210441 and http://www.nathanr.net/diary/index.php?year=2004&month=05 jpype-1.6.0/examples/linux/findjvm.py000066400000000000000000000015531501674766700176600ustar00rootroot00000000000000# ***************************************************************************** # Copyright 2004-2008 Steve Menard # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # ***************************************************************************** import jpype import os.path jvmlib = jpype.getDefaultJVMPath() print(os.path.dirname(os.path.dirname(jvmlib))) jpype-1.6.0/examples/linux/jpype.sh000066400000000000000000000017201501674766700173300ustar00rootroot00000000000000#!/bin/sh #***************************************************************************** # Copyright 2004-2008 Steve Menard # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #***************************************************************************** # # this script contributed by Dave Kuhlman, and modified By Steve menard # export JAVA_LIB_PATH=`python findjvm.py` export LD_LIBRARY_PATH=LD_LIBRARY_PATH:$JAVA_LIB_PATH:$JAVA_LIB_PATH/client python $*jpype-1.6.0/examples/rmi.py000066400000000000000000000022401501674766700156450ustar00rootroot00000000000000# ***************************************************************************** # Copyright 2004-2008 Steve Menard # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # ***************************************************************************** # the hava classes used are defined the the test harness. the class jpype.rmi.ServerImpl must be started before this script can be run. from jpype import * import os.path root = os.path.abspath(os.path.dirname(__file__)) startJVM(getDefaultJVMPath(), "-ea", "-Djava.class.path=%s/../test/classes" % root) p = java.rmi.Naming.lookup("rmi://localhost:2004/server") print(p, p.__class__) p.callRemote() shutdownJVM() jpype-1.6.0/ivy.xml000066400000000000000000000021641501674766700142240ustar00rootroot00000000000000 jpype-1.6.0/jpype/000077500000000000000000000000001501674766700140175ustar00rootroot00000000000000jpype-1.6.0/jpype/__init__.py000066400000000000000000000053521501674766700161350ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from ._jinit import * from ._jpackage import * from ._jproxy import * from ._core import * from . import _core from ._gui import * from ._classpath import * from ._jclass import * from ._jobject import * # There is a bug in lgtm with __init__ imports. It will be fixed next month. from . import _jarray # lgtm [py/import-own-module] from . import _jexception # lgtm [py/import-own-module] from .types import * from ._jcustomizer import * from . import nio # lgtm [py/import-own-module] from . import types # lgtm [py/import-own-module] from ._jcustomizer import * # Import all the class customizers # Customizers are applied in the order that they are defined currently. from . import _jmethod # lgtm [py/import-own-module] from . import _jcollection # lgtm [py/import-own-module] from . import _jio # lgtm [py/import-own-module] from . import protocol # lgtm [py/import-own-module] from . import _jthread # lgtm [py/import-own-module] __all__ = ['java', 'javax'] __all__.extend(_jinit.__all__) # type: ignore[name-defined] __all__.extend(_core.__all__) __all__.extend(_classpath.__all__) # type: ignore[name-defined] __all__.extend(types.__all__) # type: ignore[name-defined] __all__.extend(_jproxy.__all__) # type: ignore[name-defined] __all__.extend(_jpackage.__all__) # type: ignore[name-defined] __all__.extend(_jclass.__all__) # type: ignore[name-defined] __all__.extend(_jcustomizer.__all__) # type: ignore[name-defined] __all__.extend(_gui.__all__) # type: ignore[name-defined] __version__ = "1.6.0" __version_info__ = __version__.split('.') # FIXME these should be deprecated. The old JPackage system is only for # python2 series and generates lots of deceptive classes. At some point # these two are going to have to go away. java = JPackage("java", strict=True) javax = JPackage("javax", strict=True) JMethod = _jpype._JMethod JField = _jpype._JField if hasattr(_jpype, 'bootstrap'): _jpype.bootstrap() _core.initializeResources() jpype-1.6.0/jpype/_classpath.py000066400000000000000000000061511501674766700165150ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import os as _os import typing import _jpype __all__ = ['addClassPath', 'getClassPath'] _CLASSPATHS = [] _SEP = _os.path.pathsep def addClassPath(path1: typing.Union[str, _os.PathLike]) -> None: """ Add a path to the Java class path Classpath items can be a java, a directory, or a glob pattern. Relative paths are relative to the caller location. Arguments: path(str): """ # We are deferring these imports until here as we only need them # if this function is used. from pathlib import Path import inspect global _CLASSPATHS # Convert to an absolute path. Note that # relative paths will be resolve based on the location # of the caller rather than the JPype directory. path1 = Path(path1) if not path1.is_absolute(): path2 = Path(inspect.stack(1)[1].filename).parent.resolve() path1 = path2.joinpath(path1) # If the JVM is already started then we will have to load the paths # immediately into the JPypeClassLoader if _jpype.isStarted(): Paths = _jpype.JClass('java.nio.file.Paths') JContext = _jpype.JClass('org.jpype.JPypeContext') classLoader = JContext.getInstance().getClassLoader() if path1.name == "*": paths = list(path1.parent.glob("*.jar")) if len(paths) == 0: return for path in paths: classLoader.addPath(Paths.get(str(path))) else: classLoader.addPath(Paths.get(str(path1))) _CLASSPATHS.append(path1) def getClassPath(env: bool = True) -> str: """ Get the full Java class path. Includes user added paths and the environment CLASSPATH. Arguments: env(Optional, bool): If true then environment is included. (default True) """ from pathlib import Path global _CLASSPATHS global _SEP # Merge the environment path classPath = list(_CLASSPATHS) envPath = _os.environ.get("CLASSPATH") if env and envPath: classPath.extend([Path(i) for i in envPath.split(_SEP)]) out = [] for path in classPath: if path == '': continue if path.name == "*": paths = list(path.parent.glob("*.jar")) if len(paths) == 0: continue out.extend(paths) else: out.append(path) return _SEP.join([str(i) for i in out]) jpype-1.6.0/jpype/_core.py000066400000000000000000000532011501674766700154610ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from __future__ import annotations import atexit import os from pathlib import Path import sys import typing import _jpype from . import types as _jtypes from . import _classpath from . import _jcustomizer from . import _jinit from . import _pykeywords from ._jvmfinder import * # This import is required to bootstrap importlib, _jpype uses importlib.util # but on some systems it may not load properly from C. To make sure it gets # loaded properly we are going to force the issue even through we don't # use it in this module. (Do not remove) from importlib import util as _util __all__ = [ 'isJVMStarted', 'startJVM', 'shutdownJVM', 'getDefaultJVMPath', 'getJVMVersion', 'isThreadAttachedToJVM', 'attachThreadToJVM', 'detachThreadFromJVM', 'synchronized', 'JVMNotFoundException', 'JVMNotSupportedException', 'JVMNotRunning' ] class JVMNotRunning(RuntimeError): pass # Activate jedi tab completion try: from jedi import __version__ as _jedi_version import jedi.access as _jedi_access _jedi_access.ALLOWED_DESCRIPTOR_ACCESS += _jpype._JMethod, _jpype._JField except ModuleNotFoundError: pass except AttributeError: import warnings as _w _w.warn(f"provided Jedi seems out of date. Version is {_jedi_version}.") if typing.TYPE_CHECKING: _PathOrStr = typing.Union[str, os.PathLike] # See http://scottlobdell.me/2015/04/decorators-arguments-python/ def deprecated(*args): """ Marks a function a deprecated when used as decorator. Be sure to start python with -Wd to see warnings. """ def func2(*args, **kwargs): import warnings if not func2._warned: warnings.warn(func2._warning % (func2._real.__module__, func2._real.__name__), category=DeprecationWarning, stacklevel=2) func2._warned = True return func2._real(*args, **kwargs) if isinstance(args[0], str): def decorate(func): func2.__name__ = func.__name__ func2.__doc__ = func.__doc__ func2._warned = False func2._real = func func2._warning = "%s.%s is deprecated, use {0} instead".format( args[0]) return func2 return decorate else: func = args[0] func2.__name__ = func.__name__ func2.__doc__ = func.__doc__ func2._warned = False func2._real = func func2._warning = "%s.%s is deprecated" return func2 def isJVMStarted(): """ True if the JVM is currently running.""" # TODO This method is horribly named. It should be named isJVMRunning as # isJVMStarted would seem to imply that the JVM was started at some # point without regard to whether it has been shutdown. # return _jpype.isStarted() def _getOption(args, var, sep=None, keep=False): """ Get an option and remove it from the current jvm arguments list """ for i, v in enumerate(args): if not isinstance(v, str): continue _, _, value = v.partition('%s=' % var) if value: if not keep: del args[i] if sep is not None: return value.split(sep) return value return [] def _expandClassPath( classpath: typing.Union[typing.Sequence[_PathOrStr], _PathOrStr, None] = None ) -> typing.List[str]: """ Return a classpath which represents the given tuple of classpath specifications """ out: list[str] = [] if isinstance(classpath, (str, os.PathLike)): classpath = (classpath,) try: # Convert anything iterable into a tuple. classpath = tuple(classpath) # type: ignore[arg-type] except TypeError: raise TypeError("Unknown class path element") for element in classpath: try: pth = os.fspath(element) except TypeError as err: raise TypeError( "Classpath elements must be strings or Path-like") from err if isinstance(pth, bytes): # In the future we may allow this to support Paths which are undecodable. # https://docs.python.org/3/howto/unicode.html#unicode-filenames. raise TypeError("Classpath elements must be strings or Path-like") if pth.endswith('*'): import glob out.extend(glob.glob(pth + '.jar')) else: out.append(pth) return out _JVM_started = False def interactive(): return bool(getattr(sys, 'ps1', sys.flags.interactive)) def _findTemp(): dirlist = [] # Mirror Python tempfile with a check for ascii for envname in 'TMPDIR', 'TEMP', 'TMP': dirname = os.getenv(envname) if dirname and dirname.isascii(): dirlist.append(dirname) if os.name == 'nt': for dirname in [os.path.expanduser(r'~\AppData\Local\Temp'), os.path.expandvars(r'%SYSTEMROOT%\Temp'), r'c:\temp', r'c:\tmp', r'\temp', r'\tmp']: if dirname and dirname.isascii(): dirlist.append(dirname) else: dirlist.extend(['/tmp', '/var/tmp', '/usr/tmp']) name = str(os.getpid()) for d in dirlist: p = Path("%s/%s" % (d, name)) try: p.touch() p.unlink() except Exception as ex: continue return d raise SystemError("Unable to find non-ansii path") def startJVM( *jvmargs: str, jvmpath: typing.Optional[_PathOrStr] = None, classpath: typing.Union[typing.Sequence[_PathOrStr], _PathOrStr, None] = None, ignoreUnrecognized: bool = False, convertStrings: bool = False, interrupt: bool = not interactive(), ) -> None: """ Starts a Java Virtual Machine. Without options it will start the JVM with the default classpath and jvmpath. The default classpath is determined by ``jpype.getClassPath()``. The default jvmpath is determined by ``jpype.getDefaultJVMPath()``. Parameters: *jvmargs (Optional, str[]): Arguments to give to the JVM. The first argument may be the path to the JVM. Keyword Arguments: jvmpath (str, PathLike): Path to the jvm library file, Typically one of (``libjvm.so``, ``jvm.dll``, ...) Using None will apply the default jvmpath. classpath (str, PathLike, [str, PathLike]): Set the classpath for the JVM. This will override any classpath supplied in the arguments list. A value of None will give no classpath to JVM. ignoreUnrecognized (bool): Option to ignore invalid JVM arguments. Default is False. convertStrings (bool): Option to force Java strings to cast to Python strings. This option is to support legacy code for which conversion of Python strings was the default. This will globally change the behavior of all calls using strings, and a value of True is NOT recommended for newly developed code. interrupt (bool): Option to install ^C signal handlers. If True then ^C will stop the process, else ^C will transfer control to Python rather than halting. If not specified will be False if Python is started as an interactive shell. Raises: OSError: if the JVM cannot be started or is already running. TypeError: if a keyword argument conflicts with the positional arguments. """ # Code for 1.6 # modulepath: typing.Union[typing.Sequence[_PathOrStr], _PathOrStr, None] = None, if _jpype.isStarted(): raise OSError('JVM is already started') global _JVM_started if _JVM_started: raise OSError('JVM cannot be restarted') # Convert to list jvm_args: list[str] = list(jvmargs) # JVM path if jvm_args: # jvm is the first argument the first argument is a path or None if jvm_args[0] is None or (isinstance(jvm_args[0], str) and not jvm_args[0].startswith('-')): if jvmpath: raise TypeError('jvmpath specified twice') jvmpath = jvm_args[0] jvm_args = jvm_args[1:] if not jvmpath: jvmpath = getDefaultJVMPath() else: # Allow the path to be a PathLike. jvmpath = os.fspath(jvmpath) # Handle strings and list of strings. extra_jvm_args: list[str] = [] # Classpath handling old_classpath = _getOption(jvm_args, "-Djava.class.path", _classpath._SEP) if old_classpath: # Old style, specified in the arguments if classpath is not None: # Cannot apply both styles, conflict raise TypeError('classpath specified twice') classpath = old_classpath elif classpath is None: # Not specified at all, use the default classpath. classpath = _classpath.getClassPath() # Code for 1.6 release when we add module support # # Modulepath handling # old_modulepath = _getOption(jvm_args, "--module-path", _classpath._SEP) # if old_modulepath: # # Old style, specified in the arguments # if modulepath is not None: # # Cannot apply both styles, conflict # raise TypeError('modulepath specified twice') # modulepath = old_modulepath # if modulepath is not None: # mp = _classpath._SEP.join(_expandClassPath(modulepath)) # extra_jvm_args += ['--module-path=%s'%mp ] # Get the support library support_lib = os.path.join(os.path.dirname( os.path.dirname(__file__)), "org.jpype.jar") if not os.path.exists(support_lib): raise RuntimeError( "Unable to find org.jpype.jar support library at " + support_lib) system_class_loader = _getOption( jvm_args, "-Djava.system.class.loader", keep=True) java_class_path = _expandClassPath(classpath) java_class_path.append(support_lib) java_class_path = list(filter(len, java_class_path)) classpath = _classpath._SEP.join(java_class_path) tmp = None # Make sure our module is always on the classpath if not classpath.isascii(): if system_class_loader: # https://bugs.openjdk.org/browse/JDK-8079633?jql=text%20~%20%22ParseUtil%22 raise ValueError( "system classloader cannot be specified with non ascii characters in the classpath") # If we are not installed on an ascii path then we will need to copy the jar to a new location if not support_lib.isascii(): import tempfile import shutil fd, path = tempfile.mkstemp(dir=_findTemp()) if not path.isascii(): raise ValueError("Unable to find ascii temp directory.") shutil.copyfile(support_lib, path) support_lib = path tmp = path os.close(fd) # Don't remove # ok, setup the jpype system classloader and add to the path after startup # this guarentees all classes have the same permissions as they did in the past from urllib.parse import quote extra_jvm_args += [ '-Djava.system.class.loader=org.jpype.JPypeClassLoader', '-Djava.class.path=%s' % support_lib, '-Djpype.class.path=%s' % quote(classpath), '-Xshare:off' ] else: # no problems extra_jvm_args += ['-Djava.class.path=%s' % classpath] try: import locale # Gather a list of locale settings that Java may override (excluding LC_ALL) categories = [getattr(locale, i) for i in dir( locale) if i.startswith('LC_') and i != 'LC_ALL'] # Keep the current locale settings, else Java will replace them. prior = [locale.getlocale(i) for i in categories] # Start the JVM _jpype.startup(jvmpath, tuple(jvm_args + extra_jvm_args), ignoreUnrecognized, convertStrings, interrupt, tmp) # Collect required resources for operation initializeResources() # Restore locale for i, j in zip(categories, prior): try: locale.setlocale(i, j) except locale.Error: pass except RuntimeError as ex: source = str(ex) if "UnsupportedClassVersion" in source: import re match = re.search(r"([0-9]+)\.[0-9]+", source) if match: version = int(match.group(1)) - 44 raise RuntimeError( f"{jvmpath} is older than required Java version{version}") from ex raise def initializeResources(): global _JVM_started _jpype._java_lang_Class = None _jpype._java_lang_Object = _jpype.JClass("java.lang.Object") _jpype._java_lang_Throwable = _jpype.JClass("java.lang.Throwable") _jpype._java_lang_Exception = _jpype.JClass("java.lang.Exception") _jpype._java_lang_Class = _jpype.JClass("java.lang.Class") _jpype._java_lang_String = _jpype.JClass("java.lang.String") _jpype._java_lang_RuntimeException = _jpype.JClass( "java.lang.RuntimeException") # Preload needed classes _jpype._java_lang_Boolean = _jpype.JClass("java.lang.Boolean") _jpype._java_lang_Byte = _jpype.JClass("java.lang.Byte") _jpype._java_lang_Character = _jpype.JClass("java.lang.Character") _jpype._java_lang_Short = _jpype.JClass("java.lang.Short") _jpype._java_lang_Integer = _jpype.JClass("java.lang.Integer") _jpype._java_lang_Long = _jpype.JClass("java.lang.Long") _jpype._java_lang_Float = _jpype.JClass("java.lang.Float") _jpype._java_lang_Double = _jpype.JClass("java.lang.Double") # Bind types _jpype.JString.class_ = _jpype._java_lang_String _jpype.JObject.class_ = _jpype._java_lang_Object _jtypes.JBoolean.class_ = _jpype._java_lang_Boolean.TYPE _jtypes.JByte.class_ = _jpype._java_lang_Byte.TYPE _jtypes.JChar.class_ = _jpype._java_lang_Character.TYPE _jtypes.JShort.class_ = _jpype._java_lang_Short.TYPE _jtypes.JInt.class_ = _jpype._java_lang_Integer.TYPE _jtypes.JLong.class_ = _jpype._java_lang_Long.TYPE _jtypes.JFloat.class_ = _jpype._java_lang_Float.TYPE _jtypes.JDouble.class_ = _jpype._java_lang_Double.TYPE _jtypes.JBoolean._hints = _jcustomizer.getClassHints("boolean") _jtypes.JByte._hints = _jcustomizer.getClassHints("byte") _jtypes.JChar._hints = _jcustomizer.getClassHints("char") _jtypes.JShort._hints = _jcustomizer.getClassHints("short") _jtypes.JInt._hints = _jcustomizer.getClassHints("int") _jtypes.JLong._hints = _jcustomizer.getClassHints("long") _jtypes.JFloat._hints = _jcustomizer.getClassHints("float") _jtypes.JDouble._hints = _jcustomizer.getClassHints("double") # Table for automatic conversion to objects "JObject(value, type)" _jpype._object_classes = {} # These need to be deprecated so that we can support casting Python objects _jpype._object_classes[bool] = _jpype._java_lang_Boolean _jpype._object_classes[int] = _jpype._java_lang_Long _jpype._object_classes[float] = _jpype._java_lang_Double _jpype._object_classes[str] = _jpype._java_lang_String _jpype._object_classes[type] = _jpype._java_lang_Class _jpype._object_classes[object] = _jpype._java_lang_Object _jpype._object_classes[_jpype._JClass] = _jpype._java_lang_Class _jpype._object_classes[_jtypes.JBoolean] = _jpype._java_lang_Boolean _jpype._object_classes[_jtypes.JByte] = _jpype._java_lang_Byte _jpype._object_classes[_jtypes.JChar] = _jpype._java_lang_Character _jpype._object_classes[_jtypes.JShort] = _jpype._java_lang_Short _jpype._object_classes[_jtypes.JInt] = _jpype._java_lang_Integer _jpype._object_classes[_jtypes.JLong] = _jpype._java_lang_Long _jpype._object_classes[_jtypes.JFloat] = _jpype._java_lang_Float _jpype._object_classes[_jtypes.JDouble] = _jpype._java_lang_Double _jpype._object_classes[type(None)] = _jpype._java_lang_Object _jpype._object_classes[_jpype.JString] = _jpype._java_lang_String # Set up table of automatic conversions of Python primitives # this table supports "JArray(type)" _jpype._type_classes = {} _jpype._type_classes[bool] = _jtypes.JBoolean _jpype._type_classes[int] = _jtypes.JLong _jpype._type_classes[float] = _jtypes.JDouble _jpype._type_classes[str] = _jpype._java_lang_String _jpype._type_classes[type] = _jpype._java_lang_Class _jpype._type_classes[object] = _jpype._java_lang_Object _jpype._type_classes[_jpype.JString] = _jpype._java_lang_String _jpype._type_classes[_jpype.JObject] = _jpype._java_lang_Object _jpype._type_classes[_jpype.JClass] = _jpype._java_lang_Class _jinit.runJVMInitializers() _jpype.JClass('org.jpype.JPypeKeywords').setKeywords( list(_pykeywords._KEYWORDS)) _jpype.JPypeContext = _jpype.JClass('org.jpype.JPypeContext').getInstance() _jpype.JPypeClassLoader = _jpype.JPypeContext.getClassLoader() # Everything succeeded so started is now true. _JVM_started = True def shutdownJVM(): """ Shuts down the JVM. This method shuts down the JVM and disables access to existing Java objects. Due to limitations in the JPype, it is not possible to restart the JVM after being terminated. """ import threading import jpype.config if threading.current_thread() is not threading.main_thread(): raise RuntimeError("Shutdown must be called from main thread") if _jpype.isStarted(): _jpype.JPypeContext.freeResources = jpype.config.free_resources _jpype.shutdown(jpype.config.destroy_jvm, False) # In order to shutdown cleanly we need the reference queue stopped # otherwise, we can experience a crash if a Java thread is waiting # for the GIL. def _JTerminate(): try: import jpype.config # We are exiting anyway so no need to free resources if jpype.config.onexit: _jpype.shutdown(jpype.config.destroy_jvm, False) except RuntimeError: pass atexit.register(_JTerminate) @deprecated("java.lang.Thread.isAttached") def isThreadAttachedToJVM(): """ Checks if a thread is attached to the JVM. Python automatically attaches threads when a Java method is called. This creates a resource in Java for the Python thread. This method can be used to check if a Python thread is currently attached so that it can be disconnected prior to thread termination to prevent leaks. Returns: True if the thread is attached to the JVM, False if the thread is not attached or the JVM is not running. """ return _jpype.isThreadAttachedToJVM() @deprecated("java.lang.Thread.attach") def attachThreadToJVM(): """ Attaches a thread to the JVM. The function manually connects a thread to the JVM to allow access to Java objects and methods. JPype automatically attaches when a Java resource is used, so a call to this is usually not needed. Raises: RuntimeError: If the JVM is not running. """ _jpype.attachThreadToJVM() @deprecated("java.lang.Thread.detach") def detachThreadFromJVM(): """ Detaches a thread from the JVM. This function detaches the thread and frees the associated resource in the JVM. For codes making heavy use of threading this should be used to prevent resource leaks. The thread can be reattached, so there is no harm in detaching early or more than once. This method cannot fail and there is no harm in calling it when the JVM is not running. """ _jpype.detachThreadFromJVM() def synchronized(obj): """ Creates a resource lock for a Java object. Produces a monitor object. During the lifespan of the monitor Java will not be able to acquire a thread lock on the object. This will prevent multiple threads from modifying a shared resource. This should always be used as part of a Python ``with`` startment. Arguments: obj: A valid Java object shared by multiple threads. Example: .. code-block:: python with synchronized(obj): # modify obj values # lock is freed when with block ends """ return _jpype._JMonitor(obj) def getJVMVersion(): """ Get the JVM version if the JVM is started. This function can be used to determine the version of the JVM. It is useful to help determine why a Jar has failed to load. Returns: A typle with the (major, minor, revison) of the JVM if running. """ if not _jpype.isStarted(): return (0, 0, 0) import re runtime = _jpype.JClass('java.lang.Runtime') version = runtime.class_.getPackage().getImplementationVersion() # Java 9+ has a version method if not version: version = runtime.version() version = (re.match("([0-9.]+)", str(version)).group(1)) return tuple([int(i) for i in version.split('.')]) @_jcustomizer.JImplementationFor("java.lang.Runtime") class _JRuntime(object): # We need to redirect hooks so that we control the order def addShutdownHook(self, thread): return _jpype.JClass("org.jpype.JPypeContext").getInstance().addShutdownHook(thread) def removeShutdownHook(self, thread): return _jpype.JClass("org.jpype.JPypeContext").getInstance().removeShutdownHook(thread) _jpype.JVMNotRunning = JVMNotRunning jpype-1.6.0/jpype/_gui.py000066400000000000000000000026641501674766700153240ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys as _sys from . import _jproxy from . import _jclass __all__ = ['setupGuiEnvironment', 'shutdownGuiEnvironment'] # FIXME this is not documented # FIXME this is darwin specific def setupGuiEnvironment(cb): if _sys.platform == 'darwin': from PyObjCTools import AppHelper # type: ignore m = {'run': cb} proxy = _jproxy.JProxy('java.lang.Runnable', m) cbthread = _jclass.JClass("java.lang.Thread")(proxy) cbthread.start() AppHelper.runConsoleEventLoop() else: cb() def shutdownGuiEnvironment(): if _sys.platform == 'darwin': from PyObjCTools import AppHelper # type: ignore AppHelper.stopEventLoop() jpype-1.6.0/jpype/_jarray.py000066400000000000000000000151521501674766700160240ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from . import _jcustomizer from collections.abc import Sequence __all__ = ['JArray'] class JArray(_jpype._JObject, internal=True): # type: ignore[call-arg] """ Creates a Java array class for a Java type of a given dimension. This serves as a base type and factory for all Java array classes. The resulting Java array class can be used to construct a new array with a given size or specified members. JPype arrays support Python operators for iterating, length, equals, not equals, subscripting, and slicing. They also support Java object methods, clone, and length property. Java arrays may not be resized, and as such elements cannot be added nor deleted. Currently, applying the slice operator produces a new Python sequence. Example: .. code-block:: python # Define a new array class for ``int[]`` IntArrayCls = JArray(JInt) # Create an array holding 10 elements # equivalent to Java ``int[] x=new int[10]`` x = IntArrayCls(10) # Create a length 3 array initialized with [1,2,3] # equivalent to Java ``int[] x = new int[]{1,2,3};`` x = IntArrayCls([1,2,3]) # Operate on an array print(len(x)) print(x[0]) print(x[:-2]) x[1:]=(5,6) if isinstance(x, JArray): print("object is a java array") if issubclass(IntArrayCls, JArray): print("class is a java array type.") Args: javaClass (str,type): Is the type of element to hold in the array. ndims (Optional,int): the number of dimensions of the array (default=1) Returns: A new Python class that representing a Java array class. Raises: TypeError: if the component class is invalid or could not be found. Note: javaClass can be specified in three ways: - as a string with the name of a java class. - as a Java primitive type such as ``jpype.JInt``. - as a Java class type such as ``java.lang.String``. """ def __new__(cls, tp, dims=1): if cls != JArray: raise TypeError("Arrays factory can't be used as type") jc = _toJavaClass(tp) return _jpype._newArrayType(jc, dims) @classmethod def of(cls, array, dtype=None): return _jpype.arrayFromBuffer(array, dtype) def __class_getitem__(cls, key): if key is _jpype.JClass: # explicit check for JClass # _toJavaClass cannot be used # passing int, float, str, etc is not allowed key = _jpype._java_lang_Class if isinstance(key, _jpype._java_lang_Class): key = _jpype.JClass(key) if isinstance(key, _jpype.JClass): return type(key[0]) raise TypeError("%s is not a Java class" % key) class _JArrayProto(object): def __str__(self): return str(list(self)) def __iter__(self): return _JavaArrayIter(self) def __reversed__(self): for elem in self[::-1]: yield elem def clone(self): """ Clone the Java array. Create a "shallow" copy of a Java array. For a single dimensional array of primitives, the cloned array is complete independent copy of the original. For objects or multidimensional arrays, the new array is a copy which points to the same members as the original. To obtain a deep copy of a Java array, use Java serialize and deserialize operations to duplicate the entire array and contents. In order to deep copy, the objects must be Serializable. Returns: A shallow copy of the array. """ return _jpype.JClass("java.util.Arrays").copyOf(self, len(self)) def _toJavaClass(tp): """(internal) Converts a class type in python into a internal java class. Used mainly to support JArray. The type argument will operate on: - (str) lookup by class name or fail if not found. - (JClass) just returns the java type. - (type) uses a lookup table to find the class. """ # if it a string than we lookup the class by name. if isinstance(tp, str): return _jpype._java_lang_Class.forName(tp) # if is a java.lang.Class instance, then no coversion required if isinstance(tp, _jpype._JClass): return tp # Okay then it must be a type try: return _jpype._type_classes[tp].class_ except KeyError: pass # See if it a class type try: return tp.class_ except AttributeError: pass raise TypeError("Unable to find class for '%s'" % tp.__name__) # FIXME are these not sequences? They act like sequences but are they # connected to collections.abc.Sequence # has: __len__, __iter__, __getitem__ # missing: __contains__ (required for in) # Cannot be Mutable because java arrays are fixed in length class _JavaArrayIter(object): def __init__(self, a): self._array = a self._ndx = -1 def __iter__(self): return self def __next__(self): self._ndx += 1 if self._ndx >= len(self._array): raise StopIteration return self._array[self._ndx] next = __next__ # ********************************************************** # Char array customizer @_jcustomizer.JImplementationFor("byte[]") @_jcustomizer.JImplementationFor("char[]") class _JCharArray(object): def __str__(self): return str(_jpype.JString(self)) def __eq__(self, other): if isinstance(other, str): return str(self) == other try: return self.equals(self.__class__(other)) except TypeError: return False __hash__ = _jpype._JObject.__hash__ # Install module hooks _jcustomizer._applyCustomizerPost(_jpype._JArray, _JArrayProto) _jpype.JArray = JArray Sequence.register(_jpype._JArray) jpype-1.6.0/jpype/_jarray.pyi000066400000000000000000000030611501674766700161710ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import typing __all__ = ['JArray'] T = typing.TypeVar('T') U = typing.TypeVar('U') class JArray(typing.Generic[T]): def __new__(cls, tp: typing.Type[T], dims=1) -> JArray[T]: ... @classmethod def of(cls, array, dtype: typing.Optional[typing.Type[U]] = None) -> JArray[U]: ... def __len__(self) -> int: ... def __iter__(self) -> typing.Iterator[T]: ... def __reversed__(self) -> typing.Iterator[T]: ... @typing.overload def __getitem__(self, key: int) -> T: ... @typing.overload def __getitem__(self, key: slice) -> JArray[T]: ... @typing.overload def __setitem__(self, index: int, value: T): ... @typing.overload def __setitem__(self, index: slice, value: typing.Sequence[T]): ... jpype-1.6.0/jpype/_jclass.py000066400000000000000000000202251501674766700160100ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from ._pykeywords import pysafe from . import _jcustomizer __all__ = ['JClass', 'JInterface', 'JOverride'] def JOverride(*args, **kwargs): """Annotation to denote a method as overriding a Java method. This annotation applies to customizers, proxies, and extensions to Java classes. Apply it to methods to mark them as implementing or overriding Java methods. Keyword arguments are passed to the corresponding implementation factory. Args: sticky=bool: Applies a customizer method to all derived classes. """ # Check if called bare if len(args) == 1 and callable(args[0]): object.__setattr__(args[0], "__joverride__", {}) return args[0] # Otherwise apply arguments as needed def modifier(method): object.__setattr__(method, "__joverride__", kwargs) return method return modifier class JClassMeta(type): def __instancecheck__(self, other): return type(other) == _jpype._JClass class JClass(_jpype._JClass, metaclass=JClassMeta): """Meta class for all Java class instances. When called as an object, JClass will contruct a new Java class wrapper. All Python wrappers for Java classes derive from this type. To test if a Python class is a Java wrapper use ``isinstance(obj, jpype.JClass)``. Args: className (str): name of a Java type. Keyword Args: loader (java.lang.ClassLoader): specifies a class loader to use when creating a class. initialize (bool): If true the class will be loaded and initialized. Otherwise, static members will be uninitialized. Returns: JavaClass: a new wrapper for a Java class Raises: TypeError: if the component class is invalid or could not be found. """ def __new__(cls, jc, loader=None, initialize=True): if loader and isinstance(jc, str): jc = _jpype._java_lang_Class.forName(jc, initialize, loader) # Handle generics if isinstance(jc, str) and jc.endswith(">"): i = jc.find("<") params = jc[i + 1:-1] ret = _jpype._getClass(jc[:i]) acceptParams = len(ret.class_.getTypeParameters()) if acceptParams == 0: raise TypeError( "Java class '%s' does not take parameters" % (ret.__name__)) if len(params) > 0: params = params.split(',') if len(params) > 0 and len(params) != len(ret.class_.getTypeParameters()): raise TypeError( "Java generic class '%s' length mismatch" % (ret.__name__)) return ret # Pass to class factory to create the type return _jpype._getClass(jc) def __class_getitem__(cls, index): # enables JClass[1] to get a Class[] return JClass("java.lang.Class")[index] class JInterface(_jpype._JObject, internal=True): # type: ignore[call-arg] """A meta class for all Java Interfaces. ``JInterface`` is serves as the base class for any Java class that is a pure interface without implementation. It is not possible to create a instance of a Java interface. Example: .. code-block:: python if isinstance(java.util.function.Function, jpype.JInterface): print("is interface") """ pass def _jclassPre(name, bases, members): # Correct keyword conflicts with Python m = list(members.items()) for k, v in m: k2 = pysafe(k) if k2 != k: del members[k] # Remove unmappable functions if k2: members[k2] = v # Apply customizers hints = _jcustomizer.getClassHints(name) hints.applyCustomizers(name, bases, members) return (name, tuple(bases), members) def _jclassPost(res, *args): # Post customizers hints = _jcustomizer.getClassHints(res.__name__) res._hints = hints hints.applyInitializer(res) # Attach public inner classes we find # Due to bootstrapping, we must wait until java.lang.Class is defined # before we can access the class structures. if _jpype._java_lang_Class: for cls in res.class_.getDeclaredClasses(): if cls.getModifiers() & 1 == 0: continue wrapper = _jpype.JClass(cls) res._customize(str(cls.getSimpleName()), wrapper) def _jclassDoc(cls): """Generator for JClass.__doc__ property Parameters: cls (JClass): class to document. Returns: The doc string for the class. """ out = [] if not hasattr(cls, "__javadoc__"): jde = JClass("org.jpype.javadoc.JavadocExtractor")() jd = jde.getDocumentation(cls) if jd is not None: setattr(cls, "__javadoc__", jd) if jd.description is not None: out.append(str(jd.description)) if jd.ctors is not None: out.append(str(jd.ctors)) return "".join(out) setattr(cls, "__javadoc__", None) from textwrap import TextWrapper jclass = cls.class_ out.append("Java class '%s'" % (jclass.getName())) out.append("") sup = jclass.getSuperclass() if sup: out.append(" Extends:") out.append(" %s" % sup.getName()) out.append("") intfs = jclass.getInterfaces() if intfs: out.append(" Interfaces:") words = ", ".join([str(i.getCanonicalName()) for i in intfs]) wrapper = TextWrapper(initial_indent=' ' * 8, subsequent_indent=' ' * 8) out.extend(wrapper.wrap(words)) out.append("") ctors = jclass.getDeclaredConstructors() if ctors: exceptions = [] name = jclass.getSimpleName() ctordecl = [] for ctor in ctors: modifiers = ctor.getModifiers() if not modifiers & 1: continue params = ", ".join([str(i.getCanonicalName()) for i in ctor.getParameterTypes()]) ctordecl.append(" * %s(%s)" % (name, params)) exceptions.extend(ctor.getExceptionTypes()) if ctordecl: out.append(" Constructors:") out.extend(ctordecl) out.append("") if exceptions: out.append(" Raises:") for exc in set(exceptions): out.append(" %s: from java" % exc.getCanonicalName()) out.append("") fields = jclass.getDeclaredFields() if fields: fielddesc = [] for field in fields: modifiers = field.getModifiers() if not modifiers & 1: continue fieldInfo = [] if modifiers & 16: fieldInfo.append("final") if modifiers & 8: fieldInfo.append("static") if field.isEnumConstant(): fieldInfo.append("enum constant") else: fieldInfo.append("field") fielddesc.append(" %s (%s): %s" % (field.getName(), field.getType().getName(), " ".join(fieldInfo))) if fielddesc: out.append(" Attributes:") out.extend(fielddesc) out.append("") return "\n".join(out) # Install module hooks _jpype.JClass = JClass _jpype.JInterface = JInterface _jpype._jclassDoc = _jclassDoc _jpype._jclassPre = _jclassPre _jpype._jclassPost = _jclassPost jpype-1.6.0/jpype/_jcollection.py000066400000000000000000000165221501674766700170430ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from . import _jclass from . import types as _jtypes from . import _jcustomizer from collections.abc import Mapping, Sequence, MutableSequence JOverride = _jclass.JOverride @_jcustomizer.JImplementationFor("java.lang.Iterable") class _JIterable(object): """ Customizer for ``java.util.Iterable`` This customizer adds the Python iterator syntax to classes that implement Java Iterable. """ def __iter__(self): return self.iterator() @_jcustomizer.JImplementationFor("java.util.Collection") class _JCollection(object): """ Customizer for ``java.util.Collection`` This customizer adds the Python functions ``len()`` and ``del`` to Java Collisions to allow for Python syntax. """ def __len__(self): return self.size() def __delitem__(self, i): raise TypeError( "'%s' does not support item deletion, use remove() method" % type(self).__name__) def __contains__(self, i): try: return self.contains(i) except TypeError: return False def _sliceAdjust(slc, size): start = slc.start stop = slc.stop if slc.step and (slc.step > 1 or slc.step < 0): raise TypeError("Stride not supported") if start is None: start = 0 if stop is None: stop = size if start < 0: start += size if stop < 0: stop += size return slice(start, stop) @_jcustomizer.JImplementationFor('java.util.List') class _JList(object): """ Customizer for ``java.util.List`` This customizer adds the Python list operator to function on classes that implement the Java List interface. """ def __jclass_init__(self): Sequence.register(self) MutableSequence.register(self) def __getitem__(self, ndx): if isinstance(ndx, slice): ndx = _sliceAdjust(ndx, self.size()) return self.subList(ndx.start, ndx.stop) else: if ndx < 0: ndx += self.size() return self.get(ndx) def __setitem__(self, ndx, v): if isinstance(ndx, slice): ndx = _sliceAdjust(ndx, self.size()) self[ndx.start:ndx.stop].clear() self.addAll(ndx.start, v) else: if ndx < 0: ndx += self.size() self.set(ndx, v) def __delitem__(self, ndx): if isinstance(ndx, slice): ndx = _sliceAdjust(ndx, self.size()) self[ndx.start:ndx.stop].clear() elif hasattr(ndx, '__index__'): if ndx < 0: ndx += self.size() return self.remove_(_jtypes.JInt(ndx)) else: raise TypeError("Incorrect arguments to del") def __reversed__(self): iterator = self.listIterator(self.size()) while iterator.hasPrevious(): yield iterator.previous() def index(self, obj): try: return self.indexOf(obj) except TypeError: raise ValueError("%s is not in list" % repr(obj)) def count(self, obj): try: jo = _jpype.JObject(obj) c = 0 for i in self: if i.equals(jo): c += 1 return c except TypeError: return 0 def insert(self, idx, obj): if idx < 0: idx += self.size() return self.add(idx, obj) def append(self, obj): return self.add(obj) def reverse(self): _jpype.JClass("java.util.Collections").reverse(self) def extend(self, lst): self.addAll(lst) def pop(self, idx=-1): if idx < 0: idx += self.size() return self.remove_(_jtypes.JInt(idx)) def __iadd__(self, obj): self.add(obj) return self def __add__(self, obj): new = _jpype.JClass("java.util.ArrayList")(self) new.extend(obj) return new def __mul__(self, obj): new = _jpype.JClass("java.util.ArrayList")() for i in range(obj): new.extend(self) return new @JOverride(sticky=True, rename='remove_') def remove(self, obj): try: rc = self.remove_(_jpype.JObject(obj, _jpype.JObject)) if rc is True: return except TypeError: pass raise ValueError("item not in list") @_jcustomizer.JImplementationFor('java.util.Map') class _JMap(object): """ Customizer for ``java.util.Map`` This customizer adds the Python list and len operators to classes that implement the Java Map interface. """ def __jclass_init__(self): Mapping.register(self) def __len__(self): return self.size() def __iter__(self): return self.keySet().iterator() def __delitem__(self, i): return self.remove(i) def __getitem__(self, ndx): try: item = self.get(ndx) if item is not None or self.containsKey(ndx): return item except TypeError: pass raise KeyError('%s' % ndx) def __setitem__(self, ndx, v): self.put(ndx, v) def items(self): return self.entrySet() def keys(self): return list(self.keySet()) def __contains__(self, item): try: return self.containsKey(item) except TypeError: return False @_jcustomizer.JImplementationFor('java.util.Set') class _JSet(object): def __delitem__(self, i): return self.remove(i) @_jcustomizer.JImplementationFor("java.util.Map.Entry") class _JMapEntry(object): def __len__(self): return 2 def __getitem__(self, x): if x == 0: return self.getKey() if x == 1: return self.getValue() raise IndexError("Pairs are always length 2") @_jcustomizer.JImplementationFor('java.util.Iterator') class _JIterator(object): """ Customizer for ``java.util.Iterator`` This customizer adds the Python iterator concept to classes that implement the Java Iterator interface. """ def __next__(self): if self.hasNext(): return self.next() raise StopIteration def __iter__(self): return self @_jcustomizer.JImplementationFor('java.util.Enumeration') class _JEnumeration(object): """ Customizer for ``java.util.Enumerator`` This customizer adds the Python iterator concept to classes that implement the Java Enumerator interface. """ def __next__(self): if self.hasMoreElements(): return self.nextElement() raise StopIteration def __iter__(self): return self next = __next__ jpype-1.6.0/jpype/_jcollection.pyi000066400000000000000000000015301501674766700172050ustar00rootroot00000000000000from typing import Any, Iterable, Collection, List, Set, Mapping, Tuple, TypeVar, Iterator, Generator, Union, overload, Dict E = TypeVar('E') K = TypeVar('K') V = TypeVar('V') class _JIterable(Iterable[E]): def __iter__(self) -> Iterator[E]: ... class _JCollection(Collection[E]): def __len__(self) -> int: ... def __delitem__(self, i: E) -> None: ... def __contains__(self, i: Any) -> bool: ... def __iter__(self) -> Iterator[E]: ... class _JList(List[E]): pass class _JMap(Dict[K, V]): def __len__(self) -> int: ... def __iter__(self) -> Iterator[K]: ... def __getitem__(self, ndx: K) -> V: ... class _JSet(Set[E]): pass class _JMapEntry(Tuple[K, V]): pass class _JIterator(Iterator[E]): def __next__(self) -> E: ... class _JEnumeration(Iterator[E]): def __next__(self) -> E: ... jpype-1.6.0/jpype/_jcustomizer.py000066400000000000000000000241251501674766700171120ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype __all__ = ['JImplementationFor', 'JConversion'] # Member types that are copied from the prototype _jcopymembers = (str, property, staticmethod, classmethod) def JConversion(cls, exact=None, instanceof=None, attribute=None, excludes=None): """ Decorator to define a method as a converted a Java type. Whenever a method resolution is called the JPype internal rules are applied, but this may be insufficient. If only a single method requires modification then a class customizer can be applied. But if many interfaces require the same conversion than a user conversion may be a better option. To add a user conversion define a method which take the requested Java type as the first argument, the target object to be converted as the second argument and returns a Java object or Java proxy that matches the required type. If the type is not a Java type then a TypeError will be raised. This method is only evaluated after the match has been determine prior to calling. Care should be used when defining a user conversion. If example if one has an interface that requires a specific class and you want it to take a Python string, then a user conversion can do that. On the other hand, if you define a generic converter of any Python object to a Java string, then every interface will attempt to call the conversion method whenever a Java string is being matched, which can cause many methods to potentially become ambiguous. Conversion are not inherited. If the same converter needs to apply to multiple types, then multiple decorators can be applied to the same method. Args: cls(str, JClass): The class that will be produced by this conversion. exact(type): This conversion applies only to objects that have a type exactly equal to the argument. instanceof(type or protocol): This conversion applies to any object that passes isinstance(obj, type). attribute(str): This conversion applies to any object that has passes hasattr(obj, arg). (deprecated) excludes(type): Prevents a conversion for a specified type. Can be used to prevent a specific type from being converted. For example, to prevent maps or strings from passing a check for Sequence. Exclusions are applied before all other user specificied conversions. """ hints = getClassHints(cls) if excludes is not None: hints._excludeConversion(excludes) def customizer(func=None): if exact is not None: hints._addTypeConversion(exact, func, True) if instanceof is not None: hints._addTypeConversion(instanceof, func, False) if attribute is not None: hints._addAttributeConversion(attribute, func) return func return customizer def JImplementationFor(clsname, base=False): """ Decorator to define an implementation for a class. Applies to a class which will serve as a prototype as for the Java class wrapper. If it is registered as a base class, then the class must derive from JObject. Otherwise, the methods are copied from the prototype to the Java class wrapper. The method ``__jclass_init__(cls)`` will be called with the constructed class as the argument. This call is used to set methods for all classes that derive from the specified class. Use ``jclass._customize()`` to alter the class methods. Using the prototype class as a base class is used mainly to support classes which must be derived from a Python type by design. Use of a base class will produce a RuntimeError if the class has already been created. For non-base class customizers, the customizer will be applied retroactively if the class is already created. Conflicts are resolved by the last customizer applied. Args: clsname (str): name of java class. base (bool, optional): if True this will be a base class. Default is False. """ if not isinstance(clsname, str): raise TypeError("ImplementationFor requires a java classname string") def customizer(cls): hints = getClassHints(clsname) if base: hints.registerClassBase(cls) else: hints.registerClassImplementation(clsname, cls) return cls return customizer def _applyStickyMethods(cls, sticky): for method in sticky: attr = getattr(method, '__joverride__') rename = attr.get('rename', None) name = method.__name__ if rename: orig = type.__getattribute__(cls, name) cls._customize(rename, orig) cls._customize(name, method) def _applyCustomizerImpl(members, proto, sticky, setter): """ (internal) Apply a customizer to a class. This "borrows" methods from a prototype class. Current behavior is: - Copy any string or property. - Copy any callable applying @JOverride if applicable with conflict renaming. - Copy __new__ method. """ for p, v in proto.__dict__.items(): if callable(v) or isinstance(v, _jcopymembers): # Apply JOverride annotation attr = getattr(v, '__joverride__', None) if attr is not None: if attr.get('sticky', False): sticky.append(v) continue # Apply rename rename = attr.get('rename', "_" + p) if p in members and isinstance(members[p], (_jpype._JField, _jpype._JMethod)): setter(rename, members[p]) setter(p, v) def _applyAll(cls, method): applied = set() todo = [cls] while todo: c = todo.pop(0) if c in applied: continue todo.extend(c.__subclasses__()) applied.add(c) method(c) def _applyCustomizerPost(cls, proto): """ (internal) Customize a class after it has been created """ sticky = [] _applyCustomizerImpl(cls.__dict__, proto, sticky, lambda p, v: cls._customize(p, v)) # Merge sticky into existing __jclass_init__ if len(sticky) > 0: method = proto.__dict__.get('__jclass_init__', None) def init(cls): if method: method(cls) _applyStickyMethods(cls, sticky) cls._customize('__jclass_init__', init) # Apply a customizer to all derived classes if '__jclass_init__' in proto.__dict__: method = proto.__dict__['__jclass_init__'] _applyAll(cls, method) class JClassHints(_jpype._JClassHints): """ ClassHints holds class customizers and conversions. These items can be defined before the JVM is created. """ def __init__(self): self.bases = [] self.implementations = [] self.instantiated = False def registerClassBase(self, base): """ (internal) Add an implementation for a class Use @JImplementationFor(cls, base=True) to access this. """ self.bases.append(base) # Changing the base class in python can break things, # so we will tag this as an error for now. if self.instantiated: raise TypeError( "Base classes must be added before class is created") def registerClassImplementation(self, classname, proto): """ (internal) Add an implementation for a class Use @JImplementationFor(cls) to access this. """ self.implementations.append(proto) # If we have already created a class, apply it retroactively. if self.instantiated: _applyCustomizerPost(_jpype.JClass(classname), proto) def applyCustomizers(self, name, bases, members): """ (internal) Called by JClass and JArray to customize a newly created class.""" # Apply base classes for b in self.bases: bases.insert(0, b) module = name.rsplit('.', 1) if len(module) == 2: members['__module__'] = module[0] # Apply implementations sticky = [] for proto in self.implementations: _applyCustomizerImpl(members, proto, sticky, lambda p, v: members.__setitem__(p, v)) if len(sticky) > 0: method = members.get('__jclass_init__', None) def init(cls): if method is not None: method(cls) _applyStickyMethods(cls, sticky) members['__jclass_init__'] = init def applyInitializer(self, cls): """ (internal) Called after the class is created to apply any customizations required by inherited parents. """ self.instantiated = True if hasattr(cls, '__jclass_init__'): init = [] for base in cls.__mro__: if '__jclass_init__' in base.__dict__: init.insert(0, base.__dict__['__jclass_init__']) for func in init: func(cls) def getClassHints(name): if isinstance(name, _jpype._JClass): name = name.__name__ hints = _jpype._hints.get(name, None) if not hints: hints = JClassHints() _jpype._hints[name] = hints return hints _jpype._hints = {} getClassHints("java.lang.IndexOutOfBoundsException").registerClassBase( IndexError) getClassHints("java.lang.NullPointerException").registerClassBase( ValueError) jpype-1.6.0/jpype/_jexception.py000066400000000000000000000037141501674766700167050ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from . import _jcustomizer __all__ = ['JException'] @_jcustomizer.JImplementationFor("java.lang.Throwable", base=True) class JException(_jpype._JException, internal=True): # type: ignore[call-arg] """ Base class for all ``java.lang.Throwable`` objects. Use ``issubclass(cls, JException)`` to test if a class is derived from ``java.lang.Throwable.`` Use ``isinstance(obj, JException)`` to test if an object is a ``java.lang.Throwable``. """ # Included for compatibility with JPype 0.6.3 def message(self): return str(self.getMessage()) # Included for compatibility with JPype 0.6.3 def stacktrace(self): """ Get a string listing the stack frame. Returns: A string with the classic Java ``printStackTrace`` result. """ StringWriter = _jpype.JClass("java.io.StringWriter") PrintWriter = _jpype.JClass("java.io.PrintWriter") sw = StringWriter() pw = PrintWriter(sw) self.printStackTrace(pw) pw.flush() r = sw.toString() sw.close() return r @property def args(self): return self._args # Hook up module resources _jpype.JException = JException jpype-1.6.0/jpype/_jinit.py000066400000000000000000000035171501674766700156530ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype __all__ = ['onJVMStart'] JInitializers = [] def onJVMStart(func): """Decorator to register a function to be called after JVM is started. This can be used to load module resources that depend on the JVM such as loading classes. If the JVM is not started, the user supplied function is held in a list until the JVM starts. When startJVM is called, all functions on the deferred list are called and the list is cleared. If the JVM is already started, then the function is called immediately. Errors from the function will either be raised immediately if the JVM is started, or from startJVM if the JVM is not yet started. Args: func (callable): a function to call when the JVM is started. """ registerJVMInitializer(func) return func def registerJVMInitializer(func): if not _jpype.isStarted(): JInitializers.append(func) else: # JVM is already started so we are safe to execute immediately. func() def runJVMInitializers(): for func in JInitializers: func() JInitializers.clear() jpype-1.6.0/jpype/_jio.py000066400000000000000000000034651501674766700153210ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from . import _jcustomizer import sys as _sys from . import _jexception # This contains a customizer for closeable so that we can use the python "with" # statement. @_jcustomizer.JImplementationFor("java.lang.AutoCloseable") class _JCloseable(object): """ Customizer for ``java.lang.AutoCloseable`` and ``java.io.Closeable`` This customizer adds support of the ``with`` operator to all Java classes that implement the Java ``AutoCloseable`` interface. Example: .. code-block:: python from java.nio.files import Files, Paths with Files.newInputStream(Paths.get("foo")) as fd: # operate on the input stream # Input stream closes at the end of the block. """ def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback): info = _sys.exc_info() try: self.close() except _jexception.JException as jex: # Eat the second exception if we are already handling one. if (info[0] is None): raise jex jpype-1.6.0/jpype/_jio.pyi000066400000000000000000000005471501674766700154700ustar00rootroot00000000000000from types import TracebackType from typing import ContextManager, Optional, Type class _JCloseable(ContextManager['_JCloseable']): def __enter__(self) -> '_JCloseable': ... def __exit__(self, exception_type: Optional[Type[BaseException]], exception_value: Optional[BaseException], traceback: Optional[TracebackType]) -> bool: ... jpype-1.6.0/jpype/_jmethod.py000066400000000000000000000104031501674766700161600ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from . import _jclass __all__ = [] # type: ignore[var-annotated] def _jmethodGetDoc(method, cls, overloads): """Generator for _JMethod.__doc__ property Parameters: method (_JMethod): method to generate doc string for. cls (java.lang.Class): Class holding this method dispatch. overloads (java.lang.reflect.Method[]): tuple holding all the methods that are served by this method dispatch. Returns: The doc string for the method dispatch. """ jcls = _jpype.JClass(cls) if not hasattr(jcls, "__javadoc__"): jcls.__doc__ jd = getattr(jcls, "__javadoc__") if jd is not None: md = jd.methods.get(method.__name__) if md is not None: return str(md) from textwrap import TextWrapper out = [] out.append("Java method dispatch '%s' for '%s'" % (method.__name__, cls.getName())) out.append("") exceptions = [] returns = [] methods = [] classmethods = [] for ov in overloads: modifiers = ov.getModifiers() exceptions.extend(ov.getExceptionTypes()) returnName = ov.getReturnType().getCanonicalName() params = ", ".join([str(i.getCanonicalName()) for i in ov.getParameterTypes()]) if returnName != "void": returns.append(returnName) if modifiers & 8: classmethods.append(" * %s %s(%s)" % (returnName, ov.getName(), params)) else: methods.append(" * %s %s(%s)" % (returnName, ov.getName(), params)) if classmethods: out.append(" Static Methods:") out.extend(classmethods) out.append("") if methods: out.append(" Virtual Methods:") out.extend(methods) out.append("") if exceptions: out.append(" Raises:") for exc in set(exceptions): out.append(" %s: from java" % exc.getCanonicalName()) out.append("") if returns: out.append(" Returns:") words = ", ".join([str(i) for i in set(returns)]) wrapper = TextWrapper(initial_indent=' ', subsequent_indent=' ') out.extend(wrapper.wrap(words)) out.append("") return "\n".join(out) def _jmethodGetAnnotation(method, cls, overloads): """Generator for ``_JMethod.__annotation__`` property Parameters: method (_JMethod): method to generate annotations for. cls (java.lang.Class): Class holding this method dispatch. overloads (java.lang.reflect.Method[]): tuple holding all the methods that are served by this method dispatch. Returns: The dict to use for type annotations. """ returns = [] # Special handling if we have 1 overload if len(overloads) == 1: ov = overloads[0] out = {} for i, p in enumerate(ov.getParameterTypes()): out['arg%d' % i] = _jclass.JClass(p) out['return'] = _jclass.JClass(ov.getReturnType()) return out # Otherwise, we only get the return for ov in overloads: returns.append(ov.getReturnType()) returns = set(returns) if len(returns) == 1: return {"return": _jclass.JClass([i for i in returns][0])} return {} def _jmethodGetCode(method): def call(*args): return method.__call__(*args) return call _jpype.getMethodDoc = _jmethodGetDoc _jpype.getMethodAnnotations = _jmethodGetAnnotation _jpype.getMethodCode = _jmethodGetCode jpype-1.6.0/jpype/_jobject.py000066400000000000000000000074321501674766700161560ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype __all__ = ['JObject'] class JObject(_jpype._JObject, internal=True): # type: ignore[call-arg] """ Base class for all object instances. It can be used to test if an object is a Java object instance with ``isinstance(obj, JObject)``. Calling ``JObject`` as a function can be used to covert or cast to specific Java type. It will box primitive types and supports an option type to box to. This wrapper functions in three ways. - If the no type is given the object is automatically cast to type best matched given the value. This can be used to create a boxed primitive. ``JObject(JInt(i))`` - If the type is a primitve, the object will be the boxed type of that primitive. ``JObject(1, JInt)`` - If the type is a Java class and the value is a Java object, the object will be cast to the Java class and will be an exact match to the class for the purposes of matching arguments. If the object is not compatible, an exception will be raised. Args: value: The value to be cast into an Java object. type(Optional, type): The type to cast into. Raises: TypeError: If the object cannot be cast to the specified type, or the requested type is not a Java class or primitive. """ def __new__(cls, *args, **kwargs): if len(args) == 0: return _jpype._java_lang_Object() return _JObjectFactory(*args, **kwargs) def _getDefaultJavaObject(obj): """ Determine the type of the object based the type of a value. Python primitives - lookup the type in the table Java primitive - lookup boxed type in the table Java objects - just use their type directly """ tp = type(obj) # handle Python types and Java primitives try: return _jpype._object_classes[tp] except KeyError: pass # handle Class wrappers if isinstance(tp, _jpype._JClass): return tp # handle JProxy instances try: return obj.__javaclass__ except AttributeError: pass raise TypeError( "Unable to determine the default type of `{0}`".format(tp.__name__)) def _JObjectFactory(v=None, tp=None): """ Creates a Java object. If not specified type is determined based on the object. If type type is specified then then it tried to box it. """ if tp is None: # Automatically determine based on the value tp = _getDefaultJavaObject(v) elif isinstance(tp, str): tp = _jpype.JClass(tp) if tp in _jpype._object_classes: if not isinstance(tp, _jpype.JClass): import warnings warnings.warn("Using JObject with a Python type is deprecated.", category=DeprecationWarning, stacklevel=3) tp = _jpype._object_classes[tp] # Given a Java class if isinstance(tp, _jpype._JClass): return tp._cast(v) raise TypeError("Invalid type conversion to %s requested." % tp) # Hook up module resources _jpype.JObject = JObject jpype-1.6.0/jpype/_jpackage.py000066400000000000000000000044051501674766700163000ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype __all__ = ['JPackage'] class _JPackageMeta(type): def __instancecheck__(self, other): return isinstance(other, _jpype._JPackage) def __subclasscheck__(self, other): return issubclass(other, _jpype._JPackage) class JPackage(_jpype._JPackage, metaclass=_JPackageMeta): """ Gateway for automatic importation of Java classes. This class allows structured access to Java packages and classes. This functionality has been replaced by ``jpype.imports``, but is still useful in some cases. Only the root of the package tree needs to be declared with the ``JPackage`` constructor. Sub-packages will be created on demand. For example, to import the w3c DOM package: .. code-block:: python Document = JPackage('org').w3c.dom.Document Under some situations such as a missing jar file, the resulting object will be a JPackage object rather than the expected java class. This results in rather challanging debugging messages. Due to this restriction, the ``jpype.imports`` module is preferred. To prevent these types of errors, a package can be declares as ``strict`` which prevents expanding package names that do not comply with Java package name conventions. Args: path (str): Path into the Java class tree. Example: .. code-block:: python # Alias into a library google = JPackage("com").google # Access members in the library result = google.common.IntMath.pow(x,m) """ pass jpype-1.6.0/jpype/_jproxy.py000066400000000000000000000211001501674766700160550ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype __all__ = ["JProxy", "JImplements"] # FIXME the java.lang.method we are overriding should be passes to the lookup function # so we can properly handle name mangling on the override. def _checkInterfaceOverrides(interfaces, overrides): # Verify all methods are overriden for interface in interfaces: for method in interface.class_.getMethods(): if method.getModifiers() & 1024 == 0: continue if not str(method.getName()) in overrides: raise NotImplementedError("Interface '%s' requires method '%s' to be implemented." % ( interface.class_.getName(), method.getName())) def _classOverrides(cls): # Find all class defined overrides overrides = {} for k, v in cls.__dict__.items(): try: attr = object.__getattribute__(v, "__joverride__") overrides[k] = (v, attr) except AttributeError: pass return overrides def _prepareInterfaces(cls, intf): # Convert the interfaces list actualIntf = _convertInterfaces(intf) overrides = _classOverrides(cls) _checkInterfaceOverrides(actualIntf, overrides) return actualIntf def _createJProxyDeferred(cls, *intf): """ (internal) Create a proxy from a Python class with @JOverride notation on methods evaluated at first instantiation. """ if not isinstance(cls, type): raise TypeError( "JImplements only applies to types, not %s" % (type(cls))) def new(tp, *args, **kwargs): # Attach a __jpype_interfaces__ attribute to this class if # one doesn't already exist. actualIntf = getattr(tp, "__jpype_interfaces__", None) if actualIntf is None: actualIntf = _prepareInterfaces(cls, intf) tp.__jpype_interfaces__ = actualIntf return _jpype._JProxy.__new__(tp, None, None, actualIntf) members = {'__new__': new} # Return the augmented class return type("proxy.%s" % cls.__name__, (cls, _jpype._JProxy), members) def _createJProxy(cls, *intf): """ (internal) Create a proxy from a Python class with @JOverride notation on methods evaluated at declaration. """ if not isinstance(cls, type): raise TypeError( "JImplements only applies to types, not %s" % (type(cls))) actualIntf = _prepareInterfaces(cls, intf) def new(tp, *args, **kwargs): self = _jpype._JProxy.__new__(tp, None, None, actualIntf) tp.__init__(self, *args, **kwargs) return self members = {'__new__': new} # Return the augmented class return type("proxy.%s" % cls.__name__, (cls, _jpype._JProxy), members) def JImplements(*interfaces, deferred=False, **kwargs): """ Annotation for creating a new proxy that implements one or more Java interfaces. This annotation is placed on an ordinary Python class. The annotation requires a list of interfaces. It must implement all of the java methods for each of the interfaces. Each implemented method should have a @JOverride annotation. The JVM must be running in order to validate the class. Args: interfaces (str*,JClass*): Strings or JClasses for each Java interface this proxy is to implement. Kwargs: deferred (bool): Whether to defer validation of the interfaces and overrides until the first instance instantiation (True) or validate at declaration (False). Deferred validation allows a proxy class to be declared prior to starting the JVM. Validation only occurs once per proxy class, thus there is no performance penalty. Default False. Example: .. code-block:: python @JImplement("java.lang.Runnable") class MyImpl(object): @JOverride def run(self, arg): pass @JImplement("org.my.Interface1", "org.my.Interface2") class MyImpl(object): @JOverride def method(self, arg): pass """ if deferred: def JProxyCreator(cls): return _createJProxyDeferred(cls, *interfaces, **kwargs) else: def JProxyCreator(cls): return _createJProxy(cls, *interfaces, **kwargs) return JProxyCreator def _convertInterfaces(intf): """ (internal) Convert a list of interface names into a list of interfaces suitable for a proxy. """ # Flatten the list intflist = [] for item in intf: if isinstance(item, _jpype.JClass): intflist.append(item) elif isinstance(item, str) or not hasattr(item, '__iter__'): intflist.append(item) else: intflist.extend(item) # Look up the classes if given as a string actualIntf = set() for item in intflist: if isinstance(item, str): actualIntf.add(_jpype.JClass(item)) else: actualIntf.add(item) # Check that all are interfaces if not actualIntf: raise TypeError("At least one Java interface must be specified") for cls in actualIntf: # If it isn't a JClass, then it cannot be a Java interface if not isinstance(cls, _jpype.JClass): raise TypeError("'%s' is not a Java interface" % type(cls).__name__) # Java concrete and abstract classes cannot be proxied if not issubclass(cls, _jpype.JInterface): raise TypeError("'%s' is not a Java interface" % cls.__name__) return tuple(actualIntf) class _JFromDict(object): def __init__(self, dict): self.dict = dict def __getattribute__(self, name): try: return object.__getattribute__(self, 'dict')[name] except KeyError: pass raise AttributeError("attribute not found") class JProxy(_jpype._JProxy): """ Define a proxy for a Java interface. This is an older style JPype proxy interface that uses either a dictionary or an object instance to implement methods defined in Java. The Python object can be held by Java and its lifespan will continue as long as java holds a reference to the object instance. New code should use ``@JImplements`` annotation as it will support improved type safety and error handling. Name lookups can either made using a dictionary or an object instance. One of these two options must be specified. Args: intf: either a single interface or a list of java interfaces. The interfaces can either be defined by strings or JClass instance. Only interfaces may be used in a proxy, dict (dict[string, callable], optional): specifies a dictionary containing the methods to be called when executing the Java interface methods. inst (object, optional): specifies an object with methods whose names matches the Java interfaces methods. convert (bool, optional): if True the proxy is unwrapped to a Python object. """ def __new__(cls, intf, dict=None, inst=None, convert=False): # Convert the interfaces actualIntf = _convertInterfaces([intf]) # Create an interface by dictionary. If instance is given # it will be passed as self. Its presence in Python when # returned will be given by convert. if dict is not None: return _jpype._JProxy(inst, _JFromDict(dict), actualIntf, convert) # (obsolete) Use a Python object with the same methods as the interface. # This form as mostly be replaced by @JImplements form. if inst is not None: return _jpype._JProxy.__new__(cls, inst, inst, actualIntf, convert) raise TypeError("a dict or inst must be specified") @staticmethod def unwrap(obj): if not isinstance(obj, _jpype._JProxy): return obj return obj.__javainst__ jpype-1.6.0/jpype/_jstring.py000066400000000000000000000045421501674766700162150ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import typing import _jpype from . import _jcustomizer __all__ = ['JString'] class JString(_jpype._JObject, internal=True): # type: ignore[call-arg] """ Base class for ``java.lang.String`` objects When called as a function, this class will produce a ``java.lang.String`` object. It can be used to test if an object is a Java string using ``isinstance(obj, JString)``. """ def __new__(cls, *args, **kwargs): if cls != JString: raise TypeError("JString factory cannot be used as base class") cls = _jpype.JClass("java.lang.String") return cls(*args) @_jcustomizer.JImplementationFor("java.lang.String") class _JStringProto: def __add__(self, other: str) -> str: return self.concat(other) # type: ignore[attr-defined] def __len__(self) -> int: return self.length() # type: ignore[attr-defined] def __getitem__(self, i: typing.Union[slice, int]): if isinstance(i, slice): return str(self)[i] if i < 0: i += len(self) if i < 0: raise IndexError("Array index is negative") if i >= len(self): raise IndexError("Array index exceeds length") return self.charAt(i) # type: ignore[attr-defined] def __contains__(self, other: str) -> bool: return self.contains(other) # type: ignore[attr-defined] def __hash__(self): if self == None: # lgtm [py/test-equals-none] return hash(None) return self.__str__().__hash__() def __repr__(self): return "'%s'" % self.__str__() _jpype.JString = JString jpype-1.6.0/jpype/_jthread.py000066400000000000000000000061371501674766700161600ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype from . import _jcustomizer @_jcustomizer.JImplementationFor('java.lang.Thread') class _JThread: """ Customizer for ``java.land.Thread`` This adds addition JPype methods to java.lang.Thread to support Python. """ @staticmethod def isAttached() -> bool: """ Checks if a thread is attached to the JVM. Python automatically attaches as daemon threads when a Java method is called. This creates a resource in Java for the Python thread. This method can be used to check if a Python thread is currently attached so that it can be disconnected prior to thread termination to prevent leaks. Returns: True if the thread is attached to the JVM, False if the thread is not attached or the JVM is not running. """ return _jpype.isThreadAttachedToJVM() @staticmethod def attach() -> None: """ Attaches the current thread to the JVM as a user thread. User threads that are attached to the JVM will prevent the JVM from shutting down until the thread is terminated or detached. To convert a daemon thread to a main thread, the thread must first be detached. Raises: RuntimeError: If the JVM is not running. """ return _jpype.attachThreadToJVM() @staticmethod def attachAsDaemon() -> None: """ Attaches the current thread to the JVM as a daemon. Daemon threads act as background tasks and do not prevent the JVM from shutdown normally. JPype automatically attaches any threads that call Java resources as daemon threads. To convert a daemon thread to a user thread, the thread must first be detached. Raises: RuntimeError: If the JVM is not running. """ return _jpype.attachThreadAsDaemon() @staticmethod def detach() -> None: """ Detaches a thread from the JVM. This function detaches the thread and frees the associated resource in the JVM. For codes making heavy use of threading this should be used to prevent resource leaks. The thread can be reattached, so there is no harm in detaching early or more than once. This method cannot fail and there is no harm in calling it when the JVM is not running. """ return _jpype.detachThreadFromJVM() jpype-1.6.0/jpype/_jvmfinder.py000066400000000000000000000306261501674766700165230ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** # Copyright 2013 Thomas Calmant import os import sys __all__ = ['getDefaultJVMPath', 'get_default_jvm_path', 'JVMNotFoundException', 'JVMNotSupportedException'] from typing import Sequence, Tuple class JVMNotFoundException(ValueError): """Exception raised when no JVM was found in the search path. This exception is raised when the all of the places searched did not contain a JVM. The locations searched depend on the machine architecture. To avoid this exception specify the JAVA_HOME environment variable as a valid jre or jdk root directory. """ class JVMNotSupportedException(ValueError): """Exception raised when the JVM is not supported. This exception is raised after a search found a valid Java home directory was found, but the JVM shared library found is not supported. Typically this occurs when the JVM does not match the architecture of Python 32 vs 64 bit, or the JVM is older than the version used to compile JPype. """ def getDefaultJVMPath() -> str: """Retrieves the path to the default or first found JVM library. Returns: The path to the JVM shared library file Raises: JVMNotFoundException: If there was no JVM found in the search path. JVMNotSupportedException: If the JVM was found was not compatible with Python due to cpu architecture. """ if sys.platform == "win32": finder = WindowsJVMFinder() elif sys.platform == "darwin": finder = DarwinJVMFinder() else: finder = LinuxJVMFinder() return finder.get_jvm_path() get_default_jvm_path = getDefaultJVMPath class JVMFinder: """JVM library finder base class.""" # Library file name _libfile: str = "libjvm.so" # Predefined locations _locations: Tuple[str, ...] = ("/usr/lib/jvm", "/usr/java") def __init__(self): # Search methods self._methods = (self._get_from_java_home, self._get_from_known_locations) def find_libjvm(self, java_home): """Recursively looks for the given file. Parameters: java_home(str): A Java home folder Returns: The first found file path, or None """ non_supported_jvm = ('cacao', 'jamvm') found_non_supported_jvm = False # Look for the file for root, _, names in sorted(os.walk(java_home), key=lambda t: len(t[0].split(os.sep))): if self._libfile in names: # Found it, but check for non supported jvms candidate = os.path.split(root)[1] if candidate in non_supported_jvm: found_non_supported_jvm = True continue # maybe we will find another one? return os.path.join(root, self._libfile) if found_non_supported_jvm: raise JVMNotSupportedException("Sorry '{0}' is known to be " "broken. Please ensure your " "JAVA_HOME contains at least " "another JVM implementation " "(eg. server)" .format(candidate)) # File not found raise JVMNotFoundException("Sorry no JVM could be found. " "Please ensure your JAVA_HOME " "environment variable is pointing " "to correct installation.") @staticmethod def find_possible_homes(parents): """ Generator that looks for the first-level children folders that could be Java installations, according to their name Parameters: parents (str[]): A list of parent directories Returns: A list of the possible JVM installation folders """ homes = [] java_names = ('jre', 'jdk', 'java') for parent in parents: # Fast exit if folder does not exist if not os.path.exists(parent): continue for childname in sorted(os.listdir(parent)): # Compute the real path path = os.path.realpath(os.path.join(parent, childname)) if path in homes or not os.path.isdir(path): # Already known path, or not a directory -> ignore continue # Check if the path seems OK real_name = os.path.basename(path).lower() for java_name in java_names: if java_name in real_name: # Correct JVM folder name homes.append(path) yield path break def check(self, jvm): """ Check if the jvm is valid for this architecture. This method should be overriden for each architecture. Raises: JVMNotSupportedException: If the jvm is not supported. """ pass def get_jvm_path(self): """ Retrieves the path to the default or first found JVM library Returns: The path to the JVM shared library file Raises: ValueError: No JVM library found or No Support JVM found """ jvm_notsupport_ext = None for method in self._methods: try: jvm = method() # If found check the architecture if jvm: self.check(jvm) except NotImplementedError: # Ignore missing implementations pass except JVMNotFoundException: # Ignore not successful methods pass except JVMNotSupportedException as e: jvm_notsupport_ext = e else: if jvm is not None: return jvm if jvm_notsupport_ext is not None: raise jvm_notsupport_ext raise JVMNotFoundException("No JVM shared library file ({0}) " "found. Try setting up the JAVA_HOME " "environment variable properly." .format(self._libfile)) def _get_from_java_home(self): """ Retrieves the Java library path according to the JAVA_HOME environment variable Returns: The path to the JVM library, or None """ # Get the environment variable java_home = os.getenv("JAVA_HOME") if java_home and os.path.exists(java_home): # Get the real installation path java_home = os.path.realpath(java_home) if not os.path.exists(java_home): java_home = os.getenv("JAVA_HOME") # Look for the library file return self.find_libjvm(java_home) def _get_from_known_locations(self): """ Retrieves the first existing Java library path in the predefined known locations Returns: The path to the JVM library, or None """ for home in self.find_possible_homes(self._locations): jvm = self.find_libjvm(home) if jvm is not None: return jvm class LinuxJVMFinder(JVMFinder): """Linux JVM library finder class.""" # Java bin file _java = "/usr/bin/java" # Library file name _libfile = "libjvm.so" # Predefined locations _locations = ("/usr/lib/jvm", "/usr/java", "/opt/sun") def __init__(self): super().__init__() # Search methods self._methods = (self._get_from_java_home, self._get_from_bin, self._get_from_known_locations) def _get_from_bin(self): """ Retrieves the Java library path according to the real installation of the java executable :return: The path to the JVM library, or None """ # Find the real interpreter installation path java_bin = os.path.realpath(self._java) if os.path.exists(java_bin): # Get to the home directory java_home = os.path.abspath(os.path.join(os.path.dirname(java_bin), '..')) # Look for the JVM library return self.find_libjvm(java_home) class DarwinJVMFinder(LinuxJVMFinder): """ Mac OS X JVM library finder class """ # Library file name _libfile = "libjli.dylib" # Predefined locations _locations = ('/Library/Java/JavaVirtualMachines',) # type: ignore def __init__(self): """ Sets up members """ super().__init__() self._methods = list(self._methods) self._methods.append(self._javahome_binary) def _javahome_binary(self): """ for osx > 10.5 we have the nice util /usr/libexec/java_home available. Invoke it and return its output. It seems this tool has been removed in osx 10.9. """ import platform import subprocess from packaging.version import Version current = Version(platform.mac_ver()[0][:4]) # TODO: check if the java_home tool is still available and fix the version boundaries. if Version('10.6') <= current: # < Version('10.9'): return subprocess.check_output( ['/usr/libexec/java_home']).strip() def _checkJVMArch(jvmPath, maxsize=sys.maxsize): import struct IMAGE_FILE_MACHINE_I386 = 332 IMAGE_FILE_MACHINE_IA64 = 512 IMAGE_FILE_MACHINE_AMD64 = 34404 is64 = maxsize > 2**32 with open(jvmPath, "rb") as f: s = f.read(2) if s != b"MZ": raise JVMNotSupportedException("JVM not valid") f.seek(60) s = f.read(4) header_offset = struct.unpack(" typing.Optional[str]: """ Given an identifier name in Java, return an equivalent identifier name in Python that is guaranteed to not collide with the Python grammar. """ if s.startswith("__") and s.endswith("__") and len(s) >= 4: # Dunder methods should not be considered safe. # (see system defined names in the Python documentation # https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers # ) return None if s in _KEYWORDS: return s + "_" return s jpype-1.6.0/jpype/beans.py000066400000000000000000000071441501674766700154670ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** """ JPype Beans Module ------------------ This customizer finds all occurences of methods with get or set and converts them into Python properties. This behavior is sometimes useful in programming with JPype with interactive shells, but also leads to a lot of confusion. Is this class exposing a variable or is this a property added JPype. As an unnecessary behavior that violates both the Python principle *"There should be one-- and preferably only one --obvious way to do it."* and the C++ principle *"You only pay for what you use"*. This misfeature was removed from the distribution as a default. However, given that it is useful to have methods appear as properties, it was moved to a an optional module. To use beans as properties: .. code-block:: python import jpype.beans The beans property modification is a global behavior and applies retroactively to all classes currently loaded. Once started it can not be undone. """ import _jpype from . import _jcustomizer from ._pykeywords import pysafe as _pysafe def _extract_accessor_pairs(members): """Extract pairs of corresponding property access methods (getter and setter) from a Java class's members (attributes). If a public method with a property's name exists no pair for that property will be extracted. Returns a dictionary with the property name as key and a tuple of (getter method, setter method) as value. A tuple element value might be `None` if only a getter or only a setter exists. """ accessor_pairs = {} for name, member in members.items(): if not isinstance(member, _jpype._JMethod) or len(name) <= 3: continue if name == "getClass": continue if member._isBeanAccessor(): property_name = name[3].lower() + name[4:] try: pair = accessor_pairs[property_name] pair[0] = member except KeyError: accessor_pairs[property_name] = [member, None] elif member._isBeanMutator(): property_name = name[3].lower() + name[4:] try: pair = accessor_pairs[property_name] pair[1] = member except KeyError: accessor_pairs[property_name] = [None, member] return accessor_pairs @_jcustomizer.JImplementationFor("java.lang.Object") class _BeansCustomizer(object): """ Add properties for get/set Bean patterns found in classes. """ def __jclass_init__(self): accessor_pairs = _extract_accessor_pairs(self.__dict__) for attr_name, (getter, setter) in accessor_pairs.items(): attr_name = _pysafe(attr_name) # Don't mess with an existing member if attr_name is None or attr_name in self.__dict__: continue # Add the property self._customize(attr_name, property(getter, setter)) jpype-1.6.0/jpype/config.py000066400000000000000000000031401501674766700156340ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** # This files holds hints used to control behavior of the JVM # It is global variables rather than options to a function so that # they can be set a module at any time rather than requiring arguments # for a function. # # These options are to be treated as hints which may or may not be supported # in future versions. Setting an unsupported option shall have no effect. # Variables shall not be removed even if deprecated so that conditions # based on these will be valid in all future versions. # onexit = True """If this is False, the JVM not will be notified of Python exit. Java will not execute any resource cleanup routines.""" destroy_jvm = True """ If this is False, the JVM will not execute any cleanup routines and memory will not be freed.""" free_resources = True """ If this is False, the resources will be allowed to leak after the shutdown call. """ jpype-1.6.0/jpype/dbapi2.py000066400000000000000000001537161501674766700155470ustar00rootroot00000000000000import _jpype from . import _jinit from . import _jcustomizer from . import types as _jtypes import typing import _jpype import time import threading import datetime # TODO # - Callable procedures # - Isolation levels # - Default adaptors # - A complete testbench # - Testbench with more than one DB # - Documentation # This a generic implementation of PEP-249 __all__ = ['ARRAY', 'ASCII_STREAM', 'BIGINT', 'BINARY', 'BINARY_STREAM', 'BIT', 'BLOB', 'BOOLEAN', 'Binary', 'CHAR', 'CHARACTER_STREAM', 'CLOB', 'Connection', 'Cursor', 'DATE', 'DATETIME', 'DECIMAL', 'DOUBLE', 'DataError', 'DatabaseError', 'Date', 'DateFromTicks', 'Error', 'FLOAT', 'GETTERS_BY_NAME', 'GETTERS_BY_TYPE', 'INTEGER', 'IntegrityError', 'InterfaceError', 'InternalError', 'JDBCType', 'LONGNVARCHAR', 'LONGVARBINARY', 'LONGVARCHAR', 'NCHAR', 'NCHARACTER_STREAM', 'NCLOB', 'NULL', 'NUMBER', 'NUMERIC', 'NVARCHAR', 'NotSupportedError', 'OBJECT', 'OTHER', 'OperationalError', 'ProgrammingError', 'REAL', 'REF', 'RESULTSET', 'ROWID', 'SETTERS_BY_META', 'SETTERS_BY_TYPE', 'SMALLINT', 'SQLXML', 'STRING', 'TEXT', 'TIME', 'TIMESTAMP', 'TIMESTAMP_WITH_TIMEZONE', 'TIME_WITH_TIMEZONE', 'TINYINT', 'Time', 'TimeFromTicks', 'Timestamp', 'TimestampFromTicks', 'URL', 'VARBINARY', 'VARCHAR', 'Warning', 'apilevel', 'connect', 'paramstyle', 'threadsafety'] apilevel = "2.0" threadsafety = 2 paramstyle = 'qmark' _SQLException = None _SQLTimeoutException = None _registry = {} _types = [] def _nop(x): return x ############################################################################### # Types class JDBCType(object): def __init__(self, name, code=None, getter=None, setter=None): """ (internal) Create a new JDBC type. """ if isinstance(name, (str, type(None))): self._name = name self._values = [name] else: self._name = name[0] self._values = name self._code = code self._getter = getter self._setter = setter if code is not None: _registry[code] = self if name is not None: _registry[self._name.upper()] = self _types.append(self) if _jpype.isStarted(): java = _jpype._JPackage("java") self._initialize(java.sql.CallableStatement, java.sql.PreparedStatement, java.sql.ResultSet) def _initialize(self, cs, ps, rs): """ Called after the JVM starts initialize Java resources """ if self._getter is None: self._getter = "getObject" self._rsget = getattr(rs, self._getter) self._csget = getattr(cs, self._getter, None) if self._setter is None: self._setter = "setObject" self._psset = getattr(ps, self._setter) def get(self, rs, column, st): """ A method to retrieve a specific JDBC type. To use a getter add the fetch method to the JDBC type matching the column type to be pulled. For example, to set the getter for FLOAT to use the OBJECT getter, use ``cx.getter[FLOAT] = OBJECT.get``. Not all getters are available on all database drivers. Consult the database driver documentation for details. """ try: if st: return self._csget(rs, column) return self._rsget(rs, column) except _SQLException as ex: raise InterfaceError("Unable to get '%s' using '%s'" % (self._name, self._getter)) from ex def set(self, ps, column, value): """ A method used to set a parameter to a query. To use a setter place the set method in the setter dict corresponding. For example, if the database supports Blob types, the default handler for BLOB can be changed from OBJECT to BLOB with ``cx.setter[BLOB] = BLOB.set``. Not all setters are available on all database drivers. Consult the database driver documentation for details. """ # Set the column with the specialized method try: if self._psset._matches(ps, column, value): return self._psset(ps, column, value) # Otherwise, try to set with the generic method return ps.setObject(column, value) except TypeError as ex: raise InterfaceError("Unable to convert '%s' into '%s'" % ( type(value).__name__, self._name)) from ex except OverflowError as ex: raise InterfaceError("Unable to convert '%s' into '%s' calling '%s'" % ( type(value).__name__, self._name, self._setter)) from ex def __repr__(self): if self._name is None: return "" return self._name def __eq__(self, other): return other in self._values def __hash__(self): return hash(self._name) class _JDBCTypePrimitive(JDBCType): def get(self, rs, column, st): try: if st: rc = self._csget(rs, column) else: rc = self._rsget(rs, column) if rc == 0 and rs.wasNull(): return None return rc except _SQLException as ex: raise InterfaceError("Unable to get '%s' using '%s'" % (self._name, self._getter)) from ex # From https://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/jdbc/getstart/mapping.doc.html # DATALINK = JDBCType('DATALINK',70) # DISTINCT= JDBCType('DISTINCT',2001) # REF_CURSOR = JDBCType('REF_CURSOR',2012) # STRUCT = JDBCType('STRUCT',2002) ARRAY = JDBCType('ARRAY', 2003, 'getArray', 'setArray') BIGINT = _JDBCTypePrimitive('BIGINT', -5, 'getLong', 'setLong') BIT = JDBCType('BIT', -7, 'getBoolean', 'setBoolean') BLOB = JDBCType('BLOB', 2004, 'getBlob', 'setBlob') BOOLEAN = _JDBCTypePrimitive('BOOLEAN', 16, 'getBoolean', 'setBoolean') CHAR = JDBCType('CHAR', 1, 'getString', 'setString') CLOB = JDBCType('CLOB', 2005, 'getClob', 'setClob') DATE = JDBCType('DATE', 91, 'getDate', 'setDate') DOUBLE = _JDBCTypePrimitive('DOUBLE', 8, 'getDouble', 'setDouble') INTEGER = _JDBCTypePrimitive('INTEGER', 4, 'getInt', 'setInt') OBJECT = JDBCType('OBJECT', 2000) LONGNVARCHAR = JDBCType('LONGNVARCHAR', -16, 'getString', 'setString') LONGVARBINARY = JDBCType('LONGVARBINARY', -4, 'getBytes', 'setBytes') LONGVARCHAR = JDBCType('LONGVARCHAR', -1, 'getString', 'setString') NCHAR = JDBCType('NCHAR', -15, 'getString', 'setString') NCLOB = JDBCType('NCLOB', 2011, 'getNClob', 'setNClob') NULL = JDBCType('NULL', 0) NUMERIC = JDBCType('NUMERIC', 2, 'getBigDecimal', 'setBigDecimal') NVARCHAR = JDBCType('NVARCHAR', -9, 'getClob', 'setClob') OTHER = JDBCType('OTHER', 1111) REAL = _JDBCTypePrimitive('REAL', 7, 'getFloat', 'setFloat') REF = JDBCType('REF', 2006, 'getRef', 'setRef') ROWID = JDBCType('ROWID', -8, 'getRowId', 'setRowId') RESULTSET = JDBCType('RESULTSET', -10, 'getObject', 'setObject') SMALLINT = _JDBCTypePrimitive('SMALLINT', 5, 'getShort', 'setShort') SQLXML = JDBCType('SQLXML', 2009, 'getSQLXML', 'setSQLXML') TIME = JDBCType('TIME', 92, 'getTime', 'setTime') TIME_WITH_TIMEZONE = JDBCType('TIME_WITH_TIMEZONE', 2013, 'getTime', 'setTime') TIMESTAMP = JDBCType('TIMESTAMP', 93, 'getTimestamp', 'setTimestamp') TIMESTAMP_WITH_TIMEZONE = JDBCType( 'TIMESTAMP_WITH_TIMEZONE', 2014, 'getTimestamp', 'setTimestamp') TINYINT = _JDBCTypePrimitive('TINYINT', -6, 'getShort', 'setShort') VARBINARY = JDBCType('VARBINARY', -3, 'getBytes', 'setBytes') VARCHAR = JDBCType('VARCHAR', 12, 'getString', 'setString') # Aliases required by DBAPI2 STRING = JDBCType(['STRING', 'CHAR', 'NCHAR', 'NVARCHAR', 'VARCHAR', 'OTHER'], None, 'getString', 'setString') TEXT = JDBCType(['TEXT', 'CLOB', 'LONGVARCHAR', 'LONGNVARCHAR', 'NCLOB', 'SQLXML'], None, 'getString', 'setString') BINARY = JDBCType(['BINARY', 'BLOB', 'LONGVARBINARY', 'VARBINARY'], -2, 'getBytes', 'setBytes') NUMBER = JDBCType(['NUMBER', 'BOOLEAN', 'BIGINT', 'BIT', 'INTEGER', 'SMALLINT', 'TINYINT'], None, 'getObject', 'setObject') FLOAT = _JDBCTypePrimitive(['FLOAT', 'REAL', 'DOUBLE'], 6, 'getDouble', 'setDouble') DECIMAL = JDBCType(['DECIMAL', 'NUMERIC'], 3, 'getBigDecimal', 'setBigDecimal') DATETIME = TIMESTAMP # Special types ASCII_STREAM = JDBCType(None, None, 'getAsciiStream', 'setAsciiStream') BINARY_STREAM = JDBCType(None, None, 'getBinaryStream', 'setBinaryStream') CHARACTER_STREAM = JDBCType( None, None, 'getCharacterStream', 'setCharacterStream') NCHARACTER_STREAM = JDBCType( None, None, 'getNCharacterStream', 'setNCharacterStream') URL = JDBCType(None, None, 'getURL', 'setURL') # The converters are defined in a customizer def _asPython(x): return x._py() # This maps the types reported by the columns to the type used for the getter # and converter _default_map = {ARRAY: OBJECT, OBJECT: OBJECT, NULL: OBJECT, REF: OBJECT, ROWID: OBJECT, RESULTSET: OBJECT, TIME_WITH_TIMEZONE: OBJECT, TIMESTAMP_WITH_TIMEZONE: OBJECT, NVARCHAR: STRING, CHAR: STRING, NCHAR: STRING, VARCHAR: STRING, BINARY: BINARY, BLOB: BINARY, LONGVARBINARY: BINARY, VARBINARY: BINARY, NUMBER: NUMBER, BOOLEAN: BOOLEAN, BIGINT: BIGINT, BIT: BIT, INTEGER: INTEGER, SMALLINT: SMALLINT, TINYINT: TINYINT, FLOAT: FLOAT, REAL: REAL, DECIMAL: DECIMAL, NUMERIC: NUMERIC, DATE: DATE, TIMESTAMP: TIMESTAMP, TIME: TIME, CLOB: STRING, NCLOB: STRING, STRING: STRING, TEXT: STRING, SQLXML: STRING, LONGVARCHAR: STRING, LONGNVARCHAR: STRING, DOUBLE: DOUBLE, OTHER: OBJECT } _default_setters = {} # type: ignore[var-annotated] _default_converters = {} # type: ignore[var-annotated] _default_adapters = {} # type: ignore[var-annotated] # Setters take (connection, meta, col, type) -> JDBCTYPE def SETTERS_BY_META(cx, meta, col, ptype): """ Option for setters to use the metadata of the parameters. On some databases this option is useless as they do not track parameter types. This method can be cached for faster performance when the are lots of parameters. Usually types can only be determined accurately on inserts into defined columns. """ return _default_map[_registry[meta.getParameterType(col + 1)]] SETTERS_BY_META._cachable = True # type: ignore[attr-defined] def SETTERS_BY_TYPE(cx, meta, col, ptype): """ Option for setters to use the type of the object passed. This option looks at the type of the parameter being passed from Python after adapters have been applied to determine the best setter. """ return _default_setters.get(ptype, None) # Getters take (connection, meta, col) -> JDBCTYPE def GETTERS_BY_TYPE(cx, meta, idx): """ Option for getters to determine column type by the JDBC type. This option is the default option that uses the type code supplied in the meta data. On some databases it is better to use the name. If the type code is OTHER, it will attempt to find a type by name. New types can be created with JDBCType for database specific types. """ tp = _registry[meta.getColumnType(idx + 1)] if tp == OTHER: # Other may be found by name name = str(meta.getColumnTypeName(idx + 1)).upper() return _registry.get(name, tp) return _default_map[tp] def GETTERS_BY_NAME(cx, meta, idx): """ Option for getters to determine column type by the column name. This option uses the column name to select the type. It looks up the column type name, converts it to uppercase, and then searches for a matching type. It falls back to the type code meta information if the type name cannot be found in the registry. New types can be created using JDBCType for database specific types such as ``JSON``. """ name = str(meta.getColumnTypeName(idx + 1)).upper() tp = _registry.get(name, None) if tp is None: tp = _registry[meta.getColumnType(idx + 1)] return _default_map.get(tp, tp) ############################################################################### # Exceptions class Warning(Exception): """Exception raised for important warnings like data truncations while inserting, etc. """ pass class Error(Exception): """Exception that is the base class of all other error exceptions. You can use this to catch all errors with one single except statement. Warnings are not considered errors and thus should not use this class as base. """ pass class InterfaceError(Error, TypeError): """ Exception raised for errors that are related to the database interface rather than the database itself.""" pass class DatabaseError(Error): """ Exception raised for errors that are related to the database.""" pass class DataError(DatabaseError): """ Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range, etc.""" pass class OperationalError(DatabaseError): """ Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer, e.g. an unexpected disconnect occurs, the data source name is not found, a transaction could not be processed, a memory allocation error occurred during processing, etc.""" pass class IntegrityError(DatabaseError): """ Exception raised when the relational integrity of the database is affected, e.g. a foreign key check fails.""" pass class InternalError(DatabaseError): """ Exception raised when the database encounters an internal error, e.g. the cursor is not valid anymore, the transaction is out of sync, etc.""" pass class ProgrammingError(DatabaseError): """ Exception raised for programming errors, e.g. table not found or already exists, syntax error in the SQL statement, wrong number of parameters specified, etc.""" pass class NotSupportedError(DatabaseError): """ Exception raised in case a method or database API was used which is not supported by the database, e.g. requesting a .rollback() on a connection that does not support transaction or has transactions turned off. """ pass class _UnsupportedTypeError(InterfaceError, TypeError): pass ############################################################################### # Connection _default = object() def connect(dsn, *, driver=None, driver_args=None, adapters=_default, converters=_default, getters=GETTERS_BY_TYPE, setters=SETTERS_BY_TYPE, **kwargs): """ Create a connection to a database. Arguments to the driver depend on the database type. Args: dsn (str): The database connection string for JDBC. driver (str, optional): A JDBC driver to load. driver_args: Arguments to the driver. This may either be a dict, java.util.Properties. If not supplied, kwargs are used as the parameters for the JDBC connection. *kwargs: Arguments to the driver if not supplied as driver_args. Raises: Error if the connection cannot be established. Returns: A new connection if successful. """ Properties = _jpype.JClass("java.util.Properties") if driver: _jpype.JClass('java.lang.Class').forName( driver, True, _jpype.JPypeClassLoader).newInstance() DM = _jpype.JClass('java.sql.DriverManager') # DM.setLogStream(_jpype.JClass("java.lang.System").out) # User is supplying Java properties if isinstance(driver_args, Properties): connection = DM.getConnection(dsn, driver_args) # User is supplying a mapping that can be converted Properties elif isinstance(driver_args, typing.Mapping): info = Properties() for k, v in driver_args.items(): info.setProperty(k, v) connection = DM.getConnection(dsn, info) # User supplied nothing elif driver_args is None: connection = DM.getConnection(dsn) # Otherwise use the kwargs else: info = Properties() for k, v in kwargs.items(): info.setProperty(k, v) connection = DM.getConnection(url, info) return Connection(connection, adapters, converters, setters, getters) class Connection(object): """ Connection provides access to a JDBC database. Connections are managed and can be used as part of a Python 'with' statement. Connections close automatically when they are garbage collected, at the end of a with statement scope, or when manually closed. Once a connection is closed all operations using the database will raise an Error. """ Error = Error Warning = Warning InterfaceError = InterfaceError DatabaseError = DatabaseError InternalError = InternalError OperationalError = OperationalError ProgrammingError = ProgrammingError IntegrityError = IntegrityError DataError = DataError NotSupportedError = NotSupportedError def __init__(self, jconnection, adapters, converters, setters, getters): self._jcx = jconnection # Required by PEP 249 # https://www.python.org/dev/peps/pep-0249/#commit try: # Some driver do not support this feature. # https://github.com/jpype-project/jpype/issues/1003 self._jcx.setAutoCommit(False) except Exception as ex: if ex.getClass().getSimpleName() != 'SQLFeatureNotSupportedException': raise ex # Handle defaults if adapters is _default: adapters = dict(_default_adapters) if adapters is None: adapters = {} if converters is _default: converters = dict(_default_converters) if converters is None: converters = {} self._closed = False self._batch = jconnection.getMetaData().supportsBatchUpdates() self._adapters = adapters self._converters = converters self._getters = getters self._setters = setters @property def adapters(self): """ Adapters are used to convert Python types into JDBC types. Adaptors are stored in a mapping from incoming type to an adapter function. Adapters set on a connection apply only to that connection. Adapters can be overriden when calling the ``.execute*()`` method. Adapters can also be set on the JDBC types directly. """ return self._adapters @adapters.setter def adapters(self, v): if v is None: v = {} if not isinstance(v, typing.Mapping): raise _UnsupportedTypeError("Mapping is required") self._adapters = v @property def converters(self): """ Converters are applied when retrieving a JDBC type from the database. """ return self._converters @converters.setter def converters(self, v): if v is None: v = {} if not isinstance(v, typing.Mapping): raise _UnsupportedTypeError("Mapping is required") self._converters = v @property def getters(self): """ Getters are used to retrieve JDBC types from the database following a ``.fetch*()``. Getters should be a function taking (connection, meta, col) -> JDBCTYPE """ return self._getters # Setters take (connection, meta, col, type) -> JDBCTYPE @getters.setter def getters(self, v): self._getters = v @property def setters(self): """ Setters are used to set parameters to ``.execute*()`` methods. Setters should be a function taking (connection, meta, col, type) -> JDBCTYPE """ return self._setters @setters.setter def setters(self, v): self._setters = v def __setattr__(self, name, value): if isinstance(vars(type(self)).get(name, None), property): return object.__setattr__(self, name, value) if not name.startswith("_"): raise AttributeError("'%s' cannot be set" % name) object.__setattr__(self, name, value) def _close(self): if self._closed or not _jpype.isStarted(): return if not self._jcx.isClosed(): self._jcx.close() self._closed = True def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback): self._close() def __del__(self): try: self._close() except Exception: # pragma: no cover pass def close(self): """ Close the connection immediately (rather than whenever .__del__() is called). The connection will be unusable from this point forward; an Error (or subclass) exception will be raised if any operation is attempted with the connection. The same applies to all cursor objects trying to use the connection. Note that closing a connection without committing the changes first will cause an implicit rollback to be performed. """ self._validate() self._close() def commit(self): """Commit any pending transaction to the database. Calling commit on a connection that does not support the operation will raise NotSupportedError. """ self._validate() if self._jcx.getAutoCommit(): raise NotSupportedError("Autocommit is enabled") try: self._jcx.commit() except Exception as ex: # pragma: no cover raise OperationalError(ex.message()) from ex def rollback(self): """Rollback the transaction. This method is optional since not all databases provide transaction support. Calling rollback on a connection that does not support it will raise NotSupportedError. In case a database does provide transactions this method causes the database to roll back to the start of any pending transaction. Closing a connection without committing the changes first will cause an implicit rollback to be performed. """ self._validate() if self._jcx.getAutoCommit(): raise NotSupportedError("Autocommit is enabled", self.autocommit) try: self._jcx.rollback() except Exception as ex: # pragma: no cover raise OperationalError(ex.message()) from ex def cursor(self): """ Return a new Cursor Object using the connection. """ self._validate() return Cursor(self) def _validate(self): if self._closed or self._jcx.isClosed(): raise ProgrammingError @property def connection(self): """ Get the JDBC connection that is backing this Python Connection object. This can be used to retrieve additional metadata and other features that are outside of the scope of the DBAPI driver. """ return self._jcx @property def autocommit(self): """ bool: Property controlling autocommit behavior. By default connects are not autocommit. Setting autocommit will result in commit and rollback producing a ProgrammingError. """ self._validate() return self._jcx.getAutoCommit() @autocommit.setter def autocommit(self, enabled): self._validate() self._jcx.setAutoCommit(enabled) @property def typeinfo(self): """ list: The list of types that are supported by this driver. This is useful to find out the capabilities of the driver. """ self._validate() out = {} metadata = self._jcx.getMetaData() with metadata.getTypeInfo() as resultSet: while (resultSet.next()): try: key = str(resultSet.getString("TYPE_NAME")) out[key] = _registry[resultSet.getInt("DATA_TYPE")] except KeyError as ex: # pragma: no cover raise DatabaseError("Unknown data type '%s'" % key) from ex return out ############################################################################### # Cursor class Cursor(object): """ Cursors are used to execute queries and retrieve results. Part PreparedStatement, part ResultSet, Cursors are a mixture of both. The native resultSet can be accessed with ``resultSet``. Cursors are managed and can be as part of a Python with statement. Cursors close automatically when they are garbage collected, at the end of a with statement scope, or when manually closed. Once a cursor is closed all operations using the database will raise an Error. """ def __init__(self, connection): if not isinstance(connection, Connection): raise TypeError self._connection = connection self._jcx = connection._jcx self._resultSet = None self._statement = None self._rowcount = -1 self._arraysize = 1 self._description = None self._closed = False self._resultGetters = None self._thread = threading.get_ident() self._last = None def _close(self): if self._closed or not _jpype.isStarted(): return self._finish() self._closed = True def _setParams(self, params): cx = self._connection meta = self._statement.getParameterMetaData() count = meta.getParameterCount() types = self._parameterTypes if types is None: types = [None] * count if isinstance(params, str): raise _UnsupportedTypeError( "parameters must be a sequence of values") if isinstance(params, typing.Sequence): if count != len(params): raise ProgrammingError("incorrect number of parameters (%d!=%d)" % (count, len(params))) for i in range(len(params)): p = params[i] # Find and apply the adapter a = cx._adapters.get(type(p), None) if a is not None: p = a(p) # Find the setter if types[i] is None: s = cx._setters(cx, meta, i, type(p)) types[i] = s if s is None: raise _UnsupportedTypeError( "no setter found for '%s'" % type(p).__name__) s.set(self._statement, i + 1, p) # Cache if hasattr(cx._setters, "_cachable"): self._parameterTypes = types elif isinstance(params, typing.Mapping): raise _UnsupportedTypeError("mapping parameters not supported") elif isinstance(params, typing.Iterable): for i, p in enumerate(params): if i >= count: raise ProgrammingError( "incorrect number of parameters (%d!=%d)" % (count, i + 1)) # Find and apply the adapter a = cx._adapters.get(type(p), None) if a is not None: p = a(p) # Find the setter if types[i] is None: s = cx._setters(cx, meta, i, type(p)) types[i] = s if s is None: raise _UnsupportedTypeError( "no setter found for '%s'" % type(p).__name__) s.set(self._statement, i + 1, p) if count != i + 1: raise ProgrammingError( "incorrect number of parameters (%d!=%d)" % (count, i + 1)) # Cache if hasattr(cx._setters, "_cachable"): self._parameterTypes = types else: raise _UnsupportedTypeError("'%s' parameters not supported" % ( type(params).__name__)) # pragma: no cover def _onResultSet(self, rs): meta = rs.getMetaData() self._resultSet = rs self._resultSetMeta = meta self._resultSetCount = meta.getColumnCount() self._columnTypes = None def _fetchRow(self, converters): cx = self._connection count = self._resultSetCount meta = self._resultSetMeta byPosition = False if converters is _default: converters = cx._converters if isinstance(converters, typing.Sequence): if len(converters) != count: raise ProgrammingError("converter list size incorrect") byPosition = True # Get all the column types if self._columnTypes is None: gk = cx._getters # We need the type information for the columns self._columnTypes = [gk(cx, meta, i) for i in range(count)] if len(self._columnTypes) != count: raise ProgrammingError("incorrect number of columns") try: row = [] for idx in range(count): tp = self._columnTypes[idx] # Fetch the value value = tp.get(self._resultSet, idx + 1, False) if value is None or converters is None: row.append(value) elif byPosition: # find the column converter by type converter = converters[idx] row.append(converter(value)) else: # find the column converter by type converter = cx._converters.get(type(value), _nop) row.append(converter(value)) return row except TypeError as ex: raise _UnsupportedTypeError(str(ex)) from ex def _validate(self): """ Called before any method that requires the statement to be open. """ if self._closed or self._jcx.isClosed(): raise ProgrammingError("Cursor is closed") if threading.get_ident() != self._thread: raise ProgrammingError("Threading error") def _check_executed(self): """ Called before any method that requires the resultSet to be open. """ if self._closed or self._jcx.isClosed() or threading.get_ident() != self._thread: raise ProgrammingError("Cursor is closed") if self._resultSet is None: raise ProgrammingError("execute() first") def _finish(self): if self._resultSet is not None: self._resultSet.close() self._resultSet = None if self._statement is not None: self._statement.close() self._statement = None self._rowcount = -1 self._description = None self._last = None @property def resultSet(self): """ Get the Java result set if available. The object will be closed on the next call to ``.execute*()``. """ return self._resultSet @property def parameters(self): """ (extension) Parameters is a read-only attribute. It is a sequence of 6-item sequences. Each of these sequences contains information describing one result column: - type_name - jdbc_type - parameter_mode (1=in, 2=in/out, 4=out) - precision - scale - null_ok This can only be used after execute or callproc. """ desc = [] if not self._statement: raise ProgrammingError("No statement") meta = self._statement.getParameterMetaData() for i in range(1, meta.getParameterCount() + 1): desc.append((str(meta.getParameterTypeName(i)), _registry[meta.getParameterType(i)], meta.getParameterMode(i), meta.getPrecision(i), meta.getScale(i), meta.isNullable(i),)) return desc @property def description(self): """ Description is a read-only attribute. It is a sequence of 7-item sequences. Each of these sequences contains information describing one result column: - name - type_code - display_size - internal_size - precision - scale - null_ok This can only be used if the last query produced a result set. """ if self._description is not None: return self._description desc = [] if not self._resultSet: return None meta = self._resultSet.getMetaData() for i in range(1, meta.getColumnCount() + 1): size = meta.getColumnDisplaySize(i) desc.append((str(meta.getColumnName(i)), str(meta.getColumnTypeName(i)), size, size, meta.getPrecision(i), meta.getScale(i), meta.isNullable(i),)) self._description = desc return desc @property def rowcount(self): """ This read-only attribute specifies the number of rows that the last .execute*() affected (for DML statements like UPDATE or INSERT). The attribute is -1 in case no .execute*() has been performed on the cursor or the rowcount of the last operation cannot be determined by the interface. JDBC does not support getting the number of rows returned from SELECT, so for most drivers rowcount will be -1 after a SELECT statement. """ return self._rowcount def close(self): """ Close the cursor now (rather than whenever __del__ is called). The cursor will be unusable from this point forward; an Error (or subclass) exception will be raised if any operation is attempted with the cursor. """ self._validate() self._close() def callproc(self, procname, parameters=(), *, types=None): """ Call a stored procedure. (Not all JDBC drivers support this method) Call a stored database procedure with the given name. The sequence of parameters must contain one entry for each argument that the procedure expects. The result of the call is returned as modified copy of the input sequence. Input parameters are left untouched, output and input/output parameters replaced with possibly new values. For type output and input/output arguments, it is best to use types keyword argument to select the appropriate getters for the returned arguments. Converters are applied to output parameters. The procedure may also provide a result set as output. This must then be made available through the standard .fetch*() methods. """ try: self._validate() self._finish() if not isinstance(procname, str): raise _UnsupportedTypeError( "procname must be str, not '%s'" % type(procname).__name__) if not isinstance(parameters, typing.Sequence): raise _UnsupportedTypeError( "parameters must be sequence, not '%s'" % type(procname).__name__) query = "{CALL %s(%s)}" % ( procname, ",".join("?" * len(parameters))) try: self._statement = self._jcx.prepareCall(query) except _SQLException as ex: raise ProgrammingError(ex.message()) from ex # This is a special one as we need to deal with in and out arguments out = list(parameters) cx = self._connection meta = self._statement.getParameterMetaData() count = meta.getParameterCount() if types is None: types = [None] * count else: if len(types) != count: raise ProgrammingError( "expected '%d' types, got '%d'" % (count, len(types))) for i in range(count): # Lookup the JDBC Type p = parameters[i] a = cx._adapters.get(type(p), None) if a is not None: p = a(p) if types[i] is None: types[i] = cx._setters(cx, meta, i, type(p)) jdbcType = types[i] if jdbcType is None: raise _UnsupportedTypeError( "no setter found for '%s'" % type(p).__name__) mode = meta.getParameterMode(i + 1) if mode == 1: jdbcType.set(self._statement, i + 1, p) types[i] = None if mode == 2: jdbcType.set(self._statement, i + 1, p) self.registerOutParameter(i + 1, jdbcType._code) if mode == 4: self.registerOutParameter(i + 1, jdbcType._code) if self._statement.execute(): self._onResultSet(self._statement.getResultSet()) self._rowcount = self._statement.getUpdateCount() for i, t in enumerate(types): if t is None: continue # Find the converter to apply to the column value = t.get(self._statement, i + 1, True) # FIXME how do we get a converv converter = cx._converters[type(value)] out[i] = converter(value) return out # Restore the defaults except Exception as ex: self._converters = self._connection._converters self._getters = self._connection._getters raise ex finally: self._adapters = self._connection._adapters self._setters = self._connection._setters def execute(self, operation, parameters=None, *, types=None, keys=False): """ Prepare and execute a database operation (query or command). Parameters may be provided as sequence and will be bound to variables in the operation. Variables are specified in a qmark notation. JDBC does not support mapping style parameters. After executing a statement, the rowcount will be updated. If the statement has no result set then the rowcount will be -1. A statement can produce multiple result sets. Use ``.nextset()`` to traverse the sets. Parameters: operation (str): A statement to be executed. parameters (list, optional): A list of parameters for the statement. The number of parameters must match the number required by the statement or an Error will be raised. keys (bool, optional): Specify if the keys should be available to retrieve. (Default False) Returns: This cursor. """ self._last = None self._parameterTypes = types self._validate() self._finish() if parameters is None: parameters = () if not isinstance(parameters, (typing.Sequence, typing.Iterable, typing.Iterator)): raise _UnsupportedTypeError( "parameters are of unsupported type '%s'" % type(parameters).__name__) # complete the previous operation try: if keys: self._statement = self._jcx.prepareStatement(operation, 1) else: self._statement = self._jcx.prepareStatement(operation) except TypeError as ex: raise _UnsupportedTypeError(str(ex)) except _SQLException as ex: raise ProgrammingError(ex.message()) from ex self._executeone(parameters) return self def _executeone(self, params): self._setParams(params) if self._statement.execute(): self._onResultSet(self._statement.getResultSet()) self._rowcount = self._statement.getUpdateCount() return self._rowcount def executemany(self, operation, seq_of_parameters, *, types=None, keys=False): """ Prepare a database operation (query or command) and then execute it against all parameter sequences or mappings found in the sequence seq_of_parameters. Modules are free to implement this method using multiple calls to the .execute() method or by using array operations to have the database process the sequence as a whole in one call. Use of this method for an operation which produces one or more result sets constitutes undefined behavior, and the implementation is permitted (but not required) to raise an exception when it detects that a result set has been created by an invocation of the operation. The same comments as for .execute() also apply accordingly to this method. Args: operation (str): A statement to be executed. seq_of_parameters (list, optional): A list of lists of parameters for the statement. The number of parameters must match the number required by the statement or an Error will be raised. keys (bool, optional): Specify if the keys should be available to retrieve. (Default False) For drivers that do not support batch updates only the last key will be returned. Returns: This cursor. """ self._last = None self._parameterTypes = types self._validate() if seq_of_parameters is None: seq_of_parameters = () # complete the previous operation self._finish() try: if keys: self._statement = self._jcx.prepareStatement(operation, 1) else: self._statement = self._jcx.prepareStatement(operation) except TypeError as ex: raise _UnsupportedTypeError(str(ex)) except _SQLException as ex: raise ProgrammingError( "Failed to prepare '%s'" % operation) from ex if self._connection._batch: return self._executeBatch(seq_of_parameters) else: # pragma: no cover return self._executeRepeat(seq_of_parameters) def _executeBatch(self, seq_of_parameters): if isinstance(seq_of_parameters, typing.Iterable): for params in seq_of_parameters: self._setParams(params) self._statement.addBatch() else: raise _UnsupportedTypeError( "'%s' is not supported" % type(seq_of_parameters).__name__) try: counts = self._statement.executeBatch() except _SQLException as ex: # pragma: no cover raise ProgrammingError(ex.message()) from ex self._rowcount = sum(counts) if self._rowcount < 0: # pragma: no cover self._rowcount = -1 return self def _executeRepeat(self, seq_of_parameters): # pragma: no cover counts = [] if isinstance(seq_of_parameters, typing.Iterable): for params in seq_of_parameters: counts.append(self._executeone(params)) elif isinstance(seq_of_parameters, typing.Iterator): while True: try: params = next(seq_of_parameters) counts.append(self._executeone(params)) except StopIteration: break else: raise _UnsupportedTypeError( "'%s' is not supported" % str(type(seq_of_parameters))) self._rowcount = sum(counts) if self._rowcount < 0: self._rowcount = -1 return self def fetchone(self, *, types=None, converters=_default): """ Fetch the next row of a query result set, returning a single sequence, or None when no more data is available. An Error (or subclass) exception is raised if the previous call to .execute*() did not produce any result set or no call was issued yet. """ self._check_executed() if not self._resultSet.next(): return None if types is not None: self._columnTypes = types return self._fetchRow(converters) def fetchmany(self, size=None, *, types=None, converters=_default): """ Fetch multiple results. Fetch the next set of rows of a query result, returning a sequence of sequences (e.g. a list of tuples). An empty sequence is returned when no more rows are available. The number of rows to fetch per call is specified by the parameter. If it is not given, the cursor's arraysize determines the number of rows to be fetched. The method should try to fetch as many rows as indicated by the size parameter. If this is not possible due to the specified number of rows not being available, fewer rows may be returned. An Error (or subclass) exception is raised if the previous call to ``.execute*()`` did not produce any result set or no call was issued yet. Note there are performance considerations involved with the size parameter. For optimal performance, it is usually best to use the .arraysize attribute. If the size parameter is used, then it is best for it to retain the same value from one ``.fetchmany()`` call to the next. """ self._check_executed() if size is None: size = self._arraysize # Set a fetch size self._resultSet.setFetchSize(size) rows = [] if types is not None: self._columnTypes = types for i in range(size): if not self._resultSet.next(): break row = self._fetchRow(converters) rows.append(row) # Restore the default fetch size self._resultSet.setFetchSize(0) return rows def fetchall(self, *, types=None, converters=_default): """ Fetch all (remaining) rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples). Note that the cursor's arraysize attribute can affect the performance of this operation. An Error (or subclass) exception is raised if the previous call to ``.execute*()`` did not produce any result set or no call was issued yet. """ self._check_executed() # Set a fetch size rows = [] if types is not None: self._columnTypes = types while self._resultSet.next(): row = self._fetchRow(converters) rows.append(row) return rows def __iter__(self): """ (extension) Iterate through a cursor one record at a time. """ self._check_executed() # Set a fetch size while self._resultSet.next(): yield self._fetchRow(_default) def nextset(self): """ Get the next result set in this cursor. Not all databases support multiple result sets. This method will make the cursor skip to the next available set, discarding any remaining rows from the current set. If there are no more sets, the method returns None. Otherwise, it returns a true value and subsequent calls to the ``.fetch*()`` methods will return rows from the next result set. An Error (or subclass) exception is raised if the previous call to ``.execute*()`` did not produce any result set or no call was issued yet. """ self._resultSet.close() if self._statement.getMoreResults(): # pragma: no cover self._onResultSet(_statement.getResultSet()) return True else: self._rowcount = self._statement.getUpdateCount() return None @property def arraysize(self): """ Specify the number of rows to fetch with ``.fetchmany()``. This read/write attribute specifies the number of rows to fetch at a time with ``.fetchmany()``. It defaults to 1 meaning to fetch a single row at a time. """ return self._arraysize @arraysize.setter def arraysize(self, sz): self._arraysize = sz @property def lastrowid(self): """ Get the id of the last row inserted. This is not supported on all JDBC drivers. The ``.execute*()`` must have been executed with keys set to True. Returns: None if there is no rowid, the rowid if only one row was inserted, or a list of row ids if multiple rows were inserted. """ if self._last is not None: return self._last with self._statement.getGeneratedKeys() as rs: if rs.isClosed(): return self._last last = [] while rs.next(): last.append(rs.getLong(1)) if len(last) == 0: return None if len(last) == 1: self._last = last[0] return last[0] self._last = last return last def setinputsizes(self, sizes): """ This can be used before a call to .execute*() to predefine memory areas for the operation's parameters. sizes is specified as a sequence β€” one item for each input parameter. The item should be a Type Object that corresponds to the input that will be used, or it should be an integer specifying the maximum length of a string parameter. If the item is None, then no predefined memory area will be reserved for that column (this is useful to avoid predefined areas for large inputs). This method would be used before the .execute*() method is invoked. (not implemented) """ pass def setoutputsize(self, size, column=None): """ Set a column buffer size for fetches of large columns (e.g. LONGs, BLOBs, etc.). The column is specified as an index into the result sequence. Not specifying the column will set the default size for all large columns in the cursor. (not implemented) """ pass def __del__(self): try: self._close() except Exception: # pragma: no cover pass def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback): self._close() ############################################################################### # Factories def Date(year, month, day): """ This function constructs an object holding a date value. """ return _jpype.JClass('java.sql.Date')(year - 1900, month - 1, day) def Time(hour, minute, second): """ This function constructs an object holding a time value. """ return _jpype.JClass('java.sql.Time')(hour, minute, second) def Timestamp(year, month, day, hour, minute, second, nano=0): """ This function constructs an object holding a timestamp value. """ return _jpype.JClass('java.sql.Timestamp')(year - 1900, month - 1, day, hour, minute, second, nano) def DateFromTicks(ticks): """ This function constructs an object holding a date value from the given ticks value (number of seconds since the epoch; see the documentation of the standard Python time module for details). """ return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): """ This function constructs an object holding a time value from the given ticks value (number of seconds since the epoch; see the documentation of the standard Python time module for details). """ return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): """ This function constructs an object holding a timestamp value from the given ticks value (number of seconds since the epoch; see the documentation of the standard Python time module for details). """ return Timestamp(*time.localtime(ticks)[:6]) def Binary(data): """ This function constructs an object capable of holding a binary (long) string value. """ return _jtypes.JArray(_jtypes.JByte)(data) # SQL NULL values are represented by the Python None singleton on input and output. _accepted = set(["exact", "implicit"]) def _populateTypes(): global _SQLException, _SQLTimeoutException _SQLException = _jpype.JClass("java.sql.SQLException") _SQLTimeoutException = _jpype.JClass("java.sql.SQLTimeoutException") cs = _jpype.JClass("java.sql.CallableStatement") ps = _jpype.JClass("java.sql.PreparedStatement") rs = _jpype.JClass("java.sql.ResultSet") for v in _types: v._initialize(cs, ps, rs) java = _jpype._JPackage("java") byteArray = _jpype.JArray(_jtypes.JByte) _default_setters[java.lang.String] = STRING _default_setters[java.sql.Date] = DATE _default_setters[java.sql.Time] = TIME _default_setters[java.sql.Timestamp] = TIMESTAMP _default_setters[byteArray] = BINARY _default_setters[java.math.BigDecimal] = DECIMAL _default_setters[_jtypes.JFloat] = REAL _default_setters[_jtypes.JDouble] = DOUBLE _default_setters[_jtypes.JBoolean] = BOOLEAN _default_setters[_jtypes.JShort] = INTEGER _default_setters[_jtypes.JInt] = INTEGER _default_setters[_jtypes.JLong] = BIGINT _default_setters[bool] = BOOLEAN _default_setters[int] = BIGINT _default_setters[float] = DOUBLE _default_setters[str] = STRING _default_setters[memoryview] = BINARY _default_setters[bytes] = BINARY _default_setters[bytearray] = BINARY _default_setters[type(None)] = OBJECT _default_setters[java.sql.Clob] = CLOB _default_setters[java.sql.Blob] = BLOB _default_setters[datetime.datetime] = TIMESTAMP _default_setters[datetime.date] = DATE _default_setters[datetime.time] = TIME _default_converters[java.lang.String] = str _default_converters[java.sql.Date] = _asPython _default_converters[java.sql.Time] = _asPython _default_converters[java.sql.Timestamp] = _asPython _default_converters[java.math.BigDecimal] = _asPython _default_converters[byteArray] = bytes _default_converters[type(None)] = _nop # Adaptors can be installed after the JVM is started # JByteArray = _jpype.JArray(_jtypes.JByte) # VARCHAR.adapters[memoryview] = JByteArray _jcustomizer.getClassHints("java.sql.SQLException").registerClassBase(Error) _jinit.registerJVMInitializer(_populateTypes) jpype-1.6.0/jpype/imports.py000066400000000000000000000176611501674766700161010ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** """ JPype Imports Module -------------------- Once imported this module will place the standard Top Level Domains (TLD) into the Python scope. These TLDs are ``java``, ``com``, ``org``, ``gov``, ``mil``, ``net`` and ``edu``. Java symbols from these domains can be imported using the standard Python syntax. Forms supported: - **import [ as ]** - **import . [ as ]** - **from import [,*]** - **from import [ as ]** - **from . import [ as ]** - **from . import [ as ]** For further information please read the :doc:`imports` guide. Example: .. code-block:: python import jpype import jpype.imports jpype.startJVM() # Import java packages as modules from java.lang import String """ import sys import _jpype from importlib.machinery import ModuleSpec as _ModuleSpec from . import _pykeywords __all__ = ["registerImportCustomizer", "registerDomain", "JImportCustomizer"] # %% Utility def _keywordUnwrap(name): if not name.endswith('_'): return name if name[:-1] in _pykeywords._KEYWORDS: return name[:-1] return name def _keywordWrap(name): if name in _pykeywords._KEYWORDS: return name + "_" return name # %% Customizer _CUSTOMIZERS = [] def _JExceptionHandler(pkg, name, ex): javaname = str(pkg) + "." + name exname = type(ex).__name__ ex._expandStacktrace() if exname == "java.lang.ExceptionInInitializerError": raise ImportError( "Unable to import '%s' due to initializer error" % javaname) from ex if exname == "java.lang.UnsupportedClassVersionError": raise ImportError( "Unable to import '%s' due to incorrect Java version" % javaname) from ex if exname == "java.lang.NoClassDefFoundError": missing = str(ex).replace('/', '.') raise ImportError("Unable to import '%s' due to missing dependency '%s'" % ( javaname, missing)) from ex raise ImportError("Unable to import '%s'" % javaname) from ex def registerImportCustomizer(customizer): """ Import customizers can be used to import python packages into java modules automatically. """ _CUSTOMIZERS.append(customizer) # Support hook for placing other things into the java tree class JImportCustomizer(object): """ Base class for Import customizer. Import customizers should implement canCustomize and getSpec. Example: .. code-block:: python # Site packages for each java package are stored under $DEVEL//py class SiteCustomizer(jpype.imports.JImportCustomizer): def canCustomize(self, name): if name.startswith('org.mysite') and name.endswith('.py'): return True return False def getSpec(self, name): pname = name[:-3] devel = os.environ.get('DEVEL') path = os.path.join(devel, pname,'py','__init__.py') return importlib.util.spec_from_file_location(name, path) """ def canCustomize(self, name): """ Determine if this path is to be treated differently Return: True if an alternative spec is required. """ return False def getSpec(self, name): """ Get the module spec for this module. """ raise NotImplementedError # %% Finder def unwrap(name): # Deal with Python keywords in the Java path if not '_' in name: return name return ".".join([_keywordUnwrap(i) for i in name.split('.')]) class _JImportLoader: """ (internal) Finder hook for importlib. """ def find_spec(self, name, path, target=None): # If jvm is not started then we just check against the TLDs if not _jpype.isStarted(): base = name.partition('.')[0] if not base in _JDOMAINS: return None raise ImportError( "Attempt to create Java package '%s' without jvm" % name) # Check for aliases if name in _JDOMAINS: jname = _JDOMAINS[name] if not _jpype.isPackage(jname): raise ImportError( "Java package '%s' not found, requested by alias '%s'" % (jname, name)) ms = _ModuleSpec(name, self) ms._jname = jname return ms # Check if it is a TLD parts = name.rpartition('.') # Use the parent module to simplify name mangling if not parts[1] and _jpype.isPackage(parts[2]): ms = _ModuleSpec(name, self) ms._jname = name return ms if not parts[1] and not _jpype.isPackage(parts[0]): return None base = sys.modules.get(parts[0], None) if not base or not isinstance(base, _jpype._JPackage): return None # Support for external modules in java tree name = unwrap(name) for customizer in _CUSTOMIZERS: if customizer.canCustomize(name): return customizer.getSpec(name) # Using isPackage eliminates need for registering tlds if not hasattr(base, parts[2]): # If the base is a Java package and it wasn't found in the # package using getAttr, then we need to emit an error # so we produce a meaningful diagnositic. try: # Use forname because it give better diagnostics cls = _jpype._java_lang_Class.forName( name, True, _jpype.JPypeClassLoader) # This code only is hit if an error was not thrown if cls.getModifiers() & 1 == 0: raise ImportError("Class `%s` is not public" % name) raise ImportError( "Class `%s` was found but was not expected" % name) # Not found is acceptable except Exception as ex: raise ImportError("Failed to import '%s'" % name) from ex # Import the java module return _ModuleSpec(name, self) """ (internal) Loader hook for importlib. """ def create_module(self, spec): if spec.parent == "": return _jpype._JPackage(spec._jname) parts = spec.name.rsplit('.', 1) rc = getattr(sys.modules[spec.parent], parts[1]) # Install the handler rc._handler = _JExceptionHandler return rc def exec_module(self, fullname): pass # Install hooks into python importlib sys.meta_path.append(_JImportLoader()) # %% Domains _JDOMAINS = {} def registerDomain(mod, alias=None): """ Add a Java domain to Python as a dynamic module. This can be used to bind a Java path to a Python path. Args: mod(str): Is the Python module to bind to Java. alias(str, optional): Is the name of the Java path if different than the Python name. """ if not alias: alias = mod _JDOMAINS[mod] = alias # Preregister common top level domains registerDomain('com') registerDomain('gov') registerDomain('java') registerDomain('org') registerDomain('mil') registerDomain('edu') registerDomain('net') jpype-1.6.0/jpype/nio.py000066400000000000000000000024171501674766700151620ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype __all__ = ['convertToDirectBuffer'] def convertToDirectBuffer(obj): __doc__ = '''Efficiently convert all array.array and numpy ndarray types, string and unicode to java.nio.Buffer objects. If the passed object is readonly (i.e. bytes or a readonly memoryview) the returned ByteBuffer will be a readonly Buffer object. Otherwise a writable Buffer is returned.''' memoryview_of_obj = memoryview(obj) ro_view = memoryview_of_obj.readonly return _jpype.convertToDirectBuffer(memoryview_of_obj, ro_view) jpype-1.6.0/jpype/pickle.py000066400000000000000000000122331501674766700156410ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** """ JPype Pickle Module -------------------- This module contains overloaded Pickler and Unpickler classes that operate on Java classes. Pickling of Java objects is restricted to classes that implement Serializable. Mixed pickle files containing both Java and Python objects are allowed. Only one copy of each Java object will appear in the pickle file even it is appears multiple times in the data structure. JPicklers and JUnpickler use Java ObjectOutputStream and ObjectInputStream to serialize objects. All of the usual Java serialization errors may be thrown. This is backed by the native cPickler implementation. Example: .. code-block:: python myobj = jpype.JClass('java.util.ArrayList')() myobj.add("test") from jpype.pickle import JPickler, JUnpickler with open("test.pic", "wb") as fd: JPickler(fd).dump(myobj) with open("test.pic", "rb") as fd: newobj = JUnpickler(fd).load() Proxies and other JPype specific module resources cannot be pickled currently. """ from __future__ import absolute_import import _jpype import pickle from copyreg import dispatch_table # TODO: Support use of a custom classloader with the unpickler. # TODO: Use copyreg to pickle a JProxy __ALL__ = ['JPickler', 'JUnpickler'] # This must exist as a global, the real unserializer is created by the JUnpickler class JUnserializer(object): def __call__(self, *args): raise pickle.UnpicklingError("Unpickling Java requires JUnpickler") class _JDispatch(object): """Dispatch for Java classes and objects. Python does not have a good way to register a reducer that applies to many classes, thus we will substitute the usual dictionary with a class that can produce reducers as needed. """ def __init__(self, dispatch): self._encoder = _jpype.JClass('org.jpype.pickle.Encoder')() self._builder = JUnserializer() self._dispatch = dispatch # Extension dispatch table holds reduce method self._call = self.reduce # Pure Python _Pickler uses get() def get(self, cls): if not issubclass(cls, (_jpype.JClass, _jpype.JObject)): return self._dispatch.get(cls) return self._call # Python3 cPickler uses __getitem__() def __getitem__(self, cls): if not issubclass(cls, (_jpype.JClass, _jpype.JObject)): return self._dispatch[cls] return self._call def reduce(self, obj): byte = bytes(self._encoder.pack(obj)) return (self._builder, (byte, )) class JPickler(pickle.Pickler): """Pickler overloaded to support Java objects Parameters: file: a file or other writeable object. *args: any arguments support by the native pickler. Raises: java.io.NotSerializableException: if a class is not serializable or one of its members java.io.InvalidClassException: an error occures in constructing a serialization. """ def __init__(self, file, *args, **kwargs): pickle.Pickler.__init__(self, file, *args, **kwargs) # In Python3 we need to hook into the dispatch table for extensions self.dispatch_table = _JDispatch(dispatch_table) class JUnpickler(pickle.Unpickler): """Unpickler overloaded to support Java objects Parameters: file: a file or other readable object. *args: any arguments support by the native unpickler. Raises: java.lang.ClassNotFoundException: if a serialized class is not found by the current classloader. java.io.InvalidClassException: if the serialVersionUID for the class does not match, usually as a result of a new jar version. java.io.StreamCorruptedException: if the pickle file has been altered or corrupted. """ def __init__(self, file, *args, **kwargs): self._decoder = _jpype.JClass('org.jpype.pickle.Decoder')() pickle.Unpickler.__init__(self, file, *args, **kwargs) def find_class(self, module, cls): """Specialization for Java classes. We just need to substitute the stub class for a real one which points to our decoder instance. """ if cls == "JUnserializer": decoder = self._decoder class JUnserializer(object): def __call__(self, *args): return decoder.unpack(args[0]) return JUnserializer return pickle.Unpickler.find_class(self, module, cls) jpype-1.6.0/jpype/protocol.py000066400000000000000000000113761501674766700162420ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import datetime import decimal import sys import _jpype from . import _jclass from . import _jcustomizer # Copies of all private base types for reference _JClass = _jpype._JClass _JObject = _jpype._JObject _JException = _jpype._JException _JNumberLong = _jpype._JNumberLong _JNumberFloat = _jpype._JNumberFloat _JComparable = _jpype._JComparable _JChar = _jpype._JChar _JBoolean = _jpype._JBoolean _JArray = _jpype._JArray _JBuffer = _jpype._JBuffer if sys.version_info < (3, 8): # pragma: no cover from typing_extensions import Protocol, runtime_checkable from typing import Sequence, Mapping, Set # lgtm [py/unused-import] from typing import SupportsFloat, Callable # lgtm [py/unused-import] @runtime_checkable class SupportsIndex(Protocol): def __index__(self) -> int: ... else: # 3.8 onward from typing import Protocol, runtime_checkable from typing import SupportsIndex, SupportsFloat # lgtm [py/unused-import] # lgtm [py/unused-import] from typing import Sequence, Mapping, Set, Callable # Types we need @runtime_checkable class SupportsPath(Protocol): def __fspath__(self) -> str: ... @_jcustomizer.JConversion("java.nio.file.Path", instanceof=SupportsPath) def _JPathConvert(jcls, obj): Paths = _jpype.JClass("java.nio.file.Paths") return Paths.get(obj.__fspath__()) @_jcustomizer.JConversion("java.io.File", instanceof=SupportsPath) def _JFileConvert(jcls, obj): return jcls(obj.__fspath__()) # To be added in 1.1.x @_jcustomizer.JConversion("java.lang.Iterable", instanceof=Sequence, excludes=str) @_jcustomizer.JConversion("java.util.Collection", instanceof=Sequence, excludes=str) def _JSequenceConvert(jcls, obj): return _jclass.JClass('java.util.Arrays').asList(obj) @_jcustomizer.JConversion("java.lang.Iterable", instanceof=Set) @_jcustomizer.JConversion("java.util.Collection", instanceof=Set) def _JSetConvert(jcls, obj): # set does not satisfy PySequence_Check and collection is too broad as it # would let dict be converted, so we are going to have to convert twice # for now return _jclass.JClass('java.util.Arrays').asList(list(obj)) @_jcustomizer.JConversion("java.util.Map", instanceof=Mapping) def _JMapConvert(jcls, obj): hm = _jclass.JClass('java.util.HashMap')() for p, v in obj.items(): hm[p] = v return hm # Converters start here @_jcustomizer.JConversion("java.time.Instant", exact=datetime.datetime) def _JInstantConversion(jcls, obj): utc = obj.replace(tzinfo=datetime.timezone.utc).timestamp() sec = int(utc) nsec = int((utc - sec) * 1e9) return jcls.ofEpochSecond(sec, nsec) # Types needed for SQL @_jcustomizer.JImplementationFor('java.sql.Date') class _JSQLDate: def _py(self): return datetime.date(self.getYear() + 1900, self.getMonth() + 1, self.getDate()) @_jcustomizer.JImplementationFor('java.sql.Time') class _JSQLTime: def _py(self): return datetime.time(self.getHours(), self.getMinutes(), self.getSeconds()) @_jcustomizer.JImplementationFor('java.sql.Timestamp') class _JDate: def _py(self): return datetime.datetime(self.getYear() + 1900, self.getMonth() + 1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), self.getNanos() // 1000) @_jcustomizer.JImplementationFor('java.math.BigDecimal') class _JBigDecimal: def _py(self): return decimal.Decimal(str(self)) @_jcustomizer.JConversion("java.sql.Time", instanceof=datetime.time) def _toTime(jcls, x): return jcls(x.hour, x.minute, x.second) @_jcustomizer.JConversion("java.sql.Date", instanceof=datetime.date) def _toDate(jcls, x): return jcls(x.year - 1900, x.month - 1, x.day) @_jcustomizer.JConversion("java.sql.Timestamp", instanceof=datetime.datetime) def _toTimestamp(jcls, x): return jcls(x.year - 1900, x.month - 1, x.day, x.hour, x.minute, x.second, x.microsecond * 1000) @_jcustomizer.JConversion("java.math.BigDecimal", instanceof=decimal.Decimal) def _toBigDecimal(jcls, x): return jcls(str(x)) jpype-1.6.0/jpype/types.py000066400000000000000000000043351501674766700155420ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** """ JPype Types Module ------------------ Optional module containing only the Java types and factories used by JPype. Classes in this module include ``JArray``, ``JClass``, ``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``, ``JString``, ``JObject``, and ``JException``. Example: .. code-block:: python from jpype.types import * """ # import package to get minimum types needed to use module. import _jpype from ._jclass import * from ._jobject import * from ._jarray import * from ._jexception import JException from ._jstring import * __all__ = [ 'JArray', 'JClass', 'JBoolean', 'JByte', 'JChar', 'JShort', 'JInt', 'JLong', 'JFloat', 'JDouble', 'JString', 'JObject', 'JException', ] class JBoolean(_jpype._JBoolean, internal=True): # type: ignore[call-arg] pass class JByte(_jpype._JNumberLong, internal=True): # type: ignore[call-arg] pass class JChar(_jpype._JChar, internal=True): # type: ignore[call-arg] pass class JInt(_jpype._JNumberLong, internal=True): # type: ignore[call-arg] pass class JShort(_jpype._JNumberLong, internal=True): # type: ignore[call-arg] pass class JLong(_jpype._JNumberLong, internal=True): # type: ignore[call-arg] pass class JFloat(_jpype._JNumberFloat, internal=True): # type: ignore[call-arg] pass class JDouble(_jpype._JNumberFloat, internal=True): # type: ignore[call-arg] pass _jpype.JChar = JChar jpype-1.6.0/lgtm.yml000066400000000000000000000005241501674766700143570ustar00rootroot00000000000000path_classifiers: test: - setup.py - setupext - test - examples docs: - doc extraction: cpp: index: build_command: - python3 setup.py build java: index: build_command: ant -f native/build.xml python: python_setup: version: "3" jpype-1.6.0/native/000077500000000000000000000000001501674766700141565ustar00rootroot00000000000000jpype-1.6.0/native/build.xml000066400000000000000000000024171501674766700160030ustar00rootroot00000000000000 jpype-1.6.0/native/build_coverage.xml000066400000000000000000000020611501674766700176510ustar00rootroot00000000000000 jpype-1.6.0/native/common/000077500000000000000000000000001501674766700154465ustar00rootroot00000000000000jpype-1.6.0/native/common/include/000077500000000000000000000000001501674766700170715ustar00rootroot00000000000000jpype-1.6.0/native/common/include/jp_array.h000066400000000000000000000042021501674766700210470ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPARRAY_H_ #define _JPARRAY_H_ #include "jp_javaframe.h" class JPArray; class JPArrayView { public: explicit JPArrayView(JPArray* array); JPArrayView(JPArray* array, jobject collection); ~JPArrayView(); void reference(); bool unreference(); public: JPArray *m_Array; void *m_Memory{}; Py_buffer m_Buffer{}; int m_RefCount; Py_ssize_t m_Shape[5]{}; Py_ssize_t m_Strides[5]{}; jboolean m_IsCopy{}; jboolean m_Owned{}; } ; /** * Class to wrap Java Class and provide low-level behavior */ class JPArray { friend class JPArrayView; public: explicit JPArray(const JPValue& array); JPArray(JPArray* cls, jsize start, jsize stop, jsize step); virtual~ JPArray(); JPArrayClass* getClass() { return m_Class; } jsize getLength() const; void setRange(jsize start, jsize length, jsize step, PyObject* val); JPPyObject getItem(jsize ndx); void setItem(jsize ndx, PyObject*); /** * Create a shallow copy of an array. * * This is used to extract a slice before calling or casting operations. * * @param frame * @param obj * @return */ jarray clone(JPJavaFrame& frame, PyObject* obj); bool isSlice() const { return m_Slice; } jarray getJava() { return m_Object.get(); } private: JPArrayClass* m_Class; JPArrayRef m_Object; jsize m_Start; jsize m_Step; jsize m_Length; bool m_Slice; } ; #endif // _JPARRAY_H_ jpype-1.6.0/native/common/include/jp_arrayclass.h000066400000000000000000000036601501674766700221040ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPARRAYCLASS_H_ #define _JPARRAYCLASS_H_ /** * Class to wrap Java Class and provide low-level behavior */ class JPArrayClass : public JPClass { public: JPArrayClass(JPJavaFrame& frame, jclass cls, const string& name, JPClass* superClass, JPClass* componentType, jint modifiers); ~ JPArrayClass() override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPValue newArray(JPJavaFrame& frame, int length); /** * Create a new java array containing a set of items take from * a range. * * This is used to support variable arguments. * * @param refs contains a vector of python objects. * @param start is the start of the range inclusive. * @param end is the end of the range exclusive. * @return a jvalue containing a java vector. */ jvalue convertToJavaVector(JPJavaFrame& frame, JPPyObjectVector& refs, jsize start, jsize end); virtual JPClass* getComponentType() { return m_ComponentType; } bool isArray() const override { return true; } private: JPClass* m_ComponentType; } ; #endif // _JPARRAYCLASS_H_ jpype-1.6.0/native/common/include/jp_booleantype.h000077500000000000000000000060371501674766700222650ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_BOOLEAN_TYPE_H_ #define _JP_BOOLEAN_TYPE_H_ class JPBooleanType : public JPPrimitiveType { public: JPBooleanType(); ~JPBooleanType() override; using type_t = jboolean; using array_t = jbooleanArray; static inline jboolean& field(jvalue& v) { return v.z; } static inline const jboolean& field(const jvalue& v) { return v.z; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'Z'; } // GCOVR_EXCL_START // These methods are required by primitive but are not used for a non // number type jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } // GCOVR_EXCL_STOP void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_BOOLEAN_TYPE_H_ jpype-1.6.0/native/common/include/jp_boxedtype.h000066400000000000000000000037041501674766700217420ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPBOXEDCLASS_H_ #define _JPBOXEDCLASS_H_ // Boxed types have special conversion rules so that they can convert // from python primitives. This code specializes the class wrappers // to make that happen. /** * Class to wrap for Boxed types. * * These are linked to primitives. * They specialize the conversion rules to set up our table for conversion. */ class JPBoxedType : public JPClass { public: JPBoxedType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers, JPPrimitiveType* primitiveType); ~JPBoxedType() override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPrimitiveType* getPrimitive() { return m_PrimitiveType; } jobject box(JPJavaFrame &frame, jvalue v); JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; protected: JPPrimitiveType* m_PrimitiveType; public: jmethodID m_CtorID; jmethodID m_DoubleValueID; jmethodID m_FloatValueID; jmethodID m_IntValueID; jmethodID m_LongValueID; jmethodID m_BooleanValueID; jmethodID m_CharValueID; } ; #endif // _JPBOXEDCLASS_H_jpype-1.6.0/native/common/include/jp_buffer.h000066400000000000000000000024641501674766700212120ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPBUFFER_H_ #define _JPBUFFER_H_ #include "jp_javaframe.h" class JPBufferType; /** * Class to wrap Java Class and provide low-level behavior */ class JPBuffer { public: explicit JPBuffer(const JPValue& array); virtual~ JPBuffer(); JPBufferType* getClass() { return m_Class; } jobject getJava() { return m_Object.get(); } bool isReadOnly() const; Py_buffer& getView(); bool isValid() const; private: JPBufferType* m_Class; JPObjectRef m_Object; void *m_Address; Py_ssize_t m_Capacity; Py_buffer m_Buffer{}; char m_Format[3]{}; } ; #endif // _JPBUFFER_H_jpype-1.6.0/native/common/include/jp_buffertype.h000066400000000000000000000024531501674766700221120ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPBUFFERTYPE_H_ #define _JPBUFFERTYPE_H_ /** * Class to wrap Java Class and provide low-level behavior */ class JPBufferType : public JPClass { public: JPBufferType(JPJavaFrame& frame, jclass cls, const string& name, JPClass* superClass, const JPClassList& interfaces, jint modifiers); ~ JPBufferType() override; const char* getType() { return m_Type; } int getSize() const { return m_Size; } JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) override; private: const char* m_Type; int m_Size; } ; #endif // _JPBUFFERCLASS_H_jpype-1.6.0/native/common/include/jp_bytetype.h000077500000000000000000000062001501674766700216010ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPBYTE_TYPE_H_ #define _JPBYTE_TYPE_H_ class JPByteType : public JPPrimitiveType { public: JPByteType(); ~JPByteType() override; using type_t = jbyte; using array_t = jbyteArray; static inline jbyte& field(jvalue& v) { return v.b; } static inline const jbyte& field(const jvalue& v) { return v.b; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'B'; } // GCOVR_EXCL_START // Required but not exercised currently jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } // GCOVR_EXCL_STOP jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } template static T assertRange(const T& l) { if (l < -128 || l > 127) { JP_RAISE(PyExc_OverflowError, "Cannot convert value to Java byte"); } return l; } void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JPBYTE_TYPE_H_ jpype-1.6.0/native/common/include/jp_chartype.h000077500000000000000000000060201501674766700215530ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_CHAR_TYPE_H_ #define _JP_CHAR_TYPE_H_ class JPCharType : public JPPrimitiveType { public: JPCharType(); ~JPCharType() override; using type_t = jchar; using array_t = jcharArray; JPValue newInstance(JPJavaFrame& frame, JPPyObjectVector& args) override; inline jchar& field(jvalue& v) { return v.c; } inline jchar field(const jvalue& v) const { return v.c; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'C'; } // GCOVR_EXCL_START // Required but not exercised currently jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } // GCOVR_EXCL_STOP jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP-CHAR_TYPE_H_ jpype-1.6.0/native/common/include/jp_class.h000066400000000000000000000124361501674766700210460ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_CLASS_H_ #define _JP_CLASS_H_ #include "jp_modifier.h" class JPClass : public JPResource { public: // Special entry point for JVM independent entities JPClass(const string& name, jint modifiers); JPClass(JPJavaFrame& context, jclass clss, const string& name, JPClass* super, const JPClassList& interfaces, jint modifiers); ~JPClass() override; void setHost(PyObject* host); PyTypeObject* getHost() { return (PyTypeObject*) m_Host.get(); } void setHints(PyObject* host); PyObject* getHints(); public: void ensureMembers(JPJavaFrame& frame); jclass getJavaClass() const; void assignMembers(JPMethodDispatch* ctor, JPMethodDispatchList& methods, JPFieldList& fields); string toString() const; string getCanonicalName() const { return m_CanonicalName; } string getName() const; bool isAbstract() const { return JPModifier::isAbstract(m_Modifiers); } bool isFinal() const { return JPModifier::isFinal(m_Modifiers); } bool isThrowable() const { return JPModifier::isThrowable(m_Modifiers); } bool isInterface() const { return JPModifier::isInterface(m_Modifiers); } virtual bool isArray() const { return false; } jlong getModifiers() { return m_Modifiers; } virtual bool isPrimitive() const { return false; } virtual bool isPrimitiveArray() const { return JPModifier::isPrimitiveArray(m_Modifiers); } JPMethodDispatch* getCtor() { return m_Constructors; } const JPMethodDispatchList& getMethods() { return m_Methods; } const JPFieldList& getFields() { return m_Fields; } /** * Determine if a Python object will convert to this java type. * * This is used to determine which overload is the best match. * * @param pyobj is the Python object. * @return the quality of the match */ virtual JPMatch::Type findJavaConversion(JPMatch& match); virtual void getConversionInfo(JPConversionInfo &info); /** Create a new Python object to wrap a Java value. * * Some conversion convert to a Python type such as Java boolean and char. * Null pointers match to Python None. Objects convert automatically to * the most derived type. To disable this behavior the cast option can be * specified. * * @param cast force the wrapper to be the defined type. * @return a new Python object. */ virtual JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast); /** * Get the Java value representing as an object. * * This will unbox if the type is a primitive. * * @return a java value with class. */ virtual JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj); /** * Call a static method that returns this type of object. */ virtual JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*); /** * Call a method that returns this type of object. */ virtual JPPyObject invoke(JPJavaFrame& frame, jobject, jclass clazz, jmethodID, jvalue*); /** * Get a static field that returns this type. * * @param frame is the frame to hold the local reference. * @param cls is the class holding the static field. * @param fid is the field id. * @return */ virtual JPPyObject getStaticField(JPJavaFrame& frame, jclass cls, jfieldID fid); virtual void setStaticField(JPJavaFrame& frame, jclass cls, jfieldID fid, PyObject* val); virtual JPPyObject getField(JPJavaFrame& frame, jobject obj, jfieldID fid); virtual void setField(JPJavaFrame& frame, jobject obj, jfieldID fid, PyObject* val); JPClass* newArrayType(JPJavaFrame &frame, long d); virtual jarray newArrayOf(JPJavaFrame& frame, jsize size); virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject* vals); virtual JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx); virtual void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val); /** * Expose IsAssignableFrom to python. */ virtual bool isAssignableFrom(JPJavaFrame& frame, JPClass* o); // Object properties JPClass* getSuperClass() { return m_SuperClass; } virtual JPValue newInstance(JPJavaFrame& frame, JPPyObjectVector& args); const JPClassList& getInterfaces() { return m_Interfaces; } protected: JPClassRef m_Class; JPClass* m_SuperClass; JPClassList m_Interfaces; JPMethodDispatch* m_Constructors; JPMethodDispatchList m_Methods; JPFieldList m_Fields; string m_CanonicalName; jint m_Modifiers; JPPyObject m_Host; JPPyObject m_Hints; } ; #endif // _JPPOBJECTTYPE_H_ jpype-1.6.0/native/common/include/jp_classhints.h000066400000000000000000000072251501674766700221140ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_CLASSHINTS_H #define JP_CLASSHINTS_H class JPConversion { public: virtual ~JPConversion(); virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) = 0; virtual void getInfo(JPClass *cls, JPConversionInfo &info) = 0; virtual jvalue convert(JPMatch &match) = 0; } ; class JPIndexConversion : public JPConversion { public: void getInfo(JPClass *cls, JPConversionInfo &info) override; } ; class JPNumberConversion : public JPIndexConversion { public: void getInfo(JPClass *cls, JPConversionInfo &info) override; } ; class JPConversionJavaValue : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override; void getInfo(JPClass *cls, JPConversionInfo &info) override; jvalue convert(JPMatch &match) override; } ; class JPClassHints { public: JPClassHints(); ~JPClassHints(); /** Get the conversion of this type. * * Searches the list for a conversion. The first conversion better than * explicit is returned immediately. * * @returns the quality of the match */ JPMatch::Type getConversion(JPMatch& match, JPClass *cls); /** * Add a conversion based on a specified attribute. * * If the attribute is found in in the object, it is assumed to be a match. * This is for "duck type" conversions. The Python routine must return * something that holds a __javavalue__ which will be used as the converted * object. * * @param attribute is the attribute to search for. * @param method is a Python routine to call to complete the conversion * process. */ void addAttributeConversion(const string& attribute, PyObject* method); /** * Add a type conversion based on the Python type. * * The Python routine must return something that holds a __javavalue__ * which will be used as the converted object. * * @param type is a Python type object * @param method is a Python routine to call to complete the conversion * process. * @param exact require the type to be an exact match. */ void addTypeConversion(PyObject* type, PyObject* method, bool exact); void excludeConversion(PyObject* type); void getInfo(JPClass *cls, JPConversionInfo &info); bool m_ConvertJava; private: std::list conversions; } ; extern JPConversion *hintsConversion; extern JPConversion *charArrayConversion; extern JPConversion *byteArrayConversion; extern JPConversion *bufferConversion; extern JPConversion *sequenceConversion; extern JPConversion *nullConversion; extern JPConversion *classConversion; extern JPConversion *objectConversion; extern JPConversion *javaObjectAnyConversion; extern JPConversion *javaNumberAnyConversion; extern JPConversion *javaValueConversion; extern JPConversion *stringConversion; extern JPConversion *boxConversion; extern JPConversion *boxBooleanConversion; extern JPConversion *boxLongConversion; extern JPConversion *boxDoubleConversion; extern JPConversion *unboxConversion; extern JPConversion *proxyConversion; #endif /* JP_CLASSHINTS_H */ jpype-1.6.0/native/common/include/jp_classloader.h000066400000000000000000000032521501674766700222310ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPCLASSLOADER_H_ #define _JPCLASSLOADER_H_ #include "jp_class.h" /** * The class loader is reponsible for loading classes within JPype. * * Depending on the settings it may either use an internal boot loader * to get the jar from within the _jpype module, or it may use the * system class loader. */ class JPClassLoader { public: /** Initialize the class loader. */ explicit JPClassLoader(JPJavaFrame& frame); /** Load a class by name from the jpype.jar. * * String is specified as a Java binary name. * https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#name * * @returns the class loaded * @throws RuntimeException if the class is not found. */ jclass findClass(JPJavaFrame& frame, const string& name); // Classloader for Proxy jobject getBootLoader(); private: JPClassRef m_ClassClass; JPObjectRef m_SystemClassLoader; JPObjectRef m_BootLoader; jmethodID m_ForNameID; } ; #endif // _JPCLASSLOADER_H_ jpype-1.6.0/native/common/include/jp_classtype.h000066400000000000000000000025611501674766700217460ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPCLASSTYPE_H_ #define _JPCLASSTYPE_H_ /** * Wrapper for Class * * Class wrappers need to be able to cast to this type, * thus we need a specialized version. * This class should not be used outside of * the JPTypeManager. */ class JPClassType : public JPClass { public: JPClassType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers); ~ JPClassType() override; public: // JPClass implementation JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; } ; #endif // _JPCLASSTYPE_H_jpype-1.6.0/native/common/include/jp_context.h000066400000000000000000000202151501674766700214170ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_CONTEXT_H #define JP_CONTEXT_H #include #include /** JPClass is a bit heavy when we just need to hold a * class reference. It causes issues during bootstrap. Thus we * need a lightweight reference to a jclass. */ template class JPRef { private: jref m_Ref; public: JPRef() { m_Ref = 0; } JPRef(jref obj) { m_Ref = 0; JPJavaFrame frame = JPJavaFrame::outer(); m_Ref = (jref) frame.NewGlobalRef((jobject) obj); } JPRef(JPJavaFrame& frame, jref obj) { m_Ref = 0; m_Ref = (jref) frame.NewGlobalRef((jobject) obj); } JPRef(const JPRef& other); ~JPRef(); JPRef& operator=(const JPRef& other); jref get() const { return m_Ref; } } ; using JPClassRef = JPRef; using JPObjectRef = JPRef; using JPArrayRef = JPRef; using JPThrowableRef = JPRef; class JPStackInfo; class JPGarbageCollection; void assertJVMRunning(JPContext* context, const JPStackInfo& info); int hasInterrupt(); /** * A Context encapsulates the Java virtual machine, the Java classes required * to function, and the JPype services created for that machine. * * Frames, Environments and Contexts are different concepts. * - Java context is shared with all objects that exist in a virtual machine. * - Java environment exists for each thread for each machine. * - Java frames exist in the stack holding the local variables that * method. * Frames and Environments should never be held longer than the duration of * a method. * * The members in the Context are broken into * - JVM control functions * - JPype services * - Java functions * - Java type resources * * There are two critical phases in the context lifespan where things * are most likely to go wrong. During JVM startup, many vital functions are * not yet fully configured and thus an exception issued during that period * can lead to using a resource that is not yet configured. * * Second shutdown is a vital period. If the user calls shutdown from within * a callback in which there are still JPype context elements on the call * stack, it can lead to a crash. * * After shutdown, the JVM is set to NULL and all actions should assert that * it is running and thus fail. Aside from the context and the services, all * other JPype C++ resources are owned by Java. Java will delete them as needed. * The context itself belongs to Python. */ class JPContext { public: friend class JPJavaFrame; friend class JPypeException; friend class JPClass; JPContext(); virtual ~JPContext(); JPContext(const JPContext& orig) = delete; // JVM control functions bool isRunning(); void startJVM(const string& vmPath, const StringVector& args, bool ignoreUnrecognized, bool convertStrings, bool interrupt); void attachJVM(JNIEnv* env); void initializeResources(JNIEnv* env, bool interrupt); void shutdownJVM(bool destroyJVM, bool freeJVM); void attachCurrentThread(); void attachCurrentThreadAsDaemon(); bool isThreadAttached(); void detachCurrentThread(); JNIEnv* getEnv(); JavaVM* getJavaVM() { return m_JavaVM; } jobject getJavaContext() { return m_JavaContext.get(); } /** Release a global reference checking for shutdown. * * This should be used in any calls to release resources from a destructor. * It cannot fail even if the JVM is no longer operating. */ void ReleaseGlobalRef(jobject obj); // JPype services JPTypeManager* getTypeManager() { return m_TypeManager; } JPClassLoader* getClassLoader() { return m_ClassLoader; } bool getConvertStrings() const { return m_ConvertStrings; } // Java type resources JPPrimitiveType* _void{}; JPPrimitiveType* _boolean{}; JPPrimitiveType* _byte{}; JPPrimitiveType* _char{}; JPPrimitiveType* _short{}; JPPrimitiveType* _int{}; JPPrimitiveType* _long{}; JPPrimitiveType* _float{}; JPPrimitiveType* _double{}; JPBoxedType* _java_lang_Void{}; JPBoxedType* _java_lang_Boolean{}; JPBoxedType* _java_lang_Byte{}; JPBoxedType* _java_lang_Character{}; JPBoxedType* _java_lang_Short{}; JPBoxedType* _java_lang_Integer{}; JPBoxedType* _java_lang_Long{}; JPBoxedType* _java_lang_Float{}; JPBoxedType* _java_lang_Double{}; JPClass* _java_lang_Object{}; JPClass* _java_lang_Class{}; JPClass* _java_lang_reflect_Field{}; JPClass* _java_lang_reflect_Method{}; JPClass* _java_lang_Throwable{}; JPStringType* _java_lang_String{}; JPClass* _java_nio_ByteBuffer{}; private: void loadEntryPoints(const string& path); jint(JNICALL * CreateJVM_Method)(JavaVM **pvm, void **penv, void *args){}; jint(JNICALL * GetCreatedJVMs_Method)(JavaVM **pvm, jsize size, jsize * nVms){}; private: JavaVM *m_JavaVM{}; // Java half JPObjectRef m_JavaContext; // Services JPTypeManager *m_TypeManager{}; JPClassLoader *m_ClassLoader{}; public: JPClassRef m_ContextClass; JPClassRef m_RuntimeException; private: JPClassRef m_Array; JPObjectRef m_Reflector; // Java Functions jmethodID m_Object_ToStringID{}; jmethodID m_Object_EqualsID{}; jmethodID m_Object_HashCodeID{}; jmethodID m_CallMethodID{}; jmethodID m_Class_GetNameID{}; jmethodID m_Context_collectRectangularID{}; jmethodID m_Context_assembleID{}; jmethodID m_String_ToCharArrayID{}; jmethodID m_Context_CreateExceptionID{}; jmethodID m_Context_GetExcClassID{}; jmethodID m_Context_GetExcValueID{}; jmethodID m_Context_ClearInterruptID{}; jmethodID m_CompareToID{}; jmethodID m_Buffer_IsReadOnlyID{}; jmethodID m_Buffer_AsReadOnlyID{}; jmethodID m_Context_OrderID{}; jmethodID m_Object_GetClassID{}; jmethodID m_Array_NewInstanceID{}; jmethodID m_Throwable_GetCauseID{}; jmethodID m_Throwable_GetMessageID{}; jmethodID m_Context_GetFunctionalID{}; friend class JPProxy; JPClassRef m_ProxyClass; jmethodID m_Proxy_NewID{}; jmethodID m_Proxy_NewInstanceID{}; jmethodID m_Context_IsPackageID{}; jmethodID m_Context_GetPackageID{}; jmethodID m_Package_GetObjectID{}; jmethodID m_Package_GetContentsID{}; jmethodID m_Context_NewWrapperID{}; public: jmethodID m_Context_GetStackFrameID{}; void onShutdown(); private: bool m_Running{}; bool m_ConvertStrings{}; bool m_Embedded; public: JPGarbageCollection *m_GC; // This will gather C++ resources to clean up after shutdown. std::list m_Resources; } ; extern "C" JPContext* JPContext_global; extern void JPRef_failed(); // GCOVR_EXCL_START // Not currently used template JPRef::JPRef(const JPRef& other) { if (JPContext_global != nullptr) { JPJavaFrame frame = JPJavaFrame::external(JPContext_global->getEnv()); m_Ref = (jref) frame.NewGlobalRef((jobject) other.m_Ref); } else { JPRef_failed(); } } // GCOVR_EXCL_STOP template JPRef::~JPRef() { if (m_Ref != 0) { JPContext_global->ReleaseGlobalRef((jobject) m_Ref); } } template JPRef& JPRef::operator=(const JPRef& other) { if (other.m_Ref == m_Ref) return *this; // m_Context may or may not be set up here, so we need to use a // different frame for unreferencing and referencing if (JPContext_global != nullptr && m_Ref != 0) { // GCOVR_EXCL_START // This code is not currently used. JPJavaFrame frame = JPJavaFrame::external(JPContext_global->getEnv()); if (m_Ref != 0) frame.DeleteGlobalRef((jobject) m_Ref); } // GCOVR_EXCL_STOP m_Ref = other.m_Ref; if (JPContext_global != nullptr && m_Ref != 0) { JPJavaFrame frame = JPJavaFrame::external(JPContext_global->getEnv()); m_Ref = (jref) frame.NewGlobalRef((jobject) m_Ref); } return *this; } #endif /* JP_CONTEXT_H */ jpype-1.6.0/native/common/include/jp_doubletype.h000077500000000000000000000057561501674766700221270ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_DOUBLE_TYPE_H_ #define _JP_DOUBLE_TYPE_H_ class JPDoubleType : public JPPrimitiveType { public: JPDoubleType(); ~JPDoubleType() override = default; using type_t = jdouble; using array_t = jdoubleArray; static inline jdouble& field(jvalue& v) { return v.d; } static inline const jdouble& field(const jvalue& v) { return v.d; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'D'; } // GCOVR_EXCL_START // Required but not exercised currently jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } // GCOV_EXCL_STOP void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_DOUBLE_TYPE_H_ jpype-1.6.0/native/common/include/jp_encoding.h000066400000000000000000000036541501674766700215310ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_ENCODING_H_ #define _JP_ENCODING_H_ #include #include #include class JPEncoding { public: JPEncoding() = default; virtual ~JPEncoding(); /** Store a code point in an outgoing buffer. */ virtual void encode(std::ostream& out, unsigned int codePoint) const = 0; /** Retrieve a coding point from an incoming buffer. */ virtual unsigned int fetch(std::istream& is) const = 0; } ; class JPEncodingUTF8 : public JPEncoding { public: /** Store a code point in an outgoing buffer. */ void encode(std::ostream& out, unsigned int codePoint) const override; /** Retrieve a coding point from an incoming buffer. */ unsigned int fetch(std::istream& is) const override; } ; class JPEncodingJavaUTF8 : public JPEncoding { public: /** Store a code point in an outgoing buffer. */ void encode(std::ostream& out, unsigned int codePoint) const override; /** Retrieve a coding point from an incoming buffer. * returns the next coding point or -1 on invalid coding. */ unsigned int fetch(std::istream& is) const override; } ; std::string transcribe(const char* in, size_t len, const JPEncoding& sourceEncoding, const JPEncoding& targetEncoding); #endif // _JP_ENCODING_H_jpype-1.6.0/native/common/include/jp_exception.h000066400000000000000000000114631501674766700217360ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_EXCEPTION_H_ #define _JP_EXCEPTION_H_ /* All exception are passed as JPypeException. The type of the exception * is specified at creation. Exceptions may be of type * - _java_error - exception generated from within java. * - _python_error - exception generated from within python. * - _runtime_error - Failure that will issue a runtime error in python and java. * - _type_error - Failure that will issue a type error in python. * * We must throw the correct exception so that it can properly be handled * when returning to the native code. * * If we are returning to python, and it is a * - _python_error, then we assume that a python exception has already been * placed in the python virtual machine. * - _java_error, then we will convert it to a python object with the correct * object type. * - otherwise, then we will convert it to the requested python error. * * If we are returning to java, and it is a * - _java_error, then we assume there is already a Java exception queue * in the virtual machine. * - otherwise convert to a RuntimeException. * */ #include #ifndef __FUNCTION_NAME__ #ifdef WIN32 //WINDOWS #define __FUNCTION_NAME__ __FUNCTION__ #else //*NIX #define __FUNCTION_NAME__ __func__ #endif #endif /** * This is the type of the exception to issue. */ enum JPError { _java_error, _python_error, _python_exc, _os_error_unix, _os_error_windows, }; // Create a stackinfo for a particular location in the code that can then // be passed to the handler routine for auditing. #define JP_STACKINFO() JPStackInfo(__FUNCTION_NAME__, __FILE__, __LINE__) // Macro to use when hardening code // Most of these will be removed after core is debugged, but // a few are necessary to handle off normal conditions. #define ASSERT_NOT_NULL(X) {if ((X)==NULL) { JP_RAISE(PyExc_RuntimeError, "Null Pointer Exception");} } // Macro to add stack trace info when multiple paths lead to the same trouble spot #define JP_CATCH catch (JPypeException& ex) { ex.from(JP_STACKINFO()); throw; } /** Structure to pass around the location within a C++ source file. */ class JPStackInfo { const char* function_; const char* file_; int line_; public: JPStackInfo(const char* function, const char* file, int line) : function_(function), file_(file), line_(line) { } const char* getFunction() const { return function_; } const char* getFile() const { return file_; } int getLine() const { return line_; } } ; using JPStackTrace = vector; typedef union { int i; void* l; } JPErrorUnion; /** * Exception issued by JPype to indicate an internal problem. * * This is primarily focused on transferring exception handling * to Python as the majority of errors are reported there. * */ class JPypeException : std::runtime_error { public: JPypeException(JPJavaFrame &frame, jthrowable, const JPStackInfo& stackInfo); JPypeException(int type, void* error, const JPStackInfo& stackInfo); JPypeException(int type, void* error, const string& msn, const JPStackInfo& stackInfo); JPypeException(int type, const string& msn, int error, const JPStackInfo& stackInfo); // The copy constructor for an object thrown as an exception must be declared noexcept, including any implicitly-defined copy constructors. // Any function declared noexcept that terminates by throwing an exception violates ERR55-CPP. Honor exception specifications. JPypeException(const JPypeException &ex) noexcept; JPypeException& operator = (const JPypeException& ex); ~JPypeException() override = default; void from(const JPStackInfo& info); void convertJavaToPython(); void convertPythonToJava(); /** Transfer handling of this exception to python. * * This should appear in the catch block whenever we return to python. * */ void toPython(); /** Transfer handling of this exception to java. */ void toJava(); int getExceptionType() const { return m_Type; } jthrowable getThrowable() { return m_Throwable.get(); } private: int m_Type; JPErrorUnion m_Error{}; JPStackTrace m_Trace; JPThrowableRef m_Throwable; std::string m_Message; }; #endif jpype-1.6.0/native/common/include/jp_field.h000066400000000000000000000035411501674766700210210ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPFIELD_H_ #define _JPFIELD_H_ /** * Field object */ class JPField { public: /** * Create a new field based on class and java.lang.Field object */ JPField(JPJavaFrame& frame, JPClass *cls, const string& name, jobject field, jfieldID fid, JPClass *fieldType, jint modifiers); /** * destructor */ virtual ~JPField(); // disallow copying. JPField(const JPField&) = delete; JPField& operator=(const JPField&) = delete; jobject getJavaObject() { return this->m_Field.get(); } const string& getName() const { return m_Name; } JPPyObject getStaticField(); void setStaticField(PyObject *pyobj); JPPyObject getField(jobject inst); void setField(jobject inst, PyObject *pyobj); bool isFinal() const { return JPModifier::isFinal(m_Modifiers); } bool isStatic() const { return JPModifier::isStatic(m_Modifiers); } JPClass *getClass() const { return m_Class; } private: string m_Name; JPClass* m_Class; JPObjectRef m_Field; jfieldID m_FieldID; JPClass* m_Type; jint m_Modifiers; } ; #endif // _JPFIELD_H_ jpype-1.6.0/native/common/include/jp_floattype.h000077500000000000000000000057451501674766700217600ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_FLOAT_TYPE_H_ #define _JP_FLOAT_TYPE_H_ class JPFloatType : public JPPrimitiveType { public: JPFloatType(); ~JPFloatType() override; using type_t = jfloat; using array_t = jfloatArray; static inline jfloat& field(jvalue& v) { return v.f; } static inline const jfloat& field(const jvalue& v) { return v.f; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'F'; } // GCOVR_EXCL_START // This is required, but is not currently used. jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } // GCOVR_EXCL_STOP jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_FLOAT_TYPE_H_ jpype-1.6.0/native/common/include/jp_functional.h000066400000000000000000000023141501674766700220750ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_FUNCTIONAL_H #define JP_FUNCTIONAL_H class JPFunctional : public JPClass { public: JPFunctional(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers); ~JPFunctional() override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; string getMethod() { return m_Method; } protected: string m_Method; } ; #endif /* JP_FUNCTIONAL_H */jpype-1.6.0/native/common/include/jp_gc.h000066400000000000000000000033251501674766700203270ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_GC_H #define JP_GC_H struct JPGCStats { long long python_rss; long long java_rss; long long current_rss; long long max_rss; long long min_rss; long long python_triggered; } ; class JPGarbageCollection { public: explicit JPGarbageCollection(); void init(JPJavaFrame& frame); void shutdown(); void triggered(); /** * Called when Python starts it Garbage collector */ void onStart(); /** * Called when Python finishes it Garbage collector */ void onEnd(); void getStats(JPGCStats& stats); private: bool running; bool in_python_gc; bool java_triggered; PyObject *python_gc; jclass _SystemClass; jclass _ContextClass; jmethodID _gcMethodID; jmethodID _totalMemoryID; jmethodID _freeMemoryID; jmethodID _maxMemoryID; jmethodID _usedMemoryID; jmethodID _heapMemoryID; size_t last_python; size_t last_java; size_t low_water; size_t high_water; size_t limit; size_t last; int java_count; int python_count; int python_triggered; } ; #endif /* JP_GC_H */ jpype-1.6.0/native/common/include/jp_inttype.h000077500000000000000000000062001501674766700214300ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_INT_TYPE_H_ #define _JP_INT_TYPE_H_ class JPIntType : public JPPrimitiveType { public: JPIntType(); ~JPIntType() override; using type_t = jint; using array_t = jintArray; static inline jint& field(jvalue& v) { return v.i; } static inline const jint& field(const jvalue& v) { return v.i; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'I'; } // GCOVR_EXCL_START // Required but not exercised currently jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } // GCOVR_EXCL_STOP jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } static jlong assertRange(const jlong& l) { if (l < -2147483648ll || l > 2147483647ll) { JP_RAISE(PyExc_OverflowError, "Cannot convert value to Java int"); } return l; } void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_INT_TYPE_H_ jpype-1.6.0/native/common/include/jp_javaframe.h000066400000000000000000000357511501674766700217020ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_JAVA_FRAME_H_ #define _JP_JAVA_FRAME_H_ /** A Java Frame represents a memory managed scope in which * java objects can be manipulated. * * Any resources created within a Java frame will be automatically * deallocated at when the frame falls out of scope unless captured * by a global reference. * * This should be used around all entry points from python that * call java code. Failure may lead to local references not being * released. Methods will large numbers of local references * should allocate a local frame. At most one local reference * from a local frame can be kept. Addition must use global referencing. * * JavaFrames are created to hold a certain number of items in * scope at time. They will grow automatically if more items are * created, but this has overhead. For most cases the default * will be fine. However, when working with an array in which * the number of items in scope can be known in advance, it is * good to size the frame appropriately. * * A JavaFrame should not be used in a destructor as it can * throw. The most common use of JavaFrame is to delete a * global reference. See the ReleaseGlobalReference for * this purpose. */ static const int LOCAL_FRAME_DEFAULT = 8; class JPJavaFrame { JNIEnv* m_Env; bool m_Popped; bool m_Outer; private: JPJavaFrame(JNIEnv* env, int size, bool outer); public: /** Create a new JavaFrame when called from Python. * * This method will automatically attach the thread * if it is not already attached. * * @param size determines how many objects can be * created in this scope without additional overhead. * * @throws JPypeException if the jpype cannot * acquire an env handle to work with jvm. */ static JPJavaFrame outer(int size = LOCAL_FRAME_DEFAULT) { return {nullptr, size, true}; } /** Create a new JavaFrame when called internal when * there is an existing frame. * * @param size determines how many objects can be * created in this scope without additional overhead. * * @throws JPypeException if the jpype cannot * acquire an env handle to work with jvm. */ static JPJavaFrame inner(int size = LOCAL_FRAME_DEFAULT) { return {nullptr, size, false}; } /** Create a new JavaFrame when called from Java. * * The thread was attached by definition. * * @param size determines how many objects can be * created in this scope without additional overhead. * * @throws JPypeException if the jpype cannot * acquire an env handle to work with jvm. */ static JPJavaFrame external(JNIEnv* env, int size = LOCAL_FRAME_DEFAULT) { return {env, size, false}; } JPJavaFrame(const JPJavaFrame& frame); /** Exit the local scope and clean up all java * objects. * * Only the one local object passed to outer scope * by the keep method will be kept alive. */ ~JPJavaFrame(); JPContext* getContext(); void check(); /** Exit the local frame and keep a local reference to an object * * This must be called only once when the frame is about to leave * scope. Any local references other than the one that is kept * are destroyed. If the next line is not "return", you are using * this incorrectly. * * Further calls to the frame will still suceed as we do not * check for operation on a closed frame, but is not advised. */ jobject keep(jobject); /** Create a new global reference to a java object. * * This java reference may be held in a class until the object is * no longer needed. It should be deleted with DeleteGlobalRef * or ReleaseGlobalRef. */ jobject NewGlobalRef(jobject obj); /** Delete a global reference. */ void DeleteGlobalRef(jobject obj); /** Create a new local reference. * * This is only used when promoting a WeakReference to * a local reference. */ jobject NewLocalRef(jobject obj); /** Prematurely delete a local reference. * * This is used when processing an array to keep * the objects in scope from growing. */ void DeleteLocalRef(jobject obj); jweak NewWeakGlobalRef(jobject obj); void DeleteWeakGlobalRef(jweak obj); JNIEnv* getEnv() const { return m_Env; } string toString(jobject o); string toStringUTF8(jstring str); bool equals(jobject o1, jobject o2); jint hashCode(jobject o); jobject collectRectangular(jarray obj); jobject assemble(jobject dims, jobject parts); jobject newArrayInstance(jclass c, jintArray dims); jthrowable getCause(jthrowable th); jstring getMessage(jthrowable th); jint compareTo(jobject obj, jobject obj2); /** * Convert a UTF8 encoded string into Java. * * This returns a local reference. * @param str * @return */ jstring fromStringUTF8(const string& str); jobject callMethod(jobject method, jobject obj, jobject args); jobject toCharArray(jstring jstr); string getFunctional(jclass c); JPClass *findClass(jclass obj); JPClass *findClassByName(const string& name); JPClass *findClassForObject(jobject obj); // not implemented JPJavaFrame& operator= (const JPJavaFrame& frame) = delete; private: jint PushLocalFrame(jint); jobject PopLocalFrame(jobject); public: bool ExceptionCheck(); void ExceptionDescribe(); void ExceptionClear(); jthrowable ExceptionOccurred(); jint ThrowNew(jclass clazz, const char* msg); jint Throw(jthrowable th); jobject NewDirectByteBuffer(void* address, jlong capacity); /** NewObjectA */ jobject NewObjectA(jclass a0, jmethodID a1, jvalue* a2); // Monitor int MonitorEnter(jobject a0); int MonitorExit(jobject a0); jclass FindClass(const string& a0); jclass DefineClass(const char* a0, jobject a1, const jbyte* a2, jsize a3); jint RegisterNatives(jclass a0, const JNINativeMethod* a1, jint a2); jboolean IsInstanceOf(jobject a0, jclass a1); jboolean IsAssignableFrom(jclass a0, jclass a1); jsize GetArrayLength(jarray a0); jobject GetObjectArrayElement(jobjectArray a0, jsize a1); jfieldID FromReflectedField(jobject a0); jfieldID GetFieldID(jclass a0, const char* a1, const char* a2); jfieldID GetStaticFieldID(jclass a0, const char* a1, const char* a2); jmethodID FromReflectedMethod(jobject a0); jmethodID GetMethodID(jclass a0, const char* a1, const char* a2); jmethodID GetStaticMethodID(jclass a0, const char* a1, const char* a2); // Void void CallStaticVoidMethodA(jclass a0, jmethodID a1, jvalue* a2); void CallVoidMethodA(jobject a0, jmethodID a1, jvalue* a2); void CallNonvirtualVoidMethodA(jobject a0, jclass a1, jmethodID a2, jvalue* a3); // Bool jboolean GetStaticBooleanField(jclass clazz, jfieldID fid); jboolean GetBooleanField(jobject clazz, jfieldID fid); void SetStaticBooleanField(jclass clazz, jfieldID fid, jboolean val); void SetBooleanField(jobject clazz, jfieldID fid, jboolean val); jboolean CallStaticBooleanMethodA(jclass clazz, jmethodID mid, jvalue* val); jboolean CallBooleanMethodA(jobject obj, jmethodID mid, jvalue* val); jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jbooleanArray NewBooleanArray(jsize len); void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* vals); void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* vals); jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy); void ReleaseBooleanArrayElements(jbooleanArray, jboolean* v, jint mode); // Byte jbyte GetStaticByteField(jclass clazz, jfieldID fid); jbyte GetByteField(jobject clazz, jfieldID fid); void SetStaticByteField(jclass clazz, jfieldID fid, jbyte val); void SetByteField(jobject clazz, jfieldID fid, jbyte val); jbyte CallStaticByteMethodA(jclass clazz, jmethodID mid, jvalue* val); jbyte CallByteMethodA(jobject obj, jmethodID mid, jvalue* val); jbyte CallNonvirtualByteMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jbyteArray NewByteArray(jsize len); void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* vals); void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* vals); jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy); void ReleaseByteArrayElements(jbyteArray, jbyte* v, jint mode); // Char jchar GetStaticCharField(jclass clazz, jfieldID fid); jchar GetCharField(jobject clazz, jfieldID fid); void SetStaticCharField(jclass clazz, jfieldID fid, jchar val); void SetCharField(jobject clazz, jfieldID fid, jchar val); jchar CallStaticCharMethodA(jclass clazz, jmethodID mid, jvalue* val); jchar CallCharMethodA(jobject obj, jmethodID mid, jvalue* val); jchar CallNonvirtualCharMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jcharArray NewCharArray(jsize len); void SetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* vals); void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* vals); jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy); void ReleaseCharArrayElements(jcharArray, jchar* v, jint mode); // Short jshort GetStaticShortField(jclass clazz, jfieldID fid); jshort GetShortField(jobject clazz, jfieldID fid); void SetStaticShortField(jclass clazz, jfieldID fid, jshort val); void SetShortField(jobject clazz, jfieldID fid, jshort val); jshort CallStaticShortMethodA(jclass clazz, jmethodID mid, jvalue* val); jshort CallShortMethodA(jobject obj, jmethodID mid, jvalue* val); jshort CallNonvirtualShortMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jshortArray NewShortArray(jsize len); void SetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* vals); void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* vals); jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy); void ReleaseShortArrayElements(jshortArray, jshort* v, jint mode); // Integer jint GetStaticIntField(jclass clazz, jfieldID fid); jint GetIntField(jobject clazz, jfieldID fid); void SetStaticIntField(jclass clazz, jfieldID fid, jint val); void SetIntField(jobject clazz, jfieldID fid, jint val); jint CallStaticIntMethodA(jclass clazz, jmethodID mid, jvalue* val); jint CallIntMethodA(jobject obj, jmethodID mid, jvalue* val); jint CallNonvirtualIntMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jintArray NewIntArray(jsize len); void SetIntArrayRegion(jintArray array, jsize start, jsize len, jint* vals); void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* vals); jint* GetIntArrayElements(jintArray array, jboolean* isCopy); void ReleaseIntArrayElements(jintArray, jint* v, jint mode); // Long jlong GetStaticLongField(jclass clazz, jfieldID fid); jlong GetLongField(jobject clazz, jfieldID fid); void SetStaticLongField(jclass clazz, jfieldID fid, jlong val); void SetLongField(jobject clazz, jfieldID fid, jlong val); jlong CallStaticLongMethodA(jclass clazz, jmethodID mid, jvalue* val); jlong CallLongMethodA(jobject obj, jmethodID mid, jvalue* val); jlong CallNonvirtualLongMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jfloat GetStaticFloatField(jclass clazz, jfieldID fid); jlongArray NewLongArray(jsize len); void SetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* vals); void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* vals); jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy); void ReleaseLongArrayElements(jlongArray, jlong* v, jint mode); // Float jfloat GetFloatField(jobject clazz, jfieldID fid); void SetStaticFloatField(jclass clazz, jfieldID fid, jfloat val); void SetFloatField(jobject clazz, jfieldID fid, jfloat val); jfloat CallStaticFloatMethodA(jclass clazz, jmethodID mid, jvalue* val); jfloat CallFloatMethodA(jobject obj, jmethodID mid, jvalue* val); jfloat CallNonvirtualFloatMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jfloatArray NewFloatArray(jsize len); void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* vals); void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* vals); jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy); void ReleaseFloatArrayElements(jfloatArray, jfloat* v, jint mode); // Double jdouble GetStaticDoubleField(jclass clazz, jfieldID fid); jdouble GetDoubleField(jobject clazz, jfieldID fid); void SetStaticDoubleField(jclass clazz, jfieldID fid, jdouble val); void SetDoubleField(jobject clazz, jfieldID fid, jdouble val); jdouble CallStaticDoubleMethodA(jclass clazz, jmethodID mid, jvalue* val); jdouble CallDoubleMethodA(jobject obj, jmethodID mid, jvalue* val); jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jdoubleArray NewDoubleArray(jsize len); void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* vals); void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* vals); jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy); void ReleaseDoubleArrayElements(jdoubleArray, jdouble* v, jint mode); // Object jclass GetObjectClass(jobject obj); jobject GetStaticObjectField(jclass clazz, jfieldID fid); jobject GetObjectField(jobject clazz, jfieldID fid); void SetStaticObjectField(jclass clazz, jfieldID fid, jobject val); void SetObjectField(jobject clazz, jfieldID fid, jobject val); jobject CallStaticObjectMethodA(jclass clazz, jmethodID mid, jvalue* val); jobject CallObjectMethodA(jobject obj, jmethodID mid, jvalue* val); jobject CallNonvirtualObjectMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val); jobjectArray NewObjectArray(jsize a0, jclass a1, jobject a2); void SetObjectArrayElement(jobjectArray a0, jsize a1, jobject a2); // String jstring NewStringUTF(const char* a0); void* GetDirectBufferAddress(jobject obj); jlong GetDirectBufferCapacity(jobject obj); jboolean isBufferReadOnly(jobject obj); jobject asReadOnlyBuffer(jobject obj); jboolean orderBuffer(jobject obj); jclass getClass(jobject obj); /** This returns a UTF16 surogate coded UTF-8 string. */ const char* GetStringUTFChars(jstring a0, jboolean* a1); void ReleaseStringUTFChars(jstring a0, const char* a1); jsize GetStringUTFLength(jstring a0); jboolean isPackage(const string& str); jobject getPackage(const string& str); jobject getPackageObject(jobject pkg, const string& str); jarray getPackageContents(jobject pkg); void newWrapper(JPClass* cls); void registerRef(jobject obj, PyObject* hostRef); void registerRef(jobject obj, void* ref, JCleanupHook cleanup); void clearInterrupt(bool throws); } ; #endif // _JP_JAVA_FRAME_H_ jpype-1.6.0/native/common/include/jp_longtype.h000077500000000000000000000060171501674766700216030ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_LONG_TYPE_H_ #define _JP_LONG_TYPE_H_ class JPLongType : public JPPrimitiveType { public: JPLongType(); ~JPLongType() override; using type_t = jlong; using array_t = jlongArray; static inline jlong& field(jvalue& v) { return v.j; } static inline const jlong& field(const jvalue& v) { return v.j; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'J'; } // GCOVR_EXCL_START // Required but not exercised currently jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } // GCOVR_EXCL_STOP jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } static jlong assertRange(const jlong& l) { return l; } void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_LONG_TYPE_H_ jpype-1.6.0/native/common/include/jp_match.h000066400000000000000000000034061501674766700210320ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_MATCH_H #define JP_MATCH_H class JPConversion; class JPMatch { public: enum Type { _none = 0, _explicit = 1, _implicit = 2, _derived = 3, _exact = 4 } ; public: JPMatch(); JPMatch(JPJavaFrame *frame, PyObject *object); /** * Get the Java slot associated with the Python object. * * Thus uses caching. * * @return the Java slot or 0 if not available. */ JPValue *getJavaSlot(); jvalue convert(); public: JPMatch::Type type; JPConversion *conversion; JPJavaFrame *frame; PyObject *object; JPValue *slot; void *closure; } ; class JPMethodCache { public: long m_Hash; JPMethod* m_Overload; } ; class JPMethodMatch : public JPMethodCache { public: JPMethodMatch(JPJavaFrame &frame, JPPyObjectVector& args, bool callInstance); JPMatch& operator[](size_t i) { return m_Arguments[i]; } const JPMatch& operator[](size_t i) const { return m_Arguments[i]; } std::vector m_Arguments; JPMatch::Type m_Type; bool m_IsVarIndirect; char m_Offset; char m_Skip; } ; #endif /* JP_MATCH_H */ jpype-1.6.0/native/common/include/jp_method.h000066400000000000000000000063541501674766700212230ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPMETHOD_H_ #define _JPMETHOD_H_ #include "jp_modifier.h" class JPMethod; class JPMethod : public JPResource { friend class JPMethodDispatch; public: JPMethod() = default; JPMethod(JPJavaFrame& frame, JPClass* claz, const string& name, jobject mth, jmethodID mid, JPMethodList& moreSpecific, jint modifiers); ~JPMethod() override; void setParameters( JPClass *returnType, JPClassList&& parameterTypes); /** Check to see if this overload matches the argument list. * * @param frame is the scope to hold Java local variables. * @param match holds the details of the match that is found. * @param isInstance is true if the first argument is an instance object. * @param args is a list of arguments including the instance. * * @return the quality of the match. * */ JPMatch::Type matches(JPJavaFrame &frame, JPMethodMatch& match, bool isInstance, JPPyObjectVector& args); JPPyObject invoke(JPJavaFrame &frame, JPMethodMatch& match, JPPyObjectVector& arg, bool instance); JPPyObject invokeCallerSensitive(JPMethodMatch& match, JPPyObjectVector& arg, bool instance); JPValue invokeConstructor(JPJavaFrame &frame, JPMethodMatch& match, JPPyObjectVector& arg); bool isAbstract() const { return JPModifier::isAbstract(m_Modifiers); } bool isConstructor() const { return JPModifier::isConstructor(m_Modifiers); } bool isInstance() const { return !JPModifier::isStatic(m_Modifiers) && !JPModifier::isConstructor(m_Modifiers); } bool isFinal() const { return JPModifier::isFinal(m_Modifiers); } bool isStatic() const { return JPModifier::isStatic(m_Modifiers); } bool isVarArgs() const { return JPModifier::isVarArgs(m_Modifiers); } bool isCallerSensitive() const { return JPModifier::isCallerSensitive(m_Modifiers); } string toString() const; string matchReport(JPPyObjectVector& args); bool checkMoreSpecificThan(JPMethod* other) const; jobject getJava() { return m_Method.get(); } JPMethod& operator=(const JPMethod&) = delete; private: void packArgs(JPJavaFrame &frame, JPMethodMatch &match, vector &v, JPPyObjectVector &arg); void ensureTypeCache(); JPMethod(const JPMethod& o); private: JPClass* m_Class{}; string m_Name; JPObjectRef m_Method; jmethodID m_MethodID{}; JPClass* m_ReturnType{}; JPClassList m_ParameterTypes; JPMethodList m_MoreSpecificOverloads; jint m_Modifiers{}; } ; #endif // _JPMETHOD_H_jpype-1.6.0/native/common/include/jp_methoddispatch.h000066400000000000000000000044541501674766700227420ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPMETHODDISPATCH_H_ #define _JPMETHODDISPATCH_H_ #include "jp_class.h" class JPMethodDispatch : public JPResource { public: /** * Create a new method based on class and a name; */ JPMethodDispatch(JPClass *clazz, const string& name, JPMethodList& overloads, jint modifiers); ~JPMethodDispatch() override; JPMethodDispatch(const JPMethodDispatch& method) = delete; JPMethodDispatch& operator=(const JPMethodDispatch& method) = delete; public: JPClass* getClass() { return m_Class; } const string& getName() const; bool hasStatic() const { return JPModifier::isStatic(m_Modifiers); } bool isBeanMutator() const { return JPModifier::isBeanMutator(m_Modifiers); } bool isBeanAccessor() const { return JPModifier::isBeanAccessor(m_Modifiers); } JPPyObject invoke(JPJavaFrame& frame, JPPyObjectVector& vargs, bool instance); JPValue invokeConstructor(JPJavaFrame& frame, JPPyObjectVector& vargs); bool matches(JPJavaFrame& frame, JPPyObjectVector& args, bool instance); string matchReport(JPPyObjectVector& sequence); const JPMethodList& getMethodOverloads() { return m_Overloads; } private: /** Search for a matching overload. * * @param searchInstance is true if the first argument is to be skipped * when matching with a non-static. */ bool findOverload(JPJavaFrame& frame, JPMethodMatch &bestMatch, JPPyObjectVector& vargs, bool searchInstance, bool raise); JPClass* m_Class; string m_Name; JPMethodList m_Overloads; jlong m_Modifiers; JPMethodCache m_LastCache{}; } ; #endif // _JPMETHODDISPATCH_H_ jpype-1.6.0/native/common/include/jp_modifier.h000066400000000000000000000074161501674766700215410ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_MODIFIER_H #define JP_MODIFIER_H #ifdef __cplusplus extern "C" { #endif namespace JPModifier { /* https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.1-200-E.1 * https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.5-200-A.1 * https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.6-200-A.1 */ inline bool isPublic(jlong modifier) { return (modifier & 0x0001) == 0x0001; } inline bool isPrivate(jlong modifier) { return (modifier & 0x0002) == 0x0002; } inline bool isProtected(jlong modifier) { return (modifier & 0x0004) == 0x0004; } inline bool isStatic(jlong modifier) { return (modifier & 0x0008) == 0x0008; } inline bool isFinal(jlong modifier) { return (modifier & 0x0010) == 0x0010; } inline bool isSuper(jlong modifier) { return (modifier & 0x0020) == 0x0020; } inline bool isVolatile(jlong modifier) { return (modifier & 0x0040) == 0x0040; } // fields inline bool isBridge(jlong modifier) { return (modifier & 0x0040) == 0x0040; } // methods inline bool isTransient(jlong modifier) { return (modifier & 0x0080) == 0x0080; } // fields inline bool isVarArgs(jlong modifier) { return (modifier & 0x0080) == 0x0080; } //methods inline bool isNative(jlong modifier) { return (modifier & 0x0100) == 0x0100; } inline bool isInterface(jlong modifier) { return (modifier & 0x0200) == 0x0200; } inline bool isAbstract(jlong modifier) { return (modifier & 0x0400) == 0x0400; } inline bool isStrict(jlong modifier) { return (modifier & 0x0800) == 0x0800; } inline bool isSynthetic(jlong modifier) { return (modifier & 0x1000) == 0x1000; } inline bool isAnnotation(jlong modifier) { return (modifier & 0x2000) == 0x2000; } inline bool isEnum(jlong modifier) { return (modifier & 0x4000) == 0x4000; } inline bool isModule(jlong modifier) { return (modifier & 0x8000) == 0x8000; } /* JPype flags (must match ModifierCodes sections) */ inline bool isSpecial(jlong modifier) { return (modifier & 0x00010000) == 0x00010000; } inline bool isThrowable(jlong modifier) { return (modifier & 0x00020000) == 0x00020000; } inline bool isSerializable(jlong modifier) { return (modifier & 0x00040000) == 0x00040000; } inline bool isAnonymous(jlong modifier) { return (modifier & 0x00080000) == 0x00080000; } inline bool isFunctional(jlong modifier) { return (modifier & 0x00100000) == 0x00100000; } inline bool isCallerSensitive(jlong modifier) { return (modifier & 0x00200000) == 0x00200000; } inline bool isPrimitiveArray(jlong modifier) { return (modifier & 0x00400000) == 0x00400000; } inline bool isComparable(jlong modifier) { return (modifier & 0x00800000) == 0x00800000; } inline bool isBuffer(jlong modifier) { return (modifier & 0x01000000) == 0x01000000; } inline bool isConstructor(jlong modifier) { return (modifier & 0x10000000) == 0x10000000; } inline bool isBeanAccessor(jlong modifier) { return (modifier & 0x20000000) == 0x20000000; } inline bool isBeanMutator(jlong modifier) { return (modifier & 0x40000000) == 0x40000000; } } #ifdef __cplusplus } #endif #endif /* JP_MODIFIER_H */jpype-1.6.0/native/common/include/jp_monitor.h000066400000000000000000000016641501674766700214310ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPMONITOR_H_ #define _JPMONITOR_H_ class JPMonitor { public: JPMonitor(jobject obj); virtual ~JPMonitor(); void enter(); void exit(); private: JPObjectRef m_Value; } ; #endif // _JPMONITOR_H_ jpype-1.6.0/native/common/include/jp_numbertype.h000066400000000000000000000022561501674766700221320ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPNUMBERTYPE_H_ #define _JPNUMBERTYPE_H_ /** * Wrapper for Class */ class JPNumberType : public JPClass { public: JPNumberType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers); ~ JPNumberType() override; JPMatch::Type findJavaConversion(JPMatch& match) override; void getConversionInfo(JPConversionInfo &info) override; } ; #endif // _JPNUMBERTYPE_H_jpype-1.6.0/native/common/include/jp_objecttype.h000066400000000000000000000025551501674766700221120ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPOBJECTTYPE_H_ #define _JPOBJECTTYPE_H_ /** * Wrapper for Class * * Primitive types can implicitly cast to this type as * well as class wrappers, thus we need a specialized * wrapper. This class should not be used outside of * the JPTypeManager. * */ class JPObjectType : public JPClass { public: JPObjectType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers); ~ JPObjectType() override; JPMatch::Type findJavaConversion(JPMatch& match) override; void getConversionInfo(JPConversionInfo &info) override; } ; #endif // _JPOBJECTTYPE_H_jpype-1.6.0/native/common/include/jp_platform.h000066400000000000000000000021421501674766700215560ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPENV_H_ #define _JPENV_H_ /** * the platform adapter's implementation is chosen by the JPYPE_??? macros */ class JPPlatformAdapter { public: virtual ~JPPlatformAdapter(); virtual void loadLibrary(const char* path) = 0; virtual void unloadLibrary() = 0; virtual void* getSymbol(const char* name) = 0; static JPPlatformAdapter* getAdapter(); } ; #endif // _JPENV_H_jpype-1.6.0/native/common/include/jp_primitive_accessor.h000066400000000000000000000205721501674766700236330ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_PRIMITIVE_ACCESSOR_H #define JP_PRIMITIVE_ACCESSOR_H #include #include "jp_exception.h" #include "jp_javaframe.h" #include "jp_match.h" template class JPPrimitiveArrayAccessor { using releaseFnc = void (JPJavaFrame::*)(array_t, ptr_t, jint); using accessFnc = ptr_t (JPJavaFrame::*)(array_t, jboolean *); JPJavaFrame& _frame; array_t _array; ptr_t _elem; releaseFnc _release; jboolean _iscopy; public: JPPrimitiveArrayAccessor(JPJavaFrame& frame, jarray array, accessFnc access, releaseFnc release) : _frame(frame), _array((array_t) array), _release(release) { _elem = ((&_frame)->*access)(_array, &_iscopy); } ~JPPrimitiveArrayAccessor() { // This is fallback if commit or abort is not called. // It should only occur in cases where a throw has // already been issued. try { if (_array) ((&_frame)->*_release)(_array, _elem, JNI_ABORT); } catch (JPypeException&) // GCOVR_EXCL_LINE { // We can't throw here because it would abort. // But this is called on a non-op release, so // we will just eat it } } jsize size() { return _frame.GetArrayLength(_array); } ptr_t get() { return _elem; } void commit() { // Prevent the dtor from calling a second time array_t a = _array; _array = 0; ((&_frame)->*_release)(a, _elem, 0); } void abort() { // Prevent the dtor from calling a second time array_t a = _array; _array = 0; ((&_frame)->*_release)(a, _elem, JNI_ABORT); } } ; template PyObject *convertMultiArray( JPJavaFrame &frame, JPPrimitiveType* cls, void (*pack)(type_t*, jvalue), const char* code, JPPyBuffer &buffer, int subs, int base, jobject dims) { JPContext *context = frame.getContext(); Py_buffer& view = buffer.getView(); jconverter converter = getConverter(view.format, (int) view.itemsize, code); if (converter == nullptr) { PyErr_Format(PyExc_TypeError, "No type converter found"); return nullptr; } // Reserve space for array. auto contents = (jobjectArray) context->_java_lang_Object->newArrayOf(frame, subs); std::vector indices(view.ndim); int u = view.ndim - 1; int k = 0; jarray a0 = cls->newArrayOf(frame, base); frame.SetObjectArrayElement(contents, k++, a0); jboolean isCopy; void *mem = frame.getEnv()->GetPrimitiveArrayCritical(a0, &isCopy); JP_TRACE_JAVA("GetPrimitiveArrayCritical", mem); auto *dest = (type_t*) mem; Py_ssize_t step; if (view.strides == nullptr) step = view.itemsize; else step = view.strides[u]; // Align with the first element in the array char *src = buffer.getBufferPtr(indices); // Traverse the array while (true) { if (indices[u] == view.shape[u]) { int j; for (j = 0; j < u; ++j) { indices[u - j - 1]++; if (indices[u - j - 1] < view.shape[u - j - 1]) break; indices[u - j - 1] = 0; } // Commit the current section indices[u] = 0; JP_TRACE_JAVA("ReleasePrimitiveArrayCritical", mem); frame.getEnv()->ReleasePrimitiveArrayCritical(a0, mem, JNI_COMMIT); frame.DeleteLocalRef(a0); // If we hit the shape of the uppermost we are done if (j == u) break; a0 = cls->newArrayOf(frame, base); frame.SetObjectArrayElement(contents, k++, a0); mem = frame.getEnv()->GetPrimitiveArrayCritical(a0, &isCopy); JP_TRACE_JAVA("GetPrimitiveArrayCritical", mem); dest = (type_t*) mem; src = buffer.getBufferPtr(indices); } pack(dest, converter(src)); src += step; dest++; indices[u]++; } // Assemble it into a multidimensional array jobject out = frame.assemble(dims, contents); // Convert it to Python JPClass *type = context->_java_lang_Object; if (out != nullptr) type = frame.findClassForObject(out); jvalue v; v.l = out; return type->convertToPythonObject(frame, v, false).keep(); } template class JPConversionLong : public JPIndexConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyLong_CheckExact(match.object) && !PyIndex_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; } jvalue convert(JPMatch &match) override { jvalue res; if (match.type == JPMatch::_exact) { auto val = (jlong) PyLong_AsUnsignedLongLongMask(match.object); if (val == -1) JP_PY_CHECK(); // GCOVR_EXCL_LINE base_t::field(res) = (typename base_t::type_t) val; } else { auto val = (jlong) PyLong_AsLongLong(match.object); if (val == -1) JP_PY_CHECK(); // GCOVR_EXCL_LINE base_t::field(res) = (typename base_t::type_t) base_t::assertRange(val); } return res; } } ; template class JPConversionLongNumber : public JPConversionLong { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyNumber_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_explicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsFloat")); PyList_Append(info.expl, proto.get()); } jvalue convert(JPMatch &match) override { JPPyObject obj = JPPyObject::call(PyNumber_Long(match.object)); match.object = obj.get(); return JPConversionLong::convert(match); } } ; template class JPConversionLongWiden : public JPConversion { public: // GCOVR_EXCL_START JPMatch::Type matches(JPClass *cls, JPMatch &match) override { return JPMatch::_none; // Not used } void getInfo(JPClass *cls, JPConversionInfo &info) override { // Not used } // GCOVR_EXCL_STOP jvalue convert(JPMatch &match) override { JPValue *value = match.getJavaSlot(); jvalue ret; base_t::field(ret) = (typename base_t::type_t) (dynamic_cast( value->getClass()))->getAsLong(value->getValue()); return ret; } } ; template class JPConversionAsFloat : public JPNumberConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyNumber_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; } jvalue convert(JPMatch &match) override { jvalue res; double val = PyFloat_AsDouble(match.object); if (val == -1.0) JP_PY_CHECK(); // GCOVR_EXCL_LINE base_t::field(res) = (typename base_t::type_t) val; return res; } } ; template class JPConversionLongAsFloat : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyLong_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.implicit, (PyObject*) & PyLong_Type); } jvalue convert(JPMatch &match) override { jvalue res; jdouble v = PyLong_AsDouble(match.object); if (v == -1.0) JP_PY_CHECK(); // GCOVR_EXCL_LINE base_t::field(res) = (typename base_t::type_t) v; return res; } } ; template class JPConversionFloatWiden : public JPConversion { public: // GCOVR_EXCL_START JPMatch::Type matches(JPClass *cls, JPMatch &match) override { return JPMatch::_none; // not used } void getInfo(JPClass *cls, JPConversionInfo &info) override { } // GCOVR_EXCL_STOP jvalue convert(JPMatch &match) override { JPValue *value = match.getJavaSlot(); jvalue ret; base_t::field(ret) = (typename base_t::type_t) (dynamic_cast( value->getClass()))->getAsDouble(value->getValue()); return ret; } } ; #endif /* JP_PRIMITIVE_ACCESSOR_H */ jpype-1.6.0/native/common/include/jp_primitivetype.h000066400000000000000000000033641501674766700226530ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPPRIMITIVETYPE_H_ #define _JPPRIMITIVETYPE_H_ #include "jp_boxedtype.h" class JPPrimitiveType : public JPClass { protected: explicit JPPrimitiveType(const string& name); ~JPPrimitiveType() override; public: bool isPrimitive() const override; virtual JPClass* getBoxedClass(JPJavaFrame& frame) const = 0; virtual char getTypeCode() = 0; virtual jlong getAsLong(jvalue v) = 0; virtual jdouble getAsDouble(jvalue v) = 0; void setClass(JPJavaFrame& frame, jclass o) { m_Class = JPClassRef(frame, o); } virtual void getView(JPArrayView& view) = 0; virtual void releaseView(JPArrayView& view) = 0; virtual const char* getBufferFormat() = 0; virtual Py_ssize_t getItemSize() = 0; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) = 0; virtual PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer& view, int subs, int base, jobject dims) = 0; // Helper for Long types PyObject *convertLong(PyTypeObject* wrapper, PyLongObject* tmp); } ; #endif jpype-1.6.0/native/common/include/jp_proxy.h000066400000000000000000000047041501674766700211210ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPPROXY_H_ #define _JPPROXY_H_ struct PyJPProxy; class JPProxy; class JPFunctional; class JPProxy { public: friend class JPProxyType; JPProxy(PyJPProxy* inst, JPClassList& intf); virtual ~JPProxy(); const JPClassList& getInterfaces() const { return m_InterfaceClasses; } jvalue getProxy(); virtual JPPyObject getCallable(const string& cname, int& addSelf) = 0; static void releaseProxyPython(void* host); PyJPProxy* getInstance() { return m_Instance; } PyJPProxy* m_Instance; JPObjectRef m_Proxy; JPClassList m_InterfaceClasses; jweak m_Ref; } ; class JPProxyDirect : public JPProxy { public: JPProxyDirect(PyJPProxy* inst, JPClassList& intf); ~JPProxyDirect() override; JPPyObject getCallable(const string& cname, int& addSelf) override; } ; class JPProxyIndirect : public JPProxy { public: JPProxyIndirect(PyJPProxy* inst, JPClassList& intf); ~JPProxyIndirect() override; JPPyObject getCallable(const string& cname, int& addSelf) override; } ; class JPProxyFunctional : public JPProxy { public: JPProxyFunctional(PyJPProxy* inst, JPClassList& intf); ~JPProxyFunctional() override; JPPyObject getCallable(const string& cname, int& addSelf) override; private: JPFunctional *m_Functional; } ; /** Special wrapper for round trip returns */ class JPProxyType : public JPClass { public: JPProxyType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers); ~ JPProxyType() override; public: // JPClass implementation JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; private: JPClassRef m_ProxyClass; jmethodID m_GetInvocationHandlerID; jfieldID m_InstanceID; } ; #endif // JPPROXY_H jpype-1.6.0/native/common/include/jp_reference_queue.h000066400000000000000000000020341501674766700230740ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_REFERENCE_QUEUE_H__ #define JP_REFERENCE_QUEUE_H__ #include namespace JPReferenceQueue { void registerRef(JPJavaFrame &frame, jobject obj, PyObject* targetRef); void registerRef(JPJavaFrame &frame, jobject obj, void* host, JCleanupHook func); } ; // end of namespace JPReferenceQueue #endifjpype-1.6.0/native/common/include/jp_shorttype.h000077500000000000000000000062211501674766700220000ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_SHORT_TYPE_H_ #define _JP_SHORT_TYPE_H_ class JPShortType : public JPPrimitiveType { public: JPShortType(); ~JPShortType() override; using type_t = jshort; using array_t = jshortArray; static inline jshort& field(jvalue& v) { return v.s; } static inline const jshort& field(const jvalue& v) { return v.s; } JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; void getConversionInfo(JPConversionInfo &info) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override { return 'S'; } // GCOVR_EXCL_START // Required but not exercised currently jlong getAsLong(jvalue v) override { return (jlong) field(v); // GCOVR_EXCL_LINE } jdouble getAsDouble(jvalue v) override { return (jdouble) field(v); } // GCOV_EXCL_STOP template static T assertRange(const T& l) { if (l < -32768 || l > 32767) { JP_RAISE(PyExc_OverflowError, "Cannot convert value to Java short"); } return l; } void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_SHORT_TYPE_H_ jpype-1.6.0/native/common/include/jp_stringtype.h000066400000000000000000000024551501674766700221510ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_STRINGCLASS_H #define JP_STRINGCLASS_H class JPStringType : public JPClass { public: JPStringType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers); ~JPStringType() override; public: JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPMatch::Type findJavaConversion(JPMatch& match) override; void getConversionInfo(JPConversionInfo &info) override; JPValue newInstance(JPJavaFrame& frame, JPPyObjectVector& args) override; } ; #endif /* JP_STRINGTYPE_H */jpype-1.6.0/native/common/include/jp_tracer.h000066400000000000000000000103511501674766700212130ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_TRACER_H__ #define _JP_TRACER_H__ #include #include // GCOVR_EXCL_START #ifdef JP_TRACING_ENABLE #define JP_TRACE_IN_C(...) \ JPypeTracer _trace(__VA_ARGS__); try { #define JP_TRACE_OUT_C } \ catch(...) { _trace.gotError(JP_STACKINFO()); throw; } #define JP_TRACE_IN(...) \ JPypeTracer _trace(__VA_ARGS__); \ try { do {} while (0) #define JP_TRACE_OUT \ } \ catch(...) { _trace.gotError(JP_STACKINFO()); throw; } #define JP_TRACE(...) JPTracer::trace(__VA_ARGS__) #define JP_TRACE_LOCKS(...) JPypeTracer::traceLocks(__VA_ARGS__) #define JP_TRACE_PY(m, obj) JPypeTracer::tracePythonObject(m, obj) #define JP_TRACE_JAVA(m, obj) JPypeTracer::traceJavaObject(m, obj) #else #ifndef JP_INSTRUMENTATION #define JP_TRACE_IN(...) try { do {} while (0) #endif #define JP_TRACE_OUT } catch (JPypeException &ex) { ex.from(JP_STACKINFO()); throw; } #define JP_TRACE(...) #define JP_TRACE_LOCKS(...) #define JP_TRACE_PY(m, obj) #define JP_TRACE_JAVA(m, obj) #endif // Enable this option to get all the py referencing information #define JP_ENABLE_TRACE_PY class JPypeTracer { private: string m_Name; bool m_Error; JPypeTracer *m_Last; public: explicit JPypeTracer(const char *name, void *ref = nullptr); ~JPypeTracer(); void gotError(const JPStackInfo& info) { m_Error = true; try { throw; // lgtm [cpp/rethrow-no-exception] } catch (JPypeException& ex) { ex.from(info); throw; } } static void traceJavaObject(const char *msg, const void* obj); static void tracePythonObject(const char *msg, PyObject *ref); static void traceLocks(const string& msg, void *ref); static void trace1(const char *src, const char *msg); static void trace2(const char *msg1, const char *msg2); private: static void traceIn(const char *msg, void *ref); static void traceOut(const char *msg, bool error); } ; extern "C" int _PyJPModule_trace; namespace JPTracer { template inline void trace(const T& msg) { if ((_PyJPModule_trace & 1) == 0) return; std::stringstream str; str << msg; JPypeTracer::trace1(nullptr, str.str().c_str()); } inline void trace(const char *msg) { if ((_PyJPModule_trace & 1) == 0) return; JPypeTracer::trace1(nullptr, msg); } template inline void trace(const T1& msg1, const T2 & msg2) { if ((_PyJPModule_trace & 1) == 0) return; std::stringstream str; str << msg1 << " " << msg2; JPypeTracer::trace1(nullptr, str.str().c_str()); } inline void trace(const char *msg1, const char *msg2) { if ((_PyJPModule_trace & 1) == 0) return; JPypeTracer::trace2(msg1, msg2); } template inline void trace(const T1& msg1, const T2& msg2, const T3 & msg3) { if ((_PyJPModule_trace & 1) == 0) return; std::stringstream str; str << msg1 << " " << msg2 << " " << msg3; JPypeTracer::trace1(nullptr, str.str().c_str()); } template inline void trace(const T1& msg1, const T2& msg2, const T3& msg3, const T4 & msg4) { if ((_PyJPModule_trace & 1) == 0) return; std::stringstream str; str << msg1 << " " << msg2 << " " << msg3 << " " << msg4; JPypeTracer::trace1(nullptr, str.str().c_str()); } template inline void trace(const T1& msg1, const T2& msg2, const T3& msg3, const T4& msg4, const T5 & msg5) { if ((_PyJPModule_trace & 1) == 0) return; std::stringstream str; str << msg1 << " " << msg2 << " " << msg3 << " " << msg4 << " " << msg5; JPypeTracer::trace1(nullptr, str.str().c_str()); } } // GCOVR_EXCL_STOP #endif // _JP_TRACER_H__jpype-1.6.0/native/common/include/jp_typemanager.h000066400000000000000000000032561501674766700222550ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPTYPE_MANAGER_H_ #define _JPTYPE_MANAGER_H_ /** * These functions will manage the cache of found type, be it primitive types, class types or the "magic" types. */ class JPTypeManager { friend class JPContext; public: /** * Initialize the type manager caches */ explicit JPTypeManager(JPJavaFrame& frame); ~JPTypeManager() = default; /** * Find a class using a native name. * * The pointer returned is NOT owned by the caller */ JPClass* findClass(jclass cls); JPClass* findClassByName(const string& str); JPClass* findClassForObject(jobject obj); void populateMethod(void* method, jobject obj); void populateMembers(JPClass* cls); int interfaceParameterCount(JPClass* cls); private: JPObjectRef m_JavaTypeManager; jmethodID m_FindClass; jmethodID m_FindClassByName; jmethodID m_FindClassForObject; jmethodID m_PopulateMethod; jmethodID m_PopulateMembers; jmethodID m_InterfaceParameterCount; } ; #endif // _JPCLASS_H_ jpype-1.6.0/native/common/include/jp_value.h000066400000000000000000000035031501674766700210500ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPVALUE_H_ #define _JPVALUE_H_ #include "jp_class.h" #include "jp_javaframe.h" /** Lightweight representative of a jvalue with its corresponding class. * This should not have any memory management. The user of the class * must take a global reference if needed. */ class JPValue { public: JPValue() { m_Value.l = nullptr; } JPValue(JPClass* clazz, const jvalue& value) : m_Class(clazz), m_Value(value) { } JPValue(JPClass* clazz, jobject value) : m_Class(clazz) { m_Value.l = value; } ~JPValue() = default; JPClass* getClass() const { return m_Class; } jvalue& getValue() { return m_Value; } const jvalue& getValue() const { return m_Value; } jobject getJavaObject() const; // Cast operators to jvalue. // TODO: these could be explicit too, right? operator jvalue&() { return m_Value; } operator const jvalue&() const { return m_Value; } // TODO: never used. JPValue& global(JPJavaFrame& frame) { m_Value.l = frame.NewGlobalRef(m_Value.l); return *this; } private: JPClass* m_Class{}; jvalue m_Value{}; } ; #endif // _JPVALUE_H_ jpype-1.6.0/native/common/include/jp_voidtype.h000077500000000000000000000050411501674766700216010ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JP_VOID_TYPE_H_ #define _JP_VOID_TYPE_H_ class JPVoidType : public JPPrimitiveType { public: JPVoidType(); ~JPVoidType() override; JPClass* getBoxedClass(JPJavaFrame& frame) const override; JPMatch::Type findJavaConversion(JPMatch &match) override; JPPyObject convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) override; JPValue getValueFromObject(JPJavaFrame& frame, const JPValue& obj) override; JPPyObject invokeStatic(JPJavaFrame& frame, jclass, jmethodID, jvalue*) override; JPPyObject invoke(JPJavaFrame& frame, jobject, jclass, jmethodID, jvalue*) override; JPPyObject getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) override; void setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* val) override; JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; jarray newArrayOf(JPJavaFrame& frame, jsize size) override; void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; char getTypeCode() override; jlong getAsLong(jvalue v) override; jdouble getAsDouble(jvalue v) override; void getView(JPArrayView& view) override; void releaseView(JPArrayView& view) override; const char* getBufferFormat() override; Py_ssize_t getItemSize() override; void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; PyObject *newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) override; } ; #endif // _JP_VOID_TYPE_H_ jpype-1.6.0/native/common/include/jpype.h000066400000000000000000000134141501674766700203740ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef _JPYPE_H_ #define _JPYPE_H_ #ifdef __GNUC__ // Python requires char* but C++ string constants are const char* #pragma GCC diagnostic ignored "-Wwrite-strings" #endif #ifdef WIN32 #ifndef __GNUC__ // Then this must mean a variant of GCC on win32 ... #pragma warning (disable:4786) #endif #if defined(__CYGWIN__) // jni_md.h does not work for cygwin. Use this instead. #elif defined(__GNUC__) // JNICALL causes problem for function prototypes .. since I am not defining any JNI methods there is no need for it #undef JNICALL #define JNICALL #endif #endif #include // Define this and use to allow destructors to throw in C++11 or later #if defined(_MSC_VER) // Visual Studio C++ does not seem have changed __cplusplus since 1997 // see: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170&viewFallbackFrom=vs-2019 #if (_MSVC_LAND >= 201402) #define NO_EXCEPT_FALSE noexcept(false) #else #define NO_EXCEPT_FALSE throw(JPypeException) #endif #else // For all the compilers that understand standards #if (__cplusplus >= 201103L) #define NO_EXCEPT_FALSE noexcept(false) #else #define NO_EXCEPT_FALSE throw(JPypeException) #endif #endif #include #include #include using std::map; using std::string; using std::vector; #ifdef JP_INSTRUMENTATION #include template constexpr uint32_t _hash(const char *q, uint32_t v) { return _hash < i - 1 > (q + 1, v * 0x1a481023 + q[0]); } template <> constexpr uint32_t _hash<0>(const char *q, uint32_t v) { return v; } #define compile_hash(x) _hash(x, 0) extern void PyJPModuleFault_throw(uint32_t code); extern int PyJPModuleFault_check(uint32_t code); #define JP_TRACE_IN(X, ...) try { PyJPModuleFault_throw(compile_hash(X)); #define JP_FAULT_RETURN(X, Y) if (PyJPModuleFault_check(compile_hash(X))) return Y #define JP_BLOCK(X) if (PyJPModuleFault_check(compile_hash(X))==0) #else #define JP_FAULT_RETURN(X, Y) if (false) while (false) #define JP_BLOCK(X) if (false) while (false) #endif /** Definition of commonly used template types */ using StringVector = vector; /** * Converter are used for bulk byte transfers from Python to Java. */ using jconverter = jvalue (*)(void *) ; /** * Create a converter for a bulk byte transfer. * * Bulk transfers do not check for range and may be lossy. These are only * triggered when a transfer either using memoryview or a slice operator * assignment from a buffer object (such as numpy.array). Converters are * created once at the start of the transfer and used to convert each * byte by casting the memory and then assigning to the jvalue union with * the requested type. * * Byte order transfers are not supported by the Python buffer API and thus * have not been implemented. * * @param from is a Python struct designation * @param itemsize is the size of the Python item * @param to is the desired Java primitive type * @return a converter function to convert each member. */ extern jconverter getConverter(const char* from, int itemsize, const char* to); extern bool _jp_cpp_exceptions; // Types class JPClass; class JPValue; class JPProxy; class JPArray; class JPArrayClass; class JPArrayView; class JPBoxedType; class JPPrimitiveType; class JPStringType; // Members class JPMethod; class JPMethodDispatch; class JPField; // Services class JPTypeManager; class JPClassLoader; class JPContext; class JPBuffer; class JPPyObject; extern "C" using JCleanupHook = void (*)(void *) ; extern "C" struct JPConversionInfo; using JPClassList = vector; using JPFieldList = vector; using JPMethodDispatchList = vector; using JPMethodList = vector; class JPResource { public: virtual ~JPResource() = 0; } ; // Macros for raising an exception with jpype // These must be macros so that we can update the pattern and // maintain the appropriate auditing information. C++ does not // have a lot for facilities to make this easy. #define JP_RAISE_PYTHON() { throw JPypeException(JPError::_python_error, nullptr, JP_STACKINFO()); } #define JP_RAISE_OS_ERROR_UNIX(err, msg) { throw JPypeException(JPError::_os_error_unix, msg, err, JP_STACKINFO()); } #define JP_RAISE_OS_ERROR_WINDOWS(err, msg) { throw JPypeException(JPError::_os_error_windows, msg, err, JP_STACKINFO()); } #define JP_RAISE(type, msg) { throw JPypeException(JPError::_python_exc, type, msg, JP_STACKINFO()); } #ifndef PyObject_HEAD struct _object; using PyObject = _object; #endif #include "jp_pythontypes.h" template static inline JPPyObject JPPyTuple_Pack(T... args) { return JPPyObject::call(PyTuple_Pack(sizeof...(T), args...)); } // Base utility headers #include "jp_javaframe.h" #include "jp_context.h" #include "jp_exception.h" #include "jp_tracer.h" #include "jp_typemanager.h" #include "jp_encoding.h" #include "jp_modifier.h" #include "jp_match.h" // Other header files #include "jp_classhints.h" #include "jp_method.h" #include "jp_value.h" #include "jp_class.h" // Primitives classes #include "jp_primitivetype.h" #endif // _JPYPE_H_ jpype-1.6.0/native/common/jp_array.cpp000066400000000000000000000161661501674766700177730ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_arrayclass.h" #include "jp_primitive_accessor.h" // Note: java represents arrays of zero length as null, thus we // need to be careful to handle these properly. We need to // carry them around so that we can match types. JPArray::JPArray(const JPValue &value) : m_Object((jarray) value.getValue().l) { m_Class = dynamic_cast( value.getClass()); JPJavaFrame frame = JPJavaFrame::outer(); JP_TRACE_IN("JPArray::JPArray"); ASSERT_NOT_NULL(m_Class); JP_TRACE(m_Class->toString()); // We will use this during range checks, so cache it if (m_Object.get() == nullptr) m_Length = 0; // GCOVR_EXCL_LINE else m_Length = frame.GetArrayLength(m_Object.get()); m_Step = 1; m_Start = 0; m_Slice = false; JP_TRACE_OUT; } JPArray::JPArray(JPArray* instance, jsize start, jsize stop, jsize step) : m_Object((jarray) instance->getJava()) { JP_TRACE_IN("JPArray::JPArraySlice"); m_Class = instance->m_Class; m_Step = step * instance->m_Step; m_Start = instance->m_Start + instance->m_Step*start; if (step > 0) m_Length = (stop - start - 1 + step) / step; else m_Length = (stop - start + 1 + step) / step; if (m_Length < 0) m_Length = 0; // GCOVR_EXCL_LINE m_Slice = true; JP_TRACE_OUT; } JPArray::~JPArray() = default; jsize JPArray::getLength() const { return m_Length; } void JPArray::setRange(jsize start, jsize length, jsize step, PyObject* val) { JP_TRACE_IN("JPArray::setRange"); // Make sure it is an iterable before we start if (!PySequence_Check(val)) JP_RAISE(PyExc_TypeError, "can only assign a sequence"); JPJavaFrame frame = JPJavaFrame::outer(); JPClass* compType = m_Class->getComponentType(); JPPySequence seq = JPPySequence::use(val); long plength = (long) seq.size(); JP_TRACE("Verify lengths", length, plength); if ((long) length != plength) { // Python would allow mismatching size by growing or shrinking // the length of the array. But java arrays are immutable in length. std::stringstream out; out << "Slice assignment must be of equal lengths : " << length << " != " << plength; JP_RAISE(PyExc_ValueError, out.str()); } JP_TRACE("Call component set range"); jsize i0 = m_Start + m_Step*start; compType->setArrayRange(frame, m_Object.get(), i0, length, m_Step*step, val); JP_TRACE_OUT; } void JPArray::setItem(jsize ndx, PyObject* val) { JPJavaFrame frame = JPJavaFrame::outer(); JPClass* compType = m_Class->getComponentType(); if (ndx < 0) ndx += m_Length; if (ndx >= m_Length || ndx < 0) JP_RAISE(PyExc_IndexError, "java array assignment out of bounds"); compType->setArrayItem(frame, m_Object.get(), m_Start + ndx*m_Step, val); } JPPyObject JPArray::getItem(jsize ndx) { JPJavaFrame frame = JPJavaFrame::outer(); JPClass* compType = m_Class->getComponentType(); if (ndx < 0) ndx += m_Length; if (ndx >= m_Length || ndx < 0) { JP_RAISE(PyExc_IndexError, "array index out of bounds"); } return compType->getArrayItem(frame, m_Object.get(), m_Start + ndx * m_Step); } jarray JPArray::clone(JPJavaFrame& frame, PyObject* obj) { JPValue value = m_Class->newArray(frame, m_Length); JPClass* compType = m_Class->getComponentType(); auto out = (jarray) value.getValue().l; compType->setArrayRange(frame, out, 0, m_Length, 1, obj); return out; } JPArrayView::JPArrayView(JPArray* array) { JPJavaFrame frame = JPJavaFrame::outer(); m_Array = array; m_RefCount = 0; m_Buffer.obj = nullptr; m_Buffer.ndim = 1; m_Buffer.suboffsets = nullptr; auto *type = dynamic_cast( array->getClass()->getComponentType()); type->getView(*this); m_Strides[0] = m_Buffer.itemsize * array->m_Step; m_Shape[0] = array->m_Length; m_Buffer.buf = (char*) m_Memory + m_Buffer.itemsize * array->m_Start; m_Buffer.len = array->m_Length * m_Buffer.itemsize; m_Buffer.shape = m_Shape; m_Buffer.strides = m_Strides; m_Buffer.readonly = 1; m_Owned = false; } JPArrayView::JPArrayView(JPArray* array, jobject collection) { JP_TRACE_IN("JPArrayView::JPArrayView"); // All of the work has already been done by org.jpype.Utilities JPJavaFrame frame = JPJavaFrame::outer(); m_Array = array; jint len = frame.GetArrayLength((jarray) collection); jobject item0 = frame.GetObjectArrayElement((jobjectArray) collection, 0); jobject item1 = frame.GetObjectArrayElement((jobjectArray) collection, 1); // First element is the primitive type that we are packing the array from auto *componentType = dynamic_cast( frame.findClass((jclass) item0)); // Second element is the shape of the array from which we compute the // memory size, the shape, and strides int dims; Py_ssize_t itemsize; Py_ssize_t sz; { JPPrimitiveArrayAccessor accessor(frame, (jintArray) item1, &JPJavaFrame::GetIntArrayElements, &JPJavaFrame::ReleaseIntArrayElements); jint* shape2 = accessor.get(); dims = frame.GetArrayLength((jarray) item1); itemsize = componentType->getItemSize(); sz = itemsize; for (int i = 0; i < dims; ++i) { m_Shape[i] = shape2[i]; sz *= m_Shape[i]; } accessor.abort(); } Py_ssize_t stride = itemsize; for (int i = 0; i < dims; ++i) { int n = dims - 1 - i; m_Strides[n] = stride; stride *= m_Shape[n]; } m_RefCount = 0; m_Memory = new char[sz]; m_Owned = true; // All remaining elements are primitive arrays to be unpacked int offset = 0; Py_ssize_t last = m_Shape[dims - 1]; for (Py_ssize_t i = 0; i < len - 2; i++) { auto a1 = (jarray) frame.GetObjectArrayElement((jobjectArray) collection, (jsize) i + 2); componentType->copyElements(frame, a1, 0, (jsize) last, m_Memory, offset); offset += (int) (itemsize * last); frame.DeleteLocalRef(a1); } // Copy values into Python buffer for consumption m_Buffer.obj = nullptr; m_Buffer.ndim = dims; m_Buffer.suboffsets = nullptr; m_Buffer.itemsize = itemsize; m_Buffer.format = const_cast (componentType->getBufferFormat()); m_Buffer.buf = (char*) m_Memory + m_Buffer.itemsize * array->m_Start; m_Buffer.len = sz; m_Buffer.shape = m_Shape; m_Buffer.strides = m_Strides; m_Buffer.readonly = 1; JP_TRACE_OUT; // GCOVR_EXCL_LINE } JPArrayView::~JPArrayView() { if (m_Owned) delete [] (char*) m_Memory; } void JPArrayView::reference() { m_RefCount++; } bool JPArrayView::unreference() { m_RefCount--; auto *type = dynamic_cast( m_Array->getClass()->getComponentType()); if (m_RefCount == 0 && !m_Owned) type->releaseView(*this); return m_RefCount == 0; } jpype-1.6.0/native/common/jp_arrayclass.cpp000066400000000000000000000057561501674766700210240ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_arrayclass.h" #include "jp_context.h" #include "jp_stringtype.h" JPArrayClass::JPArrayClass(JPJavaFrame& frame, jclass cls, const string& name, JPClass* superClass, JPClass* componentType, jint modifiers) : JPClass(frame, cls, name, superClass, JPClassList(), modifiers) { m_ComponentType = componentType; } JPArrayClass::~JPArrayClass() = default; JPMatch::Type JPArrayClass::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPArrayClass::findJavaConversion"); if (nullConversion->matches(this, match) || objectConversion->matches(this, match) || bufferConversion->matches(this, match) || charArrayConversion->matches(this, match) || byteArrayConversion->matches(this, match) || sequenceConversion->matches(this, match) || hintsConversion->matches(this, match) ) return match.type; JP_TRACE("None"); return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPArrayClass::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); objectConversion->getInfo(this, info); charArrayConversion->getInfo(this, info); byteArrayConversion->getInfo(this, info); sequenceConversion->getInfo(this, info); hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); } JPPyObject JPArrayClass::convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) { JP_TRACE_IN("JPArrayClass::convertToPythonObject"); if (!cast) { if (value.l == nullptr) return JPPyObject::getNone(); } JPPyObject wrapper = PyJPClass_create(frame, this); JPPyObject obj = PyJPArray_create(frame, (PyTypeObject*) wrapper.get(), JPValue(this, value)); return obj; JP_TRACE_OUT; } jvalue JPArrayClass::convertToJavaVector(JPJavaFrame& frame, JPPyObjectVector& refs, jsize start, jsize end) { JP_TRACE_IN("JPArrayClass::convertToJavaVector"); auto length = (jsize) (end - start); jarray array = m_ComponentType->newArrayOf(frame, length); jvalue res; for (jsize i = start; i < end; i++) { m_ComponentType->setArrayItem(frame, array, i - start, refs[i]); } res.l = array; return res; JP_TRACE_OUT; } JPValue JPArrayClass::newArray(JPJavaFrame& frame, int length) { jvalue v; v.l = m_ComponentType->newArrayOf(frame, length); return JPValue(this, v); } jpype-1.6.0/native/common/jp_booleantype.cpp000066400000000000000000000235101501674766700211650ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_booleantype.h" #include "jp_boxedtype.h" JPBooleanType::JPBooleanType() : JPPrimitiveType("boolean") { } JPBooleanType::~JPBooleanType() = default; JPClass* JPBooleanType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Boolean; } JPPyObject JPBooleanType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { return JPPyObject::call(PyBool_FromLong(val.z)); } JPValue JPBooleanType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; field(v) = frame.CallBooleanMethodA(obj.getValue().l, frame.getContext()->_java_lang_Boolean->m_BooleanValueID, nullptr) != 0; return JPValue(this, v); } class JPConversionAsBoolean : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyBool_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_exact; } void getInfo(JPClass * cls, JPConversionInfo &info) override { PyList_Append(info.exact, (PyObject*) & PyBool_Type); } jvalue convert(JPMatch &match) override { jvalue res; jlong v = PyObject_IsTrue(match.object); if (v == -1) JP_PY_CHECK(); res.z = v != 0; return res; } } asBooleanExact; class JPConversionAsBooleanJBool : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == nullptr) return match.type = JPMatch::_none; match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; // search no further. } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_boolean->getHost()); unboxConversion->getInfo(cls, info); } } asBooleanJBool; class JPConversionAsBooleanLong : public JPConversionAsBoolean { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyLong_CheckExact(match.object) && !PyIndex_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsIndex")); PyList_Append(info.expl, proto.get()); } } asBooleanLong; class JPConversionAsBooleanNumber : public JPConversionAsBoolean { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyNumber_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_explicit; } void getInfo(JPClass * cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsFloat")); PyList_Append(info.expl, proto.get()); } } asBooleanNumber; JPMatch::Type JPBooleanType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPBooleanType::findJavaConversion", this); if (match.object == Py_None) return match.type = JPMatch::_none; if (asBooleanExact.matches(this, match) || asBooleanJBool.matches(this, match) || asBooleanLong.matches(this, match) || asBooleanNumber.matches(this, match) ) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPBooleanType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); asBooleanExact.getInfo(this, info); asBooleanJBool.getInfo(this, info); asBooleanLong.getInfo(this, info); asBooleanNumber.getInfo(this, info); PyList_Append(info.ret, (PyObject*) & PyBool_Type); } jarray JPBooleanType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewBooleanArray(sz); } JPPyObject JPBooleanType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticBooleanField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPBooleanType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetBooleanField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPBooleanType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticBooleanMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPBooleanType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallBooleanMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualBooleanMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPBooleanType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java boolean"); type_t val = field(match.convert()); frame.SetStaticBooleanField(c, fid, val); } void JPBooleanType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java boolean"); type_t val = field(match.convert()); frame.SetBooleanField(c, fid, val); } void JPBooleanType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPBooleanType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetBooleanArrayElements, &JPJavaFrame::ReleaseBooleanArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "z"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.z; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { int v = PyObject_IsTrue(seq[i].get()); if (v == -1) JP_PY_CHECK(); val[index] = v; } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPBooleanType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetBooleanArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPBooleanType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java boolean"); type_t val = field(match.convert()); frame.SetBooleanArrayRegion((array_t) a, ndx, 1, &val); } void JPBooleanType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetBooleanArrayElements( (jbooleanArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "?"; view.m_Buffer.itemsize = sizeof (jboolean); } void JPBooleanType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseBooleanArrayElements((jbooleanArray) view.m_Array->getJava(), (jboolean*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPBooleanType::getBufferFormat() { return "?"; } Py_ssize_t JPBooleanType::getItemSize() { return sizeof (jboolean); } void JPBooleanType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jboolean*) ((char*) memory + offset); frame.GetBooleanArrayRegion((jbooleanArray) a, start, len, b); } static void pack(jboolean* d, jvalue v) { *d = v.z; } PyObject *JPBooleanType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPBooleanType::newMultiArray"); return convertMultiArray( frame, this, &pack, "z", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_boxedtype.cpp000066400000000000000000000101121501674766700206410ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_boxedtype.h" JPBoxedType::JPBoxedType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers, JPPrimitiveType* primitiveType) : JPClass(frame, clss, name, super, interfaces, modifiers), m_PrimitiveType(primitiveType) { if (name != "java.lang.Void") { string s = string("(") + primitiveType->getTypeCode() + ")V"; m_CtorID = frame.GetMethodID(clss, "", s.c_str()); } m_DoubleValueID = nullptr; m_FloatValueID = nullptr; m_LongValueID = nullptr; m_IntValueID = nullptr; m_BooleanValueID = nullptr; m_CharValueID = nullptr; if (name != "java.lang.Void" && name != "java.lang.Boolean" && name != "java.lang.Character" ) { m_DoubleValueID = frame.GetMethodID(clss, "doubleValue", "()D"); m_FloatValueID = frame.GetMethodID(clss, "floatValue", "()F"); m_IntValueID = frame.GetMethodID(clss, "intValue", "()I"); m_LongValueID = frame.GetMethodID(clss, "longValue", "()J"); } if (name == "java.lang.Boolean") { m_BooleanValueID = frame.GetMethodID(clss, "booleanValue", "()Z"); } if (name == "java.lang.Character") { m_CharValueID = frame.GetMethodID(clss, "charValue", "()C"); } } JPBoxedType::~JPBoxedType() = default; JPMatch::Type JPBoxedType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPBoxedType::findJavaConversion"); JPClass::findJavaConversion(match); if (match.type != JPMatch::_none) return match.type; if (m_PrimitiveType->findJavaConversion(match) != JPMatch::_none) { JP_TRACE("Primitive", match.type); match.conversion = boxBooleanConversion; match.closure = this; if (match.type == JPMatch::_exact) return match.type = JPMatch::_implicit; return match.type = JPMatch::_explicit; } return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPBoxedType::getConversionInfo(JPConversionInfo &info) { JP_TRACE_IN("JPBoxedType::getConversionInfo"); JPJavaFrame frame = JPJavaFrame::outer(); m_PrimitiveType->getConversionInfo(info); JPPyObject::call(PyObject_CallMethod(info.expl, "extend", "O", info.implicit)); JPPyObject::call(PyObject_CallMethod(info.implicit, "clear", "")); JPPyObject::call(PyObject_CallMethod(info.implicit, "extend", "O", info.exact)); JPPyObject::call(PyObject_CallMethod(info.exact, "clear", "")); JPClass::getConversionInfo(info); JP_TRACE_OUT; } jobject JPBoxedType::box(JPJavaFrame &frame, jvalue v) { return frame.NewObjectA(m_Class.get(), m_CtorID, &v); } JPPyObject JPBoxedType::convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) { JPClass *cls = this; if (!cast) { // This loses type if (value.l == nullptr) { return JPPyObject::getNone(); } cls = frame.findClassForObject(value.l); if (cls != this) return cls->convertToPythonObject(frame, value, true); } JPPyObject wrapper = PyJPClass_create(frame, cls); JPPyObject obj; JPContext *context = JPContext_global; if (this->getPrimitive() == context->_char) { jchar value2 = 0; // Not null get the char value if (value.l != nullptr) value2 = context->_char->getValueFromObject(frame, JPValue(this, value)).getValue().c; // Create a char string object obj = JPPyObject::call(PyJPChar_Create((PyTypeObject*) wrapper.get(), value2)); } else obj = PyJPNumber_create(frame, wrapper, JPValue(cls, value)); PyJPValue_assignJavaSlot(frame, obj.get(), JPValue(cls, value)); return obj; } jpype-1.6.0/native/common/jp_buffer.cpp000066400000000000000000000035761501674766700201270ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_buffer.h" #include "jp_primitive_accessor.h" #include "jp_buffertype.h" JPBuffer::JPBuffer(const JPValue &value) : m_Object(value.getValue().l) { m_Class = dynamic_cast( value.getClass()); JPJavaFrame frame = JPJavaFrame::outer(); JP_TRACE_IN("JPBuffer::JPBuffer"); m_Address = frame.GetDirectBufferAddress(m_Object.get()); m_Capacity = (Py_ssize_t) frame.GetDirectBufferCapacity(m_Object.get()); m_Buffer.buf = m_Address; m_Buffer.format = m_Format; m_Format[0] = frame.orderBuffer(m_Object.get()) ? '<' : '>'; m_Format[1] = m_Class->getType()[0]; m_Format[2] = 0; m_Buffer.itemsize = (Py_ssize_t) m_Class->getSize(); m_Buffer.ndim = 1; m_Buffer.readonly = frame.isBufferReadOnly(m_Object.get()); m_Buffer.shape = &m_Capacity; m_Buffer.strides = &m_Buffer.itemsize; m_Buffer.suboffsets = 0; m_Buffer.len = m_Buffer.itemsize * m_Capacity; JP_TRACE_OUT; // GCOVR_EXCL_LINE } JPBuffer::~JPBuffer() = default; bool JPBuffer::isReadOnly() const { return m_Buffer.readonly != 0; } Py_buffer& JPBuffer::getView() { return m_Buffer; } bool JPBuffer::isValid() const { return m_Capacity != -1; } jpype-1.6.0/native/common/jp_buffertype.cpp000066400000000000000000000045201501674766700210170ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_context.h" #include "jp_buffertype.h" JPBufferType::JPBufferType(JPJavaFrame& frame, jclass cls, const string& name, JPClass* superClass, const JPClassList& interfaces, jint modifiers) : JPClass(frame, cls, name, superClass, interfaces, modifiers) { // Use name to get the type if (name == "java.nio.Buffer") { m_Type = "b"; m_Size = 1; } else if (name == "java.nio.ByteBuffer") { m_Type = "b"; m_Size = 1; } else if (name == "java.nio.CharBuffer") { m_Type = "H"; m_Size = 2; } else if (name == "java.nio.ShortBuffer") { m_Type = "h"; m_Size = 2; } else if (name == "java.nio.IntBuffer") { m_Type = "i"; m_Size = 4; } else if (name == "java.nio.LongBuffer") { m_Type = "q"; m_Size = 8; } else if (name == "java.nio.FloatBuffer") { m_Type = "f"; m_Size = 4; } else if (name == "java.nio.DoubleBuffer") { m_Type = "d"; m_Size = 8; } else { auto* super = dynamic_cast (m_SuperClass); if (super == nullptr) JP_RAISE(PyExc_TypeError, "Unsupported buffer type"); // GCOVR_EXCL_LINE m_Type = super->m_Type; m_Size = super->m_Size; } } JPBufferType::~JPBufferType() = default; JPPyObject JPBufferType::convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) { JP_TRACE_IN("JPBufferClass::convertToPythonObject"); if (!cast && value.l == nullptr) return JPPyObject::getNone(); // GCOVR_EXCL_LINE JPPyObject wrapper = PyJPClass_create(frame, this); JPPyObject obj = PyJPBuffer_create(frame, (PyTypeObject*) wrapper.get(), JPValue(this, value)); return obj; JP_TRACE_OUT; // GCOVR_EXCL_LINE } jpype-1.6.0/native/common/jp_bytetype.cpp000066400000000000000000000206041501674766700205120ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_bytetype.h" JPByteType::JPByteType() : JPPrimitiveType("byte") { } JPByteType::~JPByteType() = default; JPClass* JPByteType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Byte; } JPPyObject JPByteType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { JPPyObject tmp = JPPyObject::call(PyLong_FromLong(field(val))); JPPyObject out = JPPyObject::call(convertLong(getHost(), (PyLongObject*) tmp.get())); PyJPValue_assignJavaSlot(frame, out.get(), JPValue(this, val)); return out; } JPValue JPByteType::getValueFromObject(JPJavaFrame &frame, const JPValue& obj) { jvalue v; jobject jo = obj.getValue().l; auto* jb = dynamic_cast( frame.findClassForObject(jo)); field(v) = (type_t) frame.CallIntMethodA(jo, jb->m_IntValueID, nullptr); return JPValue(this, v); } JPConversionLong byteConversion; JPConversionLongNumber byteNumberConversion; class JPConversionJByte : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == nullptr) return match.type = JPMatch::_none; match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; // stop the search } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_byte->getHost()); unboxConversion->getInfo(cls, info); } } jbyteConversion; JPMatch::Type JPByteType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPByteType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (jbyteConversion.matches(this, match) || byteConversion.matches(this, match) || byteNumberConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPByteType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); jbyteConversion.getInfo(this, info); byteConversion.getInfo(this, info); byteNumberConversion.getInfo(this, info); PyList_Append(info.ret, (PyObject*) JPContext_global->_int->getHost()); } jarray JPByteType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewByteArray(sz); } JPPyObject JPByteType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticByteField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPByteType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetByteField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPByteType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticByteMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPByteType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallByteMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualByteMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPByteType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java byte"); type_t val = field(match.convert()); frame.SetStaticByteField(c, fid, val); } void JPByteType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java byte"); type_t val = field(match.convert()); frame.SetByteField(c, fid, val); } void JPByteType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPByteType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetByteArrayElements, &JPJavaFrame::ReleaseByteArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "b"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.b; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { PyObject *item = seq[i].get(); if (!PyIndex_Check(item)) { PyErr_Format(PyExc_TypeError, "Unable to implicitly convert '%s' to byte", Py_TYPE(item)->tp_name); JP_RAISE_PYTHON(); } jlong v = PyLong_AsLongLong(item); if (v == -1) JP_PY_CHECK(); val[index] = (type_t) assertRange(v); } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPByteType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetByteArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPByteType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java byte"); type_t val = field(match.convert()); frame.SetByteArrayRegion((array_t) a, ndx, 1, &val); } void JPByteType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetByteArrayElements( (jbyteArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "b"; view.m_Buffer.itemsize = sizeof (jbyte); } void JPByteType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseByteArrayElements((jbyteArray) view.m_Array->getJava(), (jbyte*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPByteType::getBufferFormat() { return "b"; } Py_ssize_t JPByteType::getItemSize() { return sizeof (jbyte); } void JPByteType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jbyte*) ((char*) memory + offset); frame.GetByteArrayRegion((jbyteArray) a, start, len, b); } static void pack(jbyte* d, jvalue v) { *d = v.b; } PyObject *JPByteType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPByteType::newMultiArray"); return convertMultiArray( frame, this, &pack, "b", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_chartype.cpp000066400000000000000000000204421501674766700204640ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_chartype.h" #include "jp_boxedtype.h" JPCharType::JPCharType() : JPPrimitiveType("char") { } JPCharType::~JPCharType() = default; JPClass* JPCharType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Character; } JPValue JPCharType::newInstance(JPJavaFrame& frame, JPPyObjectVector& args) { // This is only callable from one location so error checking is minimal if (args.size() != 1 || !PyIndex_Check(args[0])) JP_RAISE(PyExc_TypeError, "bad args"); // GCOVR_EXCL_LINE jvalue jv; // This is a cast so we must not fail int overflow; jv.c = PyLong_AsLongAndOverflow(args[0], &overflow); return JPValue(this, jv); } JPPyObject JPCharType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { // if (!cast) // { JPPyObject out = JPPyObject::call(PyJPChar_Create((PyTypeObject*) _JChar, val.c)); PyJPValue_assignJavaSlot(frame, out.get(), JPValue(this, val)); return out; // } // JPPyObject tmp = JPPyObject::call(PyLong_FromLong(field(val))); // JPPyObject out = JPPyObject::call(convertLong(getHost(), (PyLongObject*) tmp.get())); // return out; } JPValue JPCharType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; field(v) = frame.CallCharMethodA(obj.getValue().l, frame.getContext()->_java_lang_Character->m_CharValueID, nullptr); return JPValue(this, v); } class JPConversionAsChar : public JPConversion { using base_t = JPCharType; public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionAsChar::matches"); if (!JPPyString::checkCharUTF16(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; JP_TRACE_OUT; // GCOVR_EXCL_LINE } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.implicit, (PyObject*) & PyUnicode_Type); } jvalue convert(JPMatch &match) override { jvalue res; res.c = JPPyString::asCharUTF16(match.object); return res; } } asCharConversion; class JPConversionAsJChar : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == nullptr) return match.type = JPMatch::_none; match.type = JPMatch::_none; // Exact // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; // stop the search } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_char->getHost()); unboxConversion->getInfo(cls, info); } } asJCharConversion; JPMatch::Type JPCharType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPCharType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (asJCharConversion.matches(this, match) || asCharConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPCharType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); asJCharConversion.getInfo(this, info); asCharConversion.getInfo(this, info); PyList_Append(info.ret, (PyObject*) JPContext_global->_char->getHost()); } jarray JPCharType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewCharArray(sz); } JPPyObject JPCharType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticCharField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPCharType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetCharField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPCharType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticCharMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPCharType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallCharMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualCharMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPCharType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java char"); type_t val = field(match.convert()); frame.SetStaticCharField(c, fid, val); } void JPCharType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java char"); type_t val = field(match.convert()); frame.SetCharField(c, fid, val); } void JPCharType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPCharType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetCharArrayElements, &JPJavaFrame::ReleaseCharArrayElements); type_t* val = accessor.get(); JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { jchar v = JPPyString::asCharUTF16(seq[i].get()); JP_PY_CHECK(); val[index] = (type_t) v; } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPCharType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetCharArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPCharType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java char"); type_t val = field(match.convert()); frame.SetCharArrayRegion((array_t) a, ndx, 1, &val); } void JPCharType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetCharArrayElements( (jcharArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "H"; view.m_Buffer.itemsize = sizeof (jchar); } void JPCharType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseCharArrayElements((jcharArray) view.m_Array->getJava(), (jchar*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPCharType::getBufferFormat() { return "H"; } Py_ssize_t JPCharType::getItemSize() { return sizeof (jchar); } void JPCharType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jchar*) ((char*) memory + offset); frame.GetCharArrayRegion((jcharArray) a, start, len, b); } static void pack(jchar* d, jvalue v) { *d = v.c; } PyObject *JPCharType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPCharType::newMultiArray"); return convertMultiArray( frame, this, &pack, "c", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_class.cpp000066400000000000000000000264211501674766700177550ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_field.h" #include "jp_methoddispatch.h" #include "jp_method.h" JPClass::JPClass( const string& name, jint modifiers) { m_CanonicalName = name; m_SuperClass = nullptr; m_Interfaces = JPClassList(); m_Modifiers = modifiers; } JPClass::JPClass(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, const JPClassList& interfaces, jint modifiers) : m_Class(frame, clss) { m_CanonicalName = name; m_SuperClass = super; m_Interfaces = interfaces; m_Modifiers = modifiers; } JPClass::~JPClass()= default; void JPClass::setHost(PyObject* host) { m_Host = JPPyObject::use(host); } void JPClass::setHints(PyObject* host) { m_Hints = JPPyObject::use(host); } jclass JPClass::getJavaClass() const { jclass cls = m_Class.get(); // This sanity check should not be possible to exercise if (cls == nullptr) JP_RAISE(PyExc_RuntimeError, "Class is null"); // GCOVR_EXCL_LINE return cls; } void JPClass::ensureMembers(JPJavaFrame& frame) { JPContext* context = JPContext_global; JPTypeManager* typeManager = context->getTypeManager(); typeManager->populateMembers(this); } void JPClass::assignMembers(JPMethodDispatch* ctor, JPMethodDispatchList& methods, JPFieldList& fields) { m_Constructors = ctor; m_Methods = methods; m_Fields = fields; } // JPValue JPClass::newInstance(JPJavaFrame& frame, JPPyObjectVector& args) { if (m_Constructors == nullptr) { if (this->isInterface()) { JP_RAISE(PyExc_TypeError, "Cannot create Java interface instances"); } else { JP_RAISE(PyExc_TypeError, "Java class has no constructors"); } } return m_Constructors->invokeConstructor(frame, args); } JPClass* JPClass::newArrayType(JPJavaFrame &frame, long d) { if (d < 0 || d > 255) JP_RAISE(PyExc_ValueError, "Invalid array dimensions"); std::stringstream ss; for (long i = 0; i < d; ++i) ss << "["; if (isPrimitive()) ss << (dynamic_cast( this))->getTypeCode(); else if (isArray()) ss << getName(); else ss << "L" << getName() << ";"; return frame.findClassByName(ss.str()); } jarray JPClass::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewObjectArray(sz, getJavaClass(), nullptr); } // // // GCOVR_EXCL_START // This is currently only used in tracing string JPClass::toString() const { // This sanity check will not be hit in normal operation if (JPContext_global == nullptr) return m_CanonicalName; // GCOVR_EXCL_LINE JPJavaFrame frame = JPJavaFrame::outer(); return frame.toString(m_Class.get()); } // GCOVR_EXCL_STOP string JPClass::getName() const { // This sanity check will not be hit in normal operation if (JPContext_global == nullptr) return m_CanonicalName; // GCOVR_EXCL_LINE JPJavaFrame frame = JPJavaFrame::outer(); return frame.toString(frame.CallObjectMethodA( (jobject) m_Class.get(), JPContext_global->m_Class_GetNameID, nullptr)); } // // JPPyObject JPClass::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { JP_TRACE_IN("JPClass::getStaticField"); jobject r = frame.GetStaticObjectField(c, fid); JPClass* type = this; if (r != nullptr) type = frame.findClassForObject(r); jvalue v; v.l = r; return type->convertToPythonObject(frame, v, false); JP_TRACE_OUT; } JPPyObject JPClass::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { JP_TRACE_IN("JPClass::getField"); jobject r = frame.GetObjectField(c, fid); JPClass* type = this; if (r != nullptr) type = frame.findClassForObject(r); jvalue v; v.l = r; return type->convertToPythonObject(frame, v, false); JP_TRACE_OUT; } JPPyObject JPClass::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { JP_TRACE_IN("JPClass::invokeStatic"); jvalue v; { JPPyCallRelease call; v.l = frame.CallStaticObjectMethodA(claz, mth, val); } JPClass *type = this; if (v.l != nullptr) type = frame.findClassForObject(v.l); return type->convertToPythonObject(frame, v, false); JP_TRACE_OUT; } JPPyObject JPClass::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { JP_TRACE_IN("JPClass::invoke"); jvalue v; // Call method { JPPyCallRelease call; if (obj == nullptr) JP_RAISE(PyExc_ValueError, "method called on null object"); if (clazz == nullptr) v.l = frame.CallObjectMethodA(obj, mth, val); else v.l = frame.CallNonvirtualObjectMethodA(obj, clazz, mth, val); } // Get the return type JPClass *type = this; if (v.l != nullptr) type = frame.findClassForObject(v.l); return type->convertToPythonObject(frame, v, false); JP_TRACE_OUT; } void JPClass::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JP_TRACE_IN("JPClass::setStaticField"); JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) { std::stringstream err; err << "unable to convert to " << getCanonicalName(); JP_RAISE(PyExc_TypeError, err.str()); } jobject val = match.convert().l; frame.SetStaticObjectField(c, fid, val); JP_TRACE_OUT; } void JPClass::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JP_TRACE_IN("JPClass::setField"); JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) { std::stringstream err; err << "unable to convert to " << getCanonicalName(); JP_RAISE(PyExc_TypeError, err.str()); } jobject val = match.convert().l; frame.SetObjectField(c, fid, val); JP_TRACE_OUT; } void JPClass::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* vals) { JP_TRACE_IN("JPClass::setArrayRange"); auto array = (jobjectArray) a; // Verify before we start the conversion, as we wont be able // to abort once we start JPPySequence seq = JPPySequence::use(vals); JP_TRACE("Verify argument types"); for (int i = 0; i < length; i++) { JPPyObject v = seq[i]; JPMatch match(&frame, v.get()); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert"); } JP_TRACE("Copy"); int index = start; for (int i = 0; i < length; i++, index += step) { JPPyObject v = seq[i]; JPMatch match(&frame, v.get()); findJavaConversion(match); frame.SetObjectArrayElement(array, index, match.convert().l); } JP_TRACE_OUT; } void JPClass::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* val) { JP_TRACE_IN("JPClass::setArrayItem"); JPMatch match(&frame, val); findJavaConversion(match); JP_TRACE("Type", getCanonicalName()); if ( match.type < JPMatch::_implicit) { JP_RAISE(PyExc_TypeError, "Unable to convert"); } jvalue v = match.convert(); frame.SetObjectArrayElement((jobjectArray) a, ndx, v.l); JP_TRACE_OUT; } JPPyObject JPClass::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { JP_TRACE_IN("JPClass::getArrayItem"); auto array = (jobjectArray) a; jobject obj = frame.GetObjectArrayElement(array, ndx); JPClass *retType = this; jvalue v; v.l = obj; if (obj != nullptr) retType = frame.findClassForObject(v.l); return retType->convertToPythonObject(frame, v, false); JP_TRACE_OUT; } // // JPValue JPClass::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { JP_TRACE_IN("JPClass::getValueFromObject"); return JPValue(this, obj.getJavaObject()); JP_TRACE_OUT; } JPPyObject JPClass::convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) { JP_TRACE_IN("JPClass::convertToPythonObject"); JPClass *cls = this; if (!cast) { // Returning None likely incorrect from java prospective. // Java still knows the type of null objects thus // converting to None would pose a problem as we lose type. // We would need subclass None for this to make sense so we // can carry both the type and the null, but Python considers // None a singleton so this is not an option. // // Of course if we don't mind that "Object is None" would // fail, but "Object == None" would be true, the we // could support null objects properly. However, this would // need to work as "None == Object" which may be hard to // achieve. // // We will still need to have the concept of null objects // but we can get those through JObject(None, cls). if (value.l == nullptr) { return JPPyObject::getNone(); } cls = frame.findClassForObject(value.l); if (cls != this) return cls->convertToPythonObject(frame, value, true); } JPPyObject obj; JPPyObject wrapper = PyJPClass_create(frame, cls); if (isThrowable()) { JPPyObject tuple0; if (value.l == nullptr) { tuple0 = JPPyObject::call(PyTuple_New(0)); } else { jstring m = frame.getMessage((jthrowable) value.l); if (m != nullptr) { tuple0 = JPPyTuple_Pack( JPPyString::fromStringUTF8(frame.toStringUTF8(m)).get()); } else { tuple0 = JPPyTuple_Pack( JPPyString::fromStringUTF8(frame.toString(value.l)).get()); } } JPPyObject tuple1 = JPPyTuple_Pack(_JObjectKey, tuple0.get()); // Exceptions need new and init obj = JPPyObject::call(PyObject_Call(wrapper.get(), tuple1.get(), nullptr)); } else { PyTypeObject *type = ((PyTypeObject*) wrapper.get()); // Simple objects don't have a new or init function PyObject *obj2 = type->tp_alloc(type, 0); JP_PY_CHECK(); obj = JPPyObject::claim(obj2); } // Fill in the Java slot PyJPValue_assignJavaSlot(frame, obj.get(), JPValue(cls, value)); return obj; JP_TRACE_OUT; } JPMatch::Type JPClass::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPClass::findJavaConversion"); if (nullConversion->matches(this, match) || objectConversion->matches(this, match) || proxyConversion->matches(this, match) || hintsConversion->matches(this, match)) return match.type; JP_TRACE("No match"); return match.type = JPMatch::_none; JP_TRACE_OUT; } PyObject* JPClass::getHints() { PyObject* out = m_Hints.get(); if (out != nullptr) return out; // Force creation JPJavaFrame frame = JPJavaFrame::outer(); PyJPClass_create(frame, this); return m_Hints.get(); } void JPClass::getConversionInfo(JPConversionInfo &info) { JP_TRACE_IN("JPClass::getConversionInfo"); JPJavaFrame frame = JPJavaFrame::outer(); objectConversion->getInfo(this, info); hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); JP_TRACE_OUT; } // // bool JPClass::isAssignableFrom(JPJavaFrame& frame, JPClass* o) { return frame.IsAssignableFrom(m_Class.get(), o->getJavaClass()) != 0; } // jpype-1.6.0/native/common/jp_classhints.cpp000066400000000000000000000665511501674766700210330ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include "jpype.h" #include "jp_arrayclass.h" #include "jp_classhints.h" #include "jp_proxy.h" #include "jp_stringtype.h" #include "pyjp.h" JPMatch::JPMatch() { conversion = nullptr; frame = nullptr; object = nullptr; type = JPMatch::_none; slot = (JPValue*) - 1; closure = nullptr; } JPMatch::JPMatch(JPJavaFrame *fr, PyObject *obj) { conversion = nullptr; frame = fr; object = obj; type = JPMatch::_none; slot = (JPValue*) - 1; closure = nullptr; } JPValue *JPMatch::getJavaSlot() { if (slot == (JPValue*) - 1) return slot = PyJPValue_getJavaSlot(object); return slot; } jvalue JPMatch::convert() { // Sanity check, this should not happen if (conversion == nullptr) JP_RAISE(PyExc_SystemError, "Fail in conversion"); // GCOVR_EXCL_LINE return conversion->convert(*this); } JPMethodMatch::JPMethodMatch(JPJavaFrame &frame, JPPyObjectVector& args, bool callInstance) : m_Arguments(args.size()) { m_Type = JPMatch::_none; m_IsVarIndirect = false; m_Overload = nullptr; m_Offset = 0; m_Skip = 0; m_Hash = callInstance ? 0 : 1000; for (size_t i = 0; i < args.size(); ++i) { PyObject *arg = args[i]; m_Arguments[i] = JPMatch(&frame, arg); // This is an LCG used to compute a hash code for the incoming // arguments using (A*X+A_i) mod2^64 where A_i is the address of each // type the argument list. The hash will be checked to avoid needing // to resolve the method if the same overload is called twice. There // is only a speed cost if there is a collision, so we don't need to // prove this is a perfect hash function. m_Hash *= 0x10523C01; m_Hash += (long) (Py_TYPE(arg)); } } JPConversion::~JPConversion() = default; JPClassHints::JPClassHints() { m_ConvertJava = false; } JPClassHints::~JPClassHints() { for (auto & conversion : conversions) { delete conversion; } conversions.clear(); } JPMatch::Type JPClassHints::getConversion(JPMatch& match, JPClass *cls) { JPConversion *best = nullptr; for (auto & conversion : conversions) { JPMatch::Type quality = conversion->matches(cls, match); if (quality > JPMatch::_explicit) return match.type; if (quality != JPMatch::_none) best = conversion; } match.conversion = best; if (best == nullptr) return match.type = JPMatch::_none; return match.type = JPMatch::_explicit; } void JPIndexConversion::getInfo(JPClass *cls, JPConversionInfo &info) { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsIndex")); PyList_Append(info.implicit, proto.get()); } void JPNumberConversion::getInfo(JPClass *cls, JPConversionInfo &info) { JPIndexConversion::getInfo(cls, info); PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsFloat")); PyList_Append(info.implicit, proto.get()); } /** * Conversion for all user specified conversions. */ class JPPythonConversion : public JPConversion { public: explicit JPPythonConversion(PyObject *method) { method_ = JPPyObject::use(method); } ~JPPythonConversion() override = default; jvalue convert(JPMatch &match) override { JP_TRACE_IN("JPPythonConversion::convert"); JPClass *cls = ((JPClass*) match.closure); JPPyObject args = JPPyTuple_Pack(cls->getHost(), match.object); JPPyObject ret = JPPyObject::call(PyObject_Call(method_.get(), args.get(), nullptr)); JPValue *value = PyJPValue_getJavaSlot(ret.get()); if (value != nullptr) { jvalue v = value->getValue(); JP_TRACE("Value", v.l); v.l = match.frame->NewLocalRef(v.l); return v; } JPProxy *proxy = PyJPProxy_getJPProxy(ret.get()); if (proxy != nullptr) { jvalue v = proxy->getProxy(); JP_TRACE("Proxy", v.l); v.l = match.frame->NewLocalRef(v.l); return v; } JP_RAISE(PyExc_TypeError, "Bad type conversion"); JP_TRACE_OUT; } private: JPPyObject method_; } ; // class JPAttributeConversion : public JPPythonConversion { public: JPAttributeConversion(string attribute, PyObject *method) : JPPythonConversion(method), attribute_(std::move(attribute)) { } ~JPAttributeConversion() override = default; JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPAttributeConversion::matches"); JPPyObject attr = JPPyObject::accept(PyObject_GetAttrString(match.object, attribute_.c_str())); if (attr.isNull()) return JPMatch::_none; match.conversion = this; match.closure = cls; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.attributes, JPPyString::fromStringUTF8(attribute_).get()); } private: std::string attribute_; } ; void JPClassHints::addAttributeConversion(const string &attribute, PyObject *conversion) { JP_TRACE_IN("JPClassHints::addAttributeConversion", this); JP_TRACE(attribute); conversions.push_back(new JPAttributeConversion(attribute, conversion)); JP_TRACE_OUT; } // // class JPNoneConversion : public JPConversion { public: explicit JPNoneConversion(PyObject *type) { type_ = JPPyObject::use(type); } ~JPNoneConversion() override = default; JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPTypeConversion::matches"); if (!PyObject_IsInstance(match.object, type_.get())) return JPMatch::_none; match.closure = cls; match.conversion = this; match.type = JPMatch::_none; return JPMatch::_implicit; // Prevent further searching JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.none, type_.get()); } jvalue convert(JPMatch &match) override { return jvalue(); } private: JPPyObject type_; } ; class JPTypeConversion : public JPPythonConversion { public: JPTypeConversion(PyObject *type, PyObject *method, bool exact) : JPPythonConversion(method), exact_(exact) { type_ = JPPyObject::use(type); } ~JPTypeConversion() override = default; JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPTypeConversion::matches"); if ((exact_ && ((PyObject*) Py_TYPE(match.object)) == type_.get()) || PyObject_IsInstance(match.object, type_.get())) { match.closure = cls; match.conversion = this; return match.type = JPMatch::_implicit; } return JPMatch::_none; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.implicit, type_.get()); } private: JPPyObject type_; bool exact_; } ; void JPClassHints::addTypeConversion(PyObject *type, PyObject *method, bool exact) { JP_TRACE_IN("JPClassHints::addTypeConversion", this); if (PyJPClass_Check(type)) m_ConvertJava = true; conversions.push_back(new JPTypeConversion(type, method, exact)); JP_TRACE_OUT; } void JPClassHints::excludeConversion(PyObject *type) { JP_TRACE_IN("JPClassHints::addTypeConversion", this); conversions.push_front(new JPNoneConversion(type)); JP_TRACE_OUT; } void JPClassHints::getInfo(JPClass *cls, JPConversionInfo &info) { for (auto iter = conversions.begin(); iter != conversions.end(); ++iter) { (*iter)->getInfo(cls, info); } } class JPHintsConversion : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { auto *pyhints = (PyJPClassHints*) cls->getHints(); JPClassHints *hints = pyhints->m_Hints; hints->getConversion(match, cls); return match.type; } void getInfo(JPClass *cls, JPConversionInfo &info) override { auto *pyhints = (PyJPClassHints*) cls->getHints(); JPClassHints *hints = pyhints->m_Hints; hints->getInfo(cls, info); } jvalue convert(JPMatch &match) override { return jvalue(); } } _hintsConversion; // class JPConversionCharArray : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionCharArray::matches"); auto* acls = dynamic_cast( cls); if (match.frame == nullptr || !JPPyString::check(match.object) || acls->getComponentType() != JPContext_global->_char) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { auto* acls = dynamic_cast( cls); if (acls->getComponentType() != JPContext_global->_char) return; PyList_Append(info.implicit, (PyObject*) & PyUnicode_Type); } jvalue convert(JPMatch &match) override { JPJavaFrame *frame = match.frame; JP_TRACE("char[]"); jvalue res; // Convert to a string string str = JPPyString::asStringUTF8(match.object); // Convert to new java string jstring jstr = frame->fromStringUTF8(str); // call toCharArray() res.l = frame->toCharArray(jstr); return res; } } _charArrayConversion; class JPConversionByteArray : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionByteArray::matches"); auto* acls = dynamic_cast( cls); if (match.frame == nullptr || !PyBytes_Check(match.object) || acls->getComponentType() != JPContext_global->_byte) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { auto* acls = dynamic_cast( cls); if (acls->getComponentType() != JPContext_global->_byte) return; PyList_Append(info.implicit, (PyObject*) & PyBytes_Type); } jvalue convert(JPMatch &match) override { JPJavaFrame frame(*match.frame); jvalue res; Py_ssize_t size = 0; char *buffer = nullptr; PyBytes_AsStringAndSize(match.object, &buffer, &size); // internal reference jbyteArray byteArray = frame.NewByteArray((jsize) size); frame.SetByteArrayRegion(byteArray, 0, (jsize) size, (jbyte*) buffer); res.l = frame.keep(byteArray); return res; } } _byteArrayConversion; class JPConversionBuffer : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionBuffer::matches"); auto *acls = dynamic_cast( cls); JPClass *componentType = acls->getComponentType(); if ( !componentType->isPrimitive()) return match.type = JPMatch::_none; // If is isn't a buffer we can skip JPPyBuffer buffer(match.object, PyBUF_ND | PyBUF_FORMAT); if (!buffer.valid()) { PyErr_Clear(); return match.type = JPMatch::_none; } // If it is a buffer we only need to test the first item in the list JPPySequence seq = JPPySequence::use(match.object); jlong length = seq.size(); if (length == -1 && PyErr_Occurred()) { PyErr_Clear(); return match.type = JPMatch::_none; } match.type = JPMatch::_implicit; if (length > 0) { JPPyObject item = seq[0]; JPMatch imatch(match.frame, item.get()); componentType->findJavaConversion(imatch); if (imatch.type < match.type) match.type = imatch.type; } match.closure = cls; match.conversion = bufferConversion; return match.type; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { // This will be covered by Sequence } jvalue convert(JPMatch &match) override { JP_TRACE_IN("JPConversionBuffer::convert"); JPJavaFrame frame(*match.frame); jvalue res; auto *acls = (JPArrayClass *) match.closure; auto length = (jsize) PySequence_Length(match.object); JPClass *ccls = acls->getComponentType(); jarray array = ccls->newArrayOf(frame, (jsize) length); ccls->setArrayRange(frame, array, 0, length, 1, match.object); res.l = frame.keep(array); return res; JP_TRACE_OUT; } } _bufferConversion; class JPConversionSequence : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionSequence::matches"); if ( !PySequence_Check(match.object) || JPPyString::check(match.object)) return match.type = JPMatch::_none; auto *acls = dynamic_cast( cls); JPClass *componentType = acls->getComponentType(); JPPySequence seq = JPPySequence::use(match.object); jlong length = seq.size(); if (length==-1 && PyErr_Occurred()) { PyErr_Clear(); return match.type = JPMatch::_none; } match.type = JPMatch::_implicit; for (jlong i = 0; i < length && match.type > JPMatch::_none; i++) { // This is a special case. Sequences produce new references // so we must hold the reference in a container while the // the match is caching it. JPPyObject item = seq[i]; JPMatch imatch(match.frame, item.get()); componentType->findJavaConversion(imatch); if (imatch.type < match.type) match.type = imatch.type; } match.closure = cls; match.conversion = sequenceConversion; return match.type; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "Sequence")); PyList_Append(info.implicit, proto.get()); auto* acls = dynamic_cast( cls); if (acls->getComponentType() == JPContext_global->_char) return; PyList_Append(info.none, (PyObject*) & PyUnicode_Type); } jvalue convert(JPMatch &match) override { JPJavaFrame frame(*match.frame); jvalue res; auto *acls = (JPArrayClass *) match.closure; auto length = (jsize) PySequence_Length(match.object); JPClass *ccls = acls->getComponentType(); jarray array = ccls->newArrayOf(frame, (jsize) length); ccls->setArrayRange(frame, array, 0, length, 1, match.object); res.l = frame.keep(array); return res; } } _sequenceConversion; class JPConversionNull : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionNull::matches"); if (match.object != Py_None) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { } jvalue convert(JPMatch &match) override { jvalue v; v.l = nullptr; return v; } } _nullConversion; class JPConversionClass : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionClass::matches"); if (match.frame == nullptr) return match.type = JPMatch::_none; JPClass* cls2 = PyJPClass_getJPClass(match.object); if (cls2 == nullptr) return match.type = JPMatch::_none; match.conversion = this; match.closure = cls2; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPJavaFrame frame = JPJavaFrame::outer(); PyList_Append(info.implicit, (PyObject*) PyJPClass_Type); } jvalue convert(JPMatch &match) override { jvalue res; auto* cls2 = (JPClass*) match.closure; res.l = match.frame->NewLocalRef(cls2->getJavaClass()); return res; } } _classConversion; class JPConversionObject : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionObject::matches"); JPValue *value = match.getJavaSlot(); if (value == nullptr || match.frame == nullptr) return match.type = JPMatch::_none; match.conversion = this; JPClass *oc = value->getClass(); if (oc == nullptr) return match.type = JPMatch::_none; if (oc == cls) { // hey, this is me! :) return match.type = JPMatch::_exact; } bool assignable = match.frame->IsAssignableFrom(oc->getJavaClass(), cls->getJavaClass()) != 0; JP_TRACE("assignable", assignable, oc->getCanonicalName(), cls->getCanonicalName()); match.type = (assignable ? JPMatch::_derived : JPMatch::_none); // User has request a Java class to class conversion. We must pass through check it. if (!assignable) { auto *pyhints = (PyJPClassHints*) cls->getHints(); JPClassHints *hints = pyhints->m_Hints; if (hints->m_ConvertJava) return match.type; } // This is the one except to the conversion rule patterns. // If it is a Java value then we must prevent it from proceeding // through the conversion rules even if it was not a match. // Thus the return result and the match type differ here. return JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPJavaFrame frame = JPJavaFrame::outer(); PyList_Append(info.exact, PyJPClass_create(frame, cls).get()); } jvalue convert(JPMatch &match) override { jvalue res; JPValue* value = match.getJavaSlot(); res.l = match.frame->NewLocalRef(value->getValue().l); return res; } } _objectConversion; JPMatch::Type JPConversionJavaValue::matches(JPClass *cls, JPMatch &match) { JP_TRACE_IN("JPConversionJavaValue::matches"); JPValue *slot = match.getJavaSlot(); if (slot == nullptr || slot->getClass() != cls) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_exact; JP_TRACE_OUT; } void JPConversionJavaValue::getInfo(JPClass *cls, JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); PyList_Append(info.exact, PyJPClass_create(frame, cls).get()); } jvalue JPConversionJavaValue::convert(JPMatch &match) { JPValue* value = match.getJavaSlot(); return *value; } JPConversionJavaValue _javaValueConversion; class JPConversionString : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionString::matches"); if (match.frame == nullptr || !JPPyString::check(match.object)) return match.type = JPMatch::_none; match.conversion = this; if (cls == JPContext_global->_java_lang_String) return match.type = JPMatch::_exact; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.implicit, (PyObject*) & PyUnicode_Type); } jvalue convert(JPMatch &match) override { jvalue res; string str = JPPyString::asStringUTF8(match.object); res.l = match.frame->fromStringUTF8(str); return res; } } _stringConversion; class JPConversionBox : public JPConversion { public: jvalue convert(JPMatch &match) override { jvalue res; JPPyObjectVector args(match.object, nullptr); auto *cls = (JPClass*) match.closure; JPValue pobj = cls->newInstance(*match.frame, args); res.l = pobj.getJavaObject(); return res; } } ; class JPConversionBoxBoolean : public JPConversionBox { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionBoxBoolean::matches"); if (!PyBool_Check(match.object)) return match.type = JPMatch::_none; match.conversion = this; match.closure = JPContext_global->_java_lang_Boolean; return match.type = JPMatch::_implicit; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.implicit, (PyObject*) & PyBool_Type); } } _boxBooleanConversion; class JPConversionBoxLong : public JPConversionBox { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionBoxLong::matches"); if (match.frame == nullptr) return match.type = JPMatch::_none; if (PyLong_CheckExact(match.object) || PyIndex_Check(match.object)) { match.conversion = this; return match.type = JPMatch::_implicit; } return match.type = JPMatch::_none; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsIndex")); PyList_Append(info.implicit, proto.get()); } jvalue convert(JPMatch &match) override { PyTypeObject* type = Py_TYPE(match.object); const char *name = type->tp_name; match.closure = JPContext_global->_java_lang_Long; if (strncmp(name, "numpy", 5) == 0) { // We only handle specific sized types, all others go to long. if (strcmp(&name[5], ".int8") == 0) match.closure = JPContext_global->_java_lang_Byte; else if (strcmp(&name[5], ".int16") == 0) match.closure = JPContext_global->_java_lang_Short; else if (strcmp(&name[5], ".int32") == 0) match.closure = JPContext_global->_java_lang_Integer; } return JPConversionBox::convert(match); } } _boxLongConversion; class JPConversionBoxDouble : public JPConversionBox { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionBoxDouble::matches"); if (match.frame == nullptr) return match.type = JPMatch::_none; if (PyNumber_Check(match.object)) { match.conversion = this; return match.type = JPMatch::_implicit; } return match.type = JPMatch::_none; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "SupportsFloat")); PyList_Append(info.implicit, proto.get()); } jvalue convert(JPMatch &match) override { PyTypeObject* type = Py_TYPE(match.object); const char *name = type->tp_name; match.closure = JPContext_global->_java_lang_Double; if (strncmp(name, "numpy", 5) == 0) { // We only handle specific sized types, all others go to double. if (strcmp(&name[5], ".float32") == 0) match.closure = JPContext_global->_java_lang_Float; } return JPConversionBox::convert(match); } } _boxDoubleConversion; class JPConversionJavaObjectAny : public JPConversionBox { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionJavaObjectAny::matches"); JPValue *value = match.getJavaSlot(); if (value == nullptr || match.frame == nullptr || value->getClass() == nullptr) return match.type = JPMatch::_none; match.conversion = this; if (value->getClass()->isPrimitive()) match.type = JPMatch::_implicit; else if (value->getClass() == cls) match.type = JPMatch::_exact; else match.type = JPMatch::_derived; return match.type; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPJavaFrame frame = JPJavaFrame::outer(); PyList_Append(info.implicit, PyJPClass_create(frame, JPContext_global->_java_lang_Object).get()); } jvalue convert(JPMatch &match) override { jvalue res; JPJavaFrame *frame = match.frame; JPValue *value = match.getJavaSlot(); if (!value->getClass()->isPrimitive()) { res.l = frame->NewLocalRef(value->getJavaObject()); return res; } else { // Okay we need to box it. auto* type = dynamic_cast (value->getClass()); match.closure = type->getBoxedClass(*frame); res = JPConversionBox::convert(match); return res; } } } _javaObjectAnyConversion; class JPConversionJavaNumberAny : public JPConversionJavaObjectAny { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionJavaNumberAny::matches"); JPContext *context = JPContext_global; JPValue *value = match.getJavaSlot(); // This converter only works for number types, thus boolean and char // are excluded. if (value == nullptr || match.frame == nullptr || value->getClass() == nullptr || value->getClass() == context->_boolean || value->getClass() == context->_char) return match.type = JPMatch::_none; match.conversion = this; JPClass *oc = value->getClass(); // If it is the exact type, then it is exact if (oc == cls) return match.type = JPMatch::_exact; // If it is any primitive except char and boolean then implicit if (oc->isPrimitive()) return match.type = JPMatch::_implicit; // Otherwise, check if it is assignable according to Java bool assignable = match.frame->IsAssignableFrom(oc->getJavaClass(), cls->getJavaClass()) != 0; return match.type = (assignable ? JPMatch::_implicit : JPMatch::_none); JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.implicit, (PyObject*) PyJPNumberLong_Type); PyList_Append(info.implicit, (PyObject*) PyJPNumberFloat_Type); } } _javaNumberAnyConversion; class JPConversionUnbox : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPContext *context = JPContext_global; if (context == nullptr) return match.type = JPMatch::_none; JPValue *slot = match.slot; auto *pcls = dynamic_cast( cls); if (slot->getClass() != pcls->getBoxedClass(*match.frame)) return match.type = JPMatch::_none; match.conversion = this; match.closure = cls; return match.type = JPMatch::_implicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPJavaFrame frame = JPJavaFrame::outer(); auto *pcls = dynamic_cast( cls); PyList_Append(info.implicit, PyJPClass_create(frame, pcls->getBoxedClass(frame)).get()); } jvalue convert(JPMatch &match) override { JPValue* value = match.getJavaSlot(); auto *cls = (JPClass*) match.closure; return cls->getValueFromObject(*match.frame, *value); } } _unboxConversion; class JPConversionProxy : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JP_TRACE_IN("JPConversionProxy::matches"); JPProxy* proxy = PyJPProxy_getJPProxy(match.object); if (proxy == nullptr || match.frame == nullptr) return match.type = JPMatch::_none; // Check if any of the interfaces matches ... vector itf = proxy->getInterfaces(); for (auto & i : itf) { if (match.frame->IsAssignableFrom(i->getJavaClass(), cls->getJavaClass())) { JP_TRACE("implicit proxy"); match.conversion = this; return match.type = JPMatch::_implicit; } } return match.type = JPMatch::_none; JP_TRACE_OUT; } void getInfo(JPClass *cls, JPConversionInfo &info) override { } jvalue convert(JPMatch &match) override { return PyJPProxy_getJPProxy(match.object)->getProxy(); } } _proxyConversion; JPConversion *hintsConversion = &_hintsConversion; JPConversion *charArrayConversion = &_charArrayConversion; JPConversion *byteArrayConversion = &_byteArrayConversion; JPConversion *bufferConversion = &_bufferConversion; JPConversion *sequenceConversion = &_sequenceConversion; JPConversion *nullConversion = &_nullConversion; JPConversion *classConversion = &_classConversion; JPConversion *objectConversion = &_objectConversion; JPConversion *javaObjectAnyConversion = &_javaObjectAnyConversion; JPConversion *javaNumberAnyConversion = &_javaNumberAnyConversion; JPConversion *javaValueConversion = &_javaValueConversion; JPConversion *stringConversion = &_stringConversion; JPConversion *boxBooleanConversion = &_boxBooleanConversion; JPConversion *boxLongConversion = &_boxLongConversion; JPConversion *boxDoubleConversion = &_boxDoubleConversion; JPConversion *unboxConversion = &_unboxConversion; JPConversion *proxyConversion = &_proxyConversion; jpype-1.6.0/native/common/jp_classloader.cpp000066400000000000000000000054071501674766700211450ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include #include jobject JPClassLoader::getBootLoader() { return m_BootLoader.get(); } JPClassLoader::JPClassLoader(JPJavaFrame& frame) { JP_TRACE_IN("JPClassLoader::JPClassLoader"); // Define the class loader m_ClassClass = JPClassRef(frame, frame.FindClass("java/lang/Class")); m_ForNameID = frame.GetStaticMethodID(m_ClassClass.get(), "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); jclass classLoaderClass = frame.FindClass("java/lang/ClassLoader"); jmethodID getSystemClassLoader = frame.GetStaticMethodID(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); m_SystemClassLoader = JPObjectRef(frame, frame.CallStaticObjectMethodA(classLoaderClass, getSystemClassLoader, nullptr)); jclass dynamicLoaderClass = frame.getEnv()->FindClass("org/jpype/JPypeClassLoader"); if (dynamicLoaderClass != nullptr) { // Use the one in place already if (frame.IsInstanceOf(m_SystemClassLoader.get(), dynamicLoaderClass)) { m_BootLoader = m_SystemClassLoader; return; } // Easy the Dynamic loader is already in the path, so just use it as the bootloader jmethodID newDyLoader = frame.GetMethodID(dynamicLoaderClass, "", "(Ljava/lang/ClassLoader;)V"); jvalue v; v.l = m_SystemClassLoader.get(); m_BootLoader = JPObjectRef(frame, frame.NewObjectA(dynamicLoaderClass, newDyLoader, &v)); return; } frame.ExceptionClear(); // org.jpype was not loaded already so we can't proceed JP_RAISE(PyExc_RuntimeError, "Can't find org.jpype.jar support library"); JP_TRACE_OUT; // GCOVR_EXCL_LINE } jclass JPClassLoader::findClass(JPJavaFrame& frame, const string& name) { #ifdef ANDROID string cname = name; for (int i = 0; i < cname.size(); ++i) if (cname[i] == '.') cname[i] = '/'; return frame.FindClass(cname); #else jvalue v[3]; v[0].l = frame.NewStringUTF(name.c_str()); v[1].z = true; v[2].l = m_BootLoader.get(); return (jclass) frame.CallStaticObjectMethodA(m_ClassClass.get(), m_ForNameID, v); #endif } jpype-1.6.0/native/common/jp_classtype.cpp000066400000000000000000000033671501674766700206630ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include // Class has special rules JPClassType::JPClassType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers) : JPClass(frame, clss, name, super, interfaces, modifiers) { } JPClassType::~JPClassType() = default; JPMatch::Type JPClassType::findJavaConversion(JPMatch& match) { JP_TRACE_IN("JPClass::findJavaConversion"); if (nullConversion->matches(this, match) || objectConversion->matches(this, match) || classConversion->matches(this, match) || hintsConversion->matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPClassType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); nullConversion->getInfo(this, info); objectConversion->getInfo(this, info); classConversion->getInfo(this, info); hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); } jpype-1.6.0/native/common/jp_context.cpp000066400000000000000000000407621501674766700203400ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_typemanager.h" #include "jp_stringtype.h" #include "jp_classloader.h" #include "jp_proxy.h" #include "jp_platform.h" #include "jp_gc.h" #ifdef WIN32 #include #else #if defined(_HPUX) && !defined(_IA64) #include #else #include #endif // HPUX #include #endif JPResource::~JPResource() = default; #define USE_JNI_VERSION JNI_VERSION_1_4 void JPRef_failed() { JP_RAISE(PyExc_SystemError, "NULL context in JPRef()"); } JPContext::JPContext() { m_Embedded = false; m_GC = new JPGarbageCollection(); } JPContext::~JPContext() { delete m_TypeManager; delete m_GC; } bool JPContext::isRunning() { if (m_JavaVM == nullptr || !m_Running) { return false; } return true; } /** throw a JPypeException if the JVM is not started */ void assertJVMRunning(JPContext* context, const JPStackInfo& info) { if (_JVMNotRunning == nullptr) { _JVMNotRunning = PyObject_GetAttrString(PyJPModule, "JVMNotRunning"); JP_PY_CHECK(); Py_INCREF(_JVMNotRunning); } if (context == nullptr) { throw JPypeException(JPError::_python_exc, _JVMNotRunning, "Java Context is null", info); } if (!context->isRunning()) { throw JPypeException(JPError::_python_exc, _JVMNotRunning, "Java Virtual Machine is not running", info); } } void JPContext::loadEntryPoints(const string& path) { JP_TRACE_IN("JPContext::loadEntryPoints"); JPPlatformAdapter *platform = JPPlatformAdapter::getAdapter(); // Load symbols from the shared library platform->loadLibrary((char*) path.c_str()); CreateJVM_Method = (jint(JNICALL *)(JavaVM **, void **, void *) )platform->getSymbol("JNI_CreateJavaVM"); GetCreatedJVMs_Method = (jint(JNICALL *)(JavaVM **, jsize, jsize*))platform->getSymbol("JNI_GetCreatedJavaVMs"); JP_TRACE_OUT; } void JPContext::startJVM(const string& vmPath, const StringVector& args, bool ignoreUnrecognized, bool convertStrings, bool interrupt) { JP_TRACE_IN("JPContext::startJVM"); JP_TRACE("Convert strings", convertStrings); m_ConvertStrings = convertStrings; // Get the entry points in the shared library try { JP_TRACE("Load entry points"); loadEntryPoints(vmPath); } catch (JPypeException& ex) { (void) ex; throw; } // Determine the memory requirements #define PAD(x) ((x+31)&~31) size_t mem = PAD(sizeof(JavaVMInitArgs)); size_t oblock = mem; mem += PAD(sizeof(JavaVMOption)*args.size() + 1); size_t sblock = mem; for (size_t i = 0; i < args.size(); i++) { mem += PAD(args[i].size()+1); } // Pack the arguments JP_TRACE("Pack arguments"); char *block = (char*) malloc(mem); JavaVMInitArgs* jniArgs = (JavaVMInitArgs*) block; memset(jniArgs, 0, mem); jniArgs->options = (JavaVMOption*)(&block[oblock]); // prepare this ... jniArgs->version = USE_JNI_VERSION; jniArgs->ignoreUnrecognized = ignoreUnrecognized; JP_TRACE("IgnoreUnrecognized", ignoreUnrecognized); jniArgs->nOptions = (jint) args.size(); JP_TRACE("NumOptions", jniArgs->nOptions); size_t j = sblock; for (size_t i = 0; i < args.size(); i++) { JP_TRACE("Option", args[i]); strncpy(&block[j], args[i].c_str(), args[i].size()); jniArgs->options[i].optionString = (char*) &block[j]; j += PAD(args[i].size()+1); } // Launch the JVM JNIEnv* env = nullptr; JP_TRACE("Create JVM"); try { CreateJVM_Method(&m_JavaVM, (void**) &env, (void*) jniArgs); } catch (...) { JP_TRACE("Exception in CreateJVM?"); } JP_TRACE("JVM created"); free(jniArgs); if (m_JavaVM == nullptr) { JP_TRACE("Unable to start"); JP_RAISE(PyExc_RuntimeError, "Unable to start JVM"); } // Mark running for assert m_Running = true; initializeResources(env, interrupt); JP_TRACE_OUT; } void JPContext::attachJVM(JNIEnv* env) { env->GetJavaVM(&m_JavaVM); #ifndef ANDROID m_Embedded = true; #endif initializeResources(env, false); } std::string getShared() { #ifdef WIN32 // Windows specific char path[MAX_PATH]; HMODULE hm = NULL; if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) &getShared, &hm) != 0 && GetModuleFileName(hm, path, sizeof(path)) != 0) { // This is needed when there is no-ascii characters in path char shortPathBuffer[MAX_PATH]; long len = GetShortPathName(path, shortPathBuffer, MAX_PATH); if (len != 0) return std::string(shortPathBuffer); } #else // Linux specific Dl_info info; if (dladdr((void*)getShared, &info)) return info.dli_fname; #endif // Generic JPPyObject import = JPPyObject::use(PyImport_AddModule("importlib.util")); JPPyObject jpype = JPPyObject::call(PyObject_CallMethod(import.get(), "find_spec", "s", "_jpype")); JPPyObject origin = JPPyObject::call(PyObject_GetAttrString(jpype.get(), "origin")); return JPPyString::asStringUTF8(origin.get()); } void JPContext::initializeResources(JNIEnv* env, bool interrupt) { JPJavaFrame frame = JPJavaFrame::external(env); // This is the only frame that we can use until the system // is initialized. Any other frame creation will result in an error. jclass throwableClass = (jclass) frame.FindClass("java/lang/Throwable"); m_Throwable_GetCauseID = frame.GetMethodID(throwableClass, "getCause", "()Ljava/lang/Throwable;"); m_Throwable_GetMessageID = frame.GetMethodID(throwableClass, "getMessage", "()Ljava/lang/String;"); // After the JVM is created but before the context is started, we need // to set up all the services that the context will need. JP_TRACE("Initialize"); // We need these first because if anything goes south this is the first // thing that will get hit. jclass objectClass = frame.FindClass("java/lang/Object"); m_Object_ToStringID = frame.GetMethodID(objectClass, "toString", "()Ljava/lang/String;"); m_Object_EqualsID = frame.GetMethodID(objectClass, "equals", "(Ljava/lang/Object;)Z"); m_Object_HashCodeID = frame.GetMethodID(objectClass, "hashCode", "()I"); m_Object_GetClassID = frame.GetMethodID(objectClass, "getClass", "()Ljava/lang/Class;"); m_RuntimeException = JPClassRef(frame, (jclass) frame.FindClass("java/lang/RuntimeException")); jclass stringClass = frame.FindClass("java/lang/String"); m_String_ToCharArrayID = frame.GetMethodID(stringClass, "toCharArray", "()[C"); jclass classClass = frame.FindClass("java/lang/Class"); m_Class_GetNameID = frame.GetMethodID(classClass, "getName", "()Ljava/lang/String;"); // Bootloader needs to go first so we can load classes m_ClassLoader = new JPClassLoader(frame); JP_TRACE("Install native"); // Start the rest of the services m_TypeManager = new JPTypeManager(frame); // Prepare to launch JP_TRACE("Start Context"); m_ContextClass = JPClassRef(frame, (jclass) m_ClassLoader->findClass(frame, "org.jpype.JPypeContext")); jclass contextClass = m_ContextClass.get(); m_Context_GetStackFrameID = frame.GetMethodID(contextClass, "getStackTrace", "(Ljava/lang/Throwable;Ljava/lang/Throwable;)[Ljava/lang/Object;"); jmethodID startMethod = frame.GetStaticMethodID(contextClass, "createContext", "(JLjava/lang/ClassLoader;Ljava/lang/String;Z)Lorg/jpype/JPypeContext;"); // Launch jvalue val[4]; val[0].j = (jlong) this; val[1].l = m_ClassLoader->getBootLoader(); val[2].l = nullptr; val[3].z = interrupt; if (!m_Embedded) { std::string shared = getShared(); val[2].l = frame.fromStringUTF8(shared); } // Required before launch m_Context_GetFunctionalID = frame.GetStaticMethodID(contextClass, "getFunctional", "(Ljava/lang/Class;)Ljava/lang/String;"); m_JavaContext = JPObjectRef(frame, frame.CallStaticObjectMethodA(contextClass, startMethod, val)); // Post launch JP_TRACE("Connect resources"); // Hook up the type manager jmethodID getTypeManager = frame.GetMethodID(contextClass, "getTypeManager", "()Lorg/jpype/manager/TypeManager;"); m_TypeManager->m_JavaTypeManager = JPObjectRef(frame, frame.CallObjectMethodA(m_JavaContext.get(), getTypeManager, nullptr)); // Set up methods after everything is start so we get better error // messages jclass reflectorClass = frame.FindClass("org/jpype/JPypeReflector"); jfieldID reflectorField = frame.GetFieldID(contextClass, "reflector", "Lorg/jpype/JPypeReflector;"); m_Reflector = JPObjectRef(frame, frame.GetObjectField(m_JavaContext.get(), reflectorField)); m_CallMethodID = frame.GetMethodID(reflectorClass, "callMethod", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); m_Context_collectRectangularID = frame.GetMethodID(contextClass, "collectRectangular", "(Ljava/lang/Object;)[Ljava/lang/Object;"); m_Context_assembleID = frame.GetMethodID(contextClass, "assemble", "([ILjava/lang/Object;)Ljava/lang/Object;"); m_Context_CreateExceptionID = frame.GetMethodID(contextClass, "createException", "(JJ)Ljava/lang/Exception;"); m_Context_GetExcClassID = frame.GetMethodID(contextClass, "getExcClass", "(Ljava/lang/Throwable;)J"); m_Context_GetExcValueID = frame.GetMethodID(contextClass, "getExcValue", "(Ljava/lang/Throwable;)J"); m_Context_OrderID = frame.GetMethodID(contextClass, "order", "(Ljava/nio/Buffer;)Z"); m_Context_IsPackageID = frame.GetMethodID(contextClass, "isPackage", "(Ljava/lang/String;)Z"); m_Context_GetPackageID = frame.GetMethodID(contextClass, "getPackage", "(Ljava/lang/String;)Lorg/jpype/pkg/JPypePackage;"); m_Context_ClearInterruptID = frame.GetStaticMethodID(contextClass, "clearInterrupt", "(Z)V"); jclass packageClass = m_ClassLoader->findClass(frame, "org.jpype.pkg.JPypePackage"); m_Package_GetObjectID = frame.GetMethodID(packageClass, "getObject", "(Ljava/lang/String;)Ljava/lang/Object;"); m_Package_GetContentsID = frame.GetMethodID(packageClass, "getContents", "()[Ljava/lang/String;"); m_Context_NewWrapperID = frame.GetMethodID(contextClass, "newWrapper", "(J)V"); m_Array = JPClassRef(frame, frame.FindClass("java/lang/reflect/Array")); m_Array_NewInstanceID = frame.GetStaticMethodID(m_Array.get(), "newInstance", "(Ljava/lang/Class;[I)Ljava/lang/Object;"); jclass bufferClass = frame.FindClass("java/nio/Buffer"); m_Buffer_IsReadOnlyID = frame.GetMethodID(bufferClass, "isReadOnly", "()Z"); jclass bytebufferClass = frame.FindClass("java/nio/ByteBuffer"); m_Buffer_AsReadOnlyID = frame.GetMethodID(bytebufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;"); jclass comparableClass = frame.FindClass("java/lang/Comparable"); m_CompareToID = frame.GetMethodID(comparableClass, "compareTo", "(Ljava/lang/Object;)I"); jclass proxyClass = getClassLoader()->findClass(frame, "org.jpype.proxy.JPypeProxy"); m_ProxyClass = JPClassRef(frame, proxyClass); m_Proxy_NewID = frame.GetStaticMethodID(m_ProxyClass.get(), "newProxy", "(Lorg/jpype/JPypeContext;JJ[Ljava/lang/Class;)Lorg/jpype/proxy/JPypeProxy;"); m_Proxy_NewInstanceID = frame.GetMethodID(m_ProxyClass.get(), "newInstance", "()Ljava/lang/Object;"); m_GC->init(frame); _java_nio_ByteBuffer = this->getTypeManager()->findClassByName("java.nio.ByteBuffer"); // Testing code to make sure C++ exceptions are handled. // FIXME find a way to call this from instrumentation. // throw std::runtime_error("Failed"); // Everything is started. } void JPContext::onShutdown() { m_Running = false; } void JPContext::shutdownJVM(bool destroyJVM, bool freeJVM) { JP_TRACE_IN("JPContext::shutdown"); if (m_JavaVM == nullptr) JP_RAISE(PyExc_RuntimeError, "Attempt to shutdown without a live JVM"); // if (m_Embedded) // JP_RAISE(PyExc_RuntimeError, "Cannot shutdown from embedded Python"); // Wait for all non-demon threads to terminate if (destroyJVM) { JP_TRACE("Destroy JVM"); JPPyCallRelease call; m_JavaVM->DestroyJavaVM(); } // unload the jvm library if (freeJVM) { JP_TRACE("Unload JVM"); m_JavaVM = nullptr; JPPlatformAdapter::getAdapter()->unloadLibrary(); } JP_TRACE("Delete resources"); for (auto & m_Resource : m_Resources) { delete m_Resource; } m_Resources.clear(); JP_TRACE_OUT; } void JPContext::ReleaseGlobalRef(jobject obj) { JP_TRACE_IN("JPContext::ReleaseGlobalRef", obj); // Check if the JVM is already shutdown if (m_JavaVM == nullptr) return; // Get the environment and release the resource if we can. // Do not attach the thread if called from an unattached thread it is // likely a shutdown anyway. JNIEnv* env; jint res = m_JavaVM->functions->GetEnv(m_JavaVM, (void**) &env, USE_JNI_VERSION); if (res != JNI_EDETACHED) env->functions->DeleteGlobalRef(env, obj); JP_TRACE_OUT; } /*****************************************************************************/ // Thread code void JPContext::attachCurrentThread() { JNIEnv* env; jint res = m_JavaVM->functions->AttachCurrentThread(m_JavaVM, (void**) &env, nullptr); if (res != JNI_OK) JP_RAISE(PyExc_RuntimeError, "Unable to attach to thread"); } void JPContext::attachCurrentThreadAsDaemon() { JNIEnv* env; jint res = m_JavaVM->functions->AttachCurrentThreadAsDaemon(m_JavaVM, (void**) &env, nullptr); if (res != JNI_OK) JP_RAISE(PyExc_RuntimeError, "Unable to attach to thread as daemon"); } bool JPContext::isThreadAttached() { JNIEnv* env; return JNI_OK == m_JavaVM->functions->GetEnv(m_JavaVM, (void**) &env, USE_JNI_VERSION); } void JPContext::detachCurrentThread() { m_JavaVM->functions->DetachCurrentThread(m_JavaVM); } JNIEnv* JPContext::getEnv() { JNIEnv* env = nullptr; if (m_JavaVM == nullptr) { JP_RAISE(PyExc_RuntimeError, "JVM is null"); } // Get the environment jint res = m_JavaVM->functions->GetEnv(m_JavaVM, (void**) &env, USE_JNI_VERSION); // If we don't have an environment then we are in a thread, so we must attach if (res == JNI_EDETACHED) { // We will attach as daemon so that the newly attached thread does // not deadlock the shutdown. The user can convert later if they want. res = m_JavaVM->AttachCurrentThreadAsDaemon((void**) &env, nullptr); if (res != JNI_OK) { JP_RAISE(PyExc_RuntimeError, "Unable to attach to local thread"); } } return env; } extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeContext_onShutdown (JNIEnv *env, jobject obj, jlong contextPtr) { ((JPContext*) contextPtr)->onShutdown(); } /********************************************************************** * Interrupts are complex. Both Java and Python want to handle the * interrupt, but only one can be in control. Java starts later and * installs its handler over Python as a chain. If Java handles it then * the JVM will terminate which leaves Python with a bunch of bad * references which tends to lead to segfaults. So we need to disable * the Java one by routing it back to Python. But if we do so then * Java wont respect Ctrl+C. So we need to handle the interrupt, convert * it to a wait interrupt so that Java can break at the next I/O and * then trip Python signal handler so the Python gets the interrupt. * * But this leads to a few race conditions. * * If the control is in Java then it will get the interrupt next time * it hits Python code when the returned object is checked resulting * InterruptedException. Now we have two exceptions on the stack, * the one from Java and the one from Python. We check to see if * Python has a pending interrupt and eat the Java one. * * If the control is in Java and it hits an I/O call. This generates * InterruptedException which again transfers control to Python where * the Exception is resolved. * * If the control is in Python when the interrupt occurs, then * we have a bogus Java interrupt sitting on the main thread that the next * Java call will trip over. So we need to call clearInterrupt(false). * This checks clears the interrupt in C++ and in Java. * */ static int interruptState = 0; extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_interruptPy (JNIEnv *env, jclass cls, jint signal) { interruptState = 1; #if PY_MINOR_VERSION<10 PyErr_SetInterrupt(); #else PyErr_SetInterruptEx((int) signal); #endif } extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_acknowledgePy (JNIEnv *env, jclass cls) { interruptState = 0; } int hasInterrupt() { return interruptState != 0; } jpype-1.6.0/native/common/jp_convert.cpp000066400000000000000000000357231501674766700203350ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include #include namespace { template class Half { public: static jvalue convert(void* c) { uint16_t i = *(uint16_t*) c; uint32_t sign = (i&0x8000)>>15; uint32_t exp = (i&0x7C00)>>10; uint32_t frac = (i&0x03ff); uint32_t k = sign<<31; uint32_t count = (i&0x03ff); if (exp == 0) { // subnormal numbers if (frac != 0) { count = count | (count >> 1); count = count | (count >> 2); count = count | (count >> 4); count = count | (count >> 8); int zeros = std::bitset<32>(~count).count(); exp = 127-zeros+7; exp <<= 23; frac <<= zeros-8; frac &= 0x7fffff; k |= exp | frac; } } else if (exp < 31) { // normal numbers exp = exp-15+127; exp <<= 23; frac <<= 13; k |= exp | frac; } else { // to infinity and beyond! if (frac == 0) k |= 0x7f800000; else k |= 0x7f800001 | ((frac&0x200)<<12); } return func(&k); } }; template class Convert { public: static jvalue toZ(void* c) { jvalue v; v.z = (*(T*) c) != 0; return v; } static jvalue toB(void* c) { jvalue v; v.b = (jbyte) (*(T*) c); return v; } static jvalue toC(void* c) { jvalue v; v.c = (jchar) (*(T*) c); return v; } static jvalue toS(void* c) { jvalue v; v.s = (jshort) (*(T*) c); return v; } static jvalue toI(void* c) { jvalue v; v.i = (jint) (*(T*) c); return v; } static jvalue toJ(void* c) { jvalue v; v.j = (jlong) (*(T*) c); return v; } static jvalue toF(void* c) { jvalue v; v.f = (jfloat) (*(T*) c); return v; } static jvalue toD(void* c) { jvalue v; v.d = (jdouble) (*(T*) c); return v; } } ; template class Reverse { public: static jvalue call2(void* c) { char r[2]; char* c2 = (char*)c; r[0]=c2[1]; r[1]=c2[0]; return func(r); } static jvalue call4(void* c) { char r[4]; char* c2 = (char*)c; r[0]=c2[3]; r[1]=c2[2]; r[2]=c2[1]; r[3]=c2[0]; return func(r); } static jvalue call8(void* c) { char r[8]; char* c2 = (char*)c; r[0]=c2[7]; r[1]=c2[6]; r[2]=c2[5]; r[3]=c2[4]; r[4]=c2[3]; r[5]=c2[2]; r[6]=c2[1]; r[7]=c2[0]; return func(r); } } ; } // namespace jconverter getConverter(const char* from, int itemsize, const char* to) { // If not specified then the type is bytes if (from == nullptr) from = "B"; // Skip specifiers bool reverse = false; unsigned int x = 1; bool little = *((char*)&x)==1; switch (from[0]) { case '!': case '>': if (little) reverse = true; from++; break; case '<': if (!little) reverse = true; from++; break; case '@': case '=': from++; default: break; } // Standard size for 'l' is 4 in docs, but numpy uses format 'l' for long long if (itemsize == 8 && from[0] == 'l') from = "q"; if (itemsize == 8 && from[0] == 'L') from = "Q"; switch (from[0]) { case '?': case 'c': case 'b': switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'B': switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'h': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call2; case 'b': return &Reverse::toB>::call2; case 'c': return &Reverse::toC>::call2; case 's': return &Reverse::toS>::call2; case 'i': return &Reverse::toI>::call2; case 'j': return &Reverse::toJ>::call2; case 'f': return &Reverse::toF>::call2; case 'd': return &Reverse::toD>::call2; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'H': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call2; case 'b': return &Reverse::toB>::call2; case 'c': return &Reverse::toC>::call2; case 's': return &Reverse::toS>::call2; case 'i': return &Reverse::toI>::call2; case 'j': return &Reverse::toJ>::call2; case 'f': return &Reverse::toF>::call2; case 'd': return &Reverse::toD>::call2; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'i': case 'l': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call4; case 'b': return &Reverse::toB>::call4; case 'c': return &Reverse::toC>::call4; case 's': return &Reverse::toS>::call4; case 'i': return &Reverse::toI>::call4; case 'j': return &Reverse::toJ>::call4; case 'f': return &Reverse::toF>::call4; case 'd': return &Reverse::toD>::call4; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'I': case 'L': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call4; case 'b': return &Reverse::toB>::call4; case 'c': return &Reverse::toC>::call4; case 's': return &Reverse::toS>::call4; case 'i': return &Reverse::toI>::call4; case 'j': return &Reverse::toJ>::call4; case 'f': return &Reverse::toF>::call4; case 'd': return &Reverse::toD>::call4; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'q': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call8; case 'b': return &Reverse::toB>::call8; case 'c': return &Reverse::toC>::call8; case 's': return &Reverse::toS>::call8; case 'i': return &Reverse::toI>::call8; case 'j': return &Reverse::toJ>::call8; case 'f': return &Reverse::toF>::call8; case 'd': return &Reverse::toD>::call8; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'Q': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call8; case 'b': return &Reverse::toB>::call8; case 'c': return &Reverse::toC>::call8; case 's': return &Reverse::toS>::call8; case 'i': return &Reverse::toI>::call8; case 'j': return &Reverse::toJ>::call8; case 'f': return &Reverse::toF>::call8; case 'd': return &Reverse::toD>::call8; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'f': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call4; case 'b': return &Reverse::toB>::call4; case 'c': return &Reverse::toC>::call4; case 's': return &Reverse::toS>::call4; case 'i': return &Reverse::toI>::call4; case 'j': return &Reverse::toJ>::call4; case 'f': return &Reverse::toF>::call4; case 'd': return &Reverse::toD>::call4; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'd': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call8; case 'b': return &Reverse::toB>::call8; case 'c': return &Reverse::toC>::call8; case 's': return &Reverse::toS>::call8; case 'i': return &Reverse::toI>::call8; case 'j': return &Reverse::toJ>::call8; case 'f': return &Reverse::toF>::call8; case 'd': return &Reverse::toD>::call8; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'e': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::convert>::call4; case 'b': return &Reverse::toB>::convert>::call4; case 'c': return &Reverse::toC>::convert>::call4; case 's': return &Reverse::toS>::convert>::call4; case 'i': return &Reverse::toI>::convert>::call4; case 'j': return &Reverse::toJ>::convert>::call4; case 'f': return &Reverse::toF>::convert>::call4; case 'd': return &Reverse::toD>::convert>::call4; } else switch (to[0]) { case 'z': return &Half::toZ>::convert; case 'b': return &Half::toB>::convert; case 'c': return &Half::toC>::convert; case 's': return &Half::toS>::convert; case 'i': return &Half::toI>::convert; case 'j': return &Half::toJ>::convert; case 'f': return &Half::toF>::convert; case 'd': return &Half::toD>::convert; } break; case 'n': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call8; case 'b': return &Reverse::toB>::call8; case 'c': return &Reverse::toC>::call8; case 's': return &Reverse::toS>::call8; case 'i': return &Reverse::toI>::call8; case 'j': return &Reverse::toJ>::call8; case 'f': return &Reverse::toF>::call8; case 'd': return &Reverse::toD>::call8; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; case 'N': if (reverse) switch (to[0]) { case 'z': return &Reverse::toZ>::call8; case 'b': return &Reverse::toB>::call8; case 'c': return &Reverse::toC>::call8; case 's': return &Reverse::toS>::call8; case 'i': return &Reverse::toI>::call8; case 'j': return &Reverse::toJ>::call8; case 'f': return &Reverse::toF>::call8; case 'd': return &Reverse::toD>::call8; } else switch (to[0]) { case 'z': return &Convert::toZ; case 'b': return &Convert::toB; case 'c': return &Convert::toC; case 's': return &Convert::toS; case 'i': return &Convert::toI; case 'j': return &Convert::toJ; case 'f': return &Convert::toF; case 'd': return &Convert::toD; } break; default: break; } PyErr_Format(PyExc_ValueError, "Unable to handle buffer type '%s'", from); JP_RAISE_PYTHON(); } jpype-1.6.0/native/common/jp_doubletype.cpp000066400000000000000000000232411501674766700210210ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_doubletype.h" JPDoubleType::JPDoubleType() : JPPrimitiveType("double") { } JPClass* JPDoubleType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Double; } JPPyObject JPDoubleType::convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) { PyTypeObject * wrapper = getHost(); JPPyObject obj = JPPyObject::call(wrapper->tp_alloc(wrapper, 0)); ((PyFloatObject*) obj.get())->ob_fval = value.d; PyJPValue_assignJavaSlot(frame, obj.get(), JPValue(this, value)); return obj; } JPValue JPDoubleType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; jobject jo = obj.getValue().l; auto* jb = dynamic_cast( frame.findClassForObject(jo)); field(v) = (type_t) frame.CallDoubleMethodA(jo, jb->m_DoubleValueID, nullptr); return JPValue(this, v); } static JPConversionAsFloat asDoubleConversion; static JPConversionLongAsFloat asDoubleLongConversion; static JPConversionFloatWiden doubleWidenConversion; class JPConversionAsDoubleExact : public JPConversionAsFloat { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyFloat_CheckExact(match.object)) return match.type = JPMatch::_none; match.conversion = this; return match.type = JPMatch::_exact; } } asDoubleExactConversion; class JPConversionAsJDouble : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == nullptr) return match.type = JPMatch::_none; match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Consider widening JPClass *cls2 = value->getClass(); if (cls2->isPrimitive()) { // https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2 auto *prim = dynamic_cast( cls2); switch (prim->getTypeCode()) { case 'B': case 'S': case 'C': case 'I': case 'J': case 'F': match.conversion = &doubleWidenConversion; return match.type = JPMatch::_implicit; default: break; } } // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_double->getHost()); PyList_Append(info.implicit, (PyObject*) context->_byte->getHost()); PyList_Append(info.implicit, (PyObject*) context->_char->getHost()); PyList_Append(info.implicit, (PyObject*) context->_short->getHost()); PyList_Append(info.implicit, (PyObject*) context->_int->getHost()); PyList_Append(info.implicit, (PyObject*) context->_long->getHost()); PyList_Append(info.implicit, (PyObject*) context->_float->getHost()); unboxConversion->getInfo(cls, info); } } asJDoubleConversion; JPMatch::Type JPDoubleType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPDoubleType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (asJDoubleConversion.matches(this, match) || asDoubleExactConversion.matches(this, match) || asDoubleLongConversion.matches(this, match) || asDoubleConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPDoubleType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); asJDoubleConversion.getInfo(this, info); asDoubleExactConversion.getInfo(this, info); asDoubleLongConversion.getInfo(this, info); asDoubleConversion.getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); } jarray JPDoubleType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewDoubleArray(sz); } JPPyObject JPDoubleType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticDoubleField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPDoubleType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetDoubleField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPDoubleType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticDoubleMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPDoubleType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallDoubleMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualDoubleMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPDoubleType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java double"); type_t val = field(match.convert()); frame.SetStaticDoubleField(c, fid, val); } void JPDoubleType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java double"); type_t val = field(match.convert()); frame.SetDoubleField(c, fid, val); } void JPDoubleType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPDoubleType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetDoubleArrayElements, &JPJavaFrame::ReleaseDoubleArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "d"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.d; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { type_t v = (type_t) PyFloat_AsDouble(seq[i].get()); if (v == -1) JP_PY_CHECK(); val[index] = v; } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPDoubleType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetDoubleArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPDoubleType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java double"); type_t val = field(match.convert()); frame.SetDoubleArrayRegion((array_t) a, ndx, 1, &val); } void JPDoubleType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetDoubleArrayElements( (jdoubleArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "d"; view.m_Buffer.itemsize = sizeof (jdouble); } void JPDoubleType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseDoubleArrayElements((jdoubleArray) view.m_Array->getJava(), (jdouble*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPDoubleType::getBufferFormat() { return "d"; } Py_ssize_t JPDoubleType::getItemSize() { return sizeof (jdouble); } void JPDoubleType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jdouble*) ((char*) memory + offset); frame.GetDoubleArrayRegion((jdoubleArray) a, start, len, b); } static void pack(jdouble* d, jvalue v) { *d = v.d; } PyObject *JPDoubleType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPDoubleType::newMultiArray"); return convertMultiArray( frame, this, &pack, "d", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_encoding.cpp000066400000000000000000000271401501674766700204350ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jp_encoding.h" // These encoders handle all the codes expected to be passed between // Java and Python assuming they both generate compliant codings. However, // this code does not handle miscodings very well. The current behavior // is to simply terminate the string at the bad code without producing // a warning. I can add errors, but I am not sure how I would test it // as both java and python will produce an error if I tried to force a // bad encoding on them. Thus I am going to leave this with the truncation // behavior for now. // // Examples of bad encodings: // - non-utf8 such as extended ascii passed from python // - encoded forbidden utf passed from python // - truncated surrogate codes passed from java // // The secondary problem is that string conversion is part of exception // handling. And the last thing we want to do is throw an exception // while trying to convert an exception string while reporting. That // would completely redirect the user. Thus truncation seems like // a sane policy unless we can verify all the potential edge cases. // // Alternatively we could make them a bit more permissive and // try to automatically correct bad encodings by recognizing // extend ASCII, etc. But this is potentially complex and // has the downside that we can't test it currently. JPEncoding::~JPEncoding() = default; // char* to stream from // https://stackoverflow.com/questions/7781898/get-an-istream-from-a-char struct membuf : std::streambuf { membuf(char* begin, char* end) { setg(begin, begin, end); } } ; // Convert a string from one encoding to another. // Currently we use this to transcribe from utf-8 to java-utf-8 and back. // It could do other encodings, but would need to be generalized to // a template to handle wider character sets. std::string transcribe(const char* in, size_t len, const JPEncoding& sourceEncoding, const JPEncoding& targetEncoding) { // ASCII bypass bool ascii = true; for (size_t i = 0; i < len; ++i) { if (in[i]&0x80 || in[i] == 0) { ascii = false; break; } } if (ascii) { return {in, len}; } // Convert input to istream source membuf sbuf(const_cast (in), const_cast (in + len)); std::istream inStream(&sbuf); // Create an output stream with reserve std::string out; out.reserve(len + 8); std::ostringstream outStream(out); while (!inStream.eof()) { unsigned int coding = sourceEncoding.fetch(inStream); if (coding == (unsigned int) - 1) { if (inStream.eof()) break; // Truncate bad strings for now. return outStream.str(); } targetEncoding.encode(outStream, coding); } return outStream.str(); } //************************************************************** // Encode a 21 bit code point as UTF-8 // // There are 4 encodings used // // Range Encoding // 0x000000-0x00007F // 0xxxxxxx // bits 6543210 // // 0x000080-0x0007FF // 110xxxxx 10xxxxxx // 1 // bits 09876 543210 // // 0x000800-0x000FFFF (excluding 0xD800-0xDFFF) // 1110xxxx 10xxxxxx 10xxxxxx // 1111 11 // bits 5432 109876 543210 // // 0x100000-0x10FFFF // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx // 211 111111 11 // bits 098 765432 109876 543210 // // The encoding process is simply to add the constant portion encoding // the byte position, then shifting to get the lowest bits, the masking // off the required bits. // void JPEncodingUTF8::encode(std::ostream& out, unsigned int c) const { if (c < 0x80) { // encode 1 out.put(char(c & 0xff)); } else if (c < 0x800) { // encode 2 out.put(char(0xc0 + ((c >> 6)&0x1f))); out.put(char(0x80 + ((c >> 0)&0x3f))); } else if (c < 0x10000) // Note 0xd800-0xdfff are not valid codes { // encode 3 out.put(char(0xe0 + ((c >> 12)&0x0f))); out.put(char(0x80 + ((c >> 6)&0x3f))); out.put(char(0x80 + ((c >> 0)&0x3f))); } else if (c < 0x110000) { // encode 4 out.put(char(0xf0 + ((c >> 18)&0x07))); out.put(char(0x80 + ((c >> 12)&0x3f))); out.put(char(0x80 + ((c >> 6)&0x3f))); out.put(char(0x80 + ((c >> 0)&0x3f))); } } // Retrieve a 21 unicode code point from a stream of bytes. // // This is the reverse of the process of encoding. // We must first locate the position encoding bits from the // top of the byte, then pull off the encoded bits. The // final code point is the sum of each set of encoded bits. // unsigned int JPEncodingUTF8::fetch(std::istream& in) const { unsigned int c0 = in.get(); if (in.eof()) return -1; // 1 byte code if ((c0 & 0x80) == 0) return c0; unsigned int c1 = in.get(); if (in.eof()) return -1; // 2 byte code if ((c0 & 0xe0) == 0xc0) { if ((c1 & 0xc0) == 0x80) return ((c0 & 0x1f) << 6) + (c1 & 0x3f); else return -1; // bad 2 byte format } unsigned int c2 = in.get(); if (in.eof()) return -1; // 3 byte code if ((c0 & 0xf0) == 0xe0) { if ((c1 & 0xc0) == 0x80 && (c2 & 0xc0) == 0x80) return ((c0 & 0xf) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f); return -1; } unsigned int c3 = in.get(); if (in.eof()) return -1; // 4 byte code if ((c0 & 0xf8) == 0xf0) { if ((c1 & 0xc0) == 0x80 && (c2 & 0xc0) == 0x80 && (c3 & 0xc0) == 0x80) return ((c0 & 0xf) << 18) + ((c1 & 0x3f) << 12) + ((c2 & 0x3f) << 6) + (c3 & 0x3f); return -1; } return -1; } // Encode a 21 code point into a Java UTF char stream. // // Java uses 16 bit characters to represent a string, but this is not // sufficient to hold all 21 bits of valid unicode chars. Thus to // encode the upper bits java uses UTF-16 encoding internally. Unfortunately, // when java converts the 16 bit string into 8 bit encoding, it does not // decode the string first. As a result we receive not UTF-8 but rather // doublely encoded UTF-16/UTF-8. Further UTF-16 encoding requires // not just simple byte packing but also an offset of the bits. Thus this // process becomes a real mess. Also a special code point is required for // embedded nulls. // // There are 4 encodings used // // Range Encoding // 0x000001-0x00007F // 0xxxxxxx // bits 6543210 // // 0x000080-0x0007FF, special code 0 // 110xxxxx 10xxxxxx // 1 // bits 09876 543210 // // 0x000800-0x000D7FF or 0x00DFFF-0x000FFFF // 1110xxxx 10xxxxxx 10xxxxxx // 1111 11 // bits 5432 109876 543210 // // 0x100000-0x10FFFF (called surgate codes in UTF-16) // (doublely encoded 6 byte code point) // first subtract 0x10000 which reduces the range by 2 bits // 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx // 1111 111111 // bits 9876 543210 9876 543210 // // The encoding process is simply to add the constant portion encoding // the byte position, then shifting to get the lowest bits, the masking // off the required bits. The exception being the surgate codes which // require an offset then a bit pack. // // 4 byte unicode or coding a 3 point in the invalid range followed by anything other // than 3 byte encoding (in the invalid range) is a coding error. // void JPEncodingJavaUTF8::encode(std::ostream& out, unsigned int c) const { if (c == 0) { // encode 0 as 2 out.put(char(0xc0)); out.put(char(0x80)); } else if (c < 0x80) { // encode 1 out.put(char(c & 0xff)); } else if (c < 0x800) { // encode 2 out.put(char(0xc0 + ((c >> 6)&0x1f))); out.put(char(0x80 + ((c >> 0)&0x3f))); } else if (c < 0xd800 || (c >= 0xe000 && c < 0x10000)) { // encode 3 out.put(char(0xe0 + char((c >> 12)&0x0f))); out.put(char(0x80 + char((c >> 6)&0x3f))); out.put(char(0x80 + char((c >> 0)&0x3f))); } else if (c < 0x110000) { c = c - 0x10000; // encode 6 out.put(char(0xed)); out.put(char(0xa0 + ((c >> 16)&0xf))); out.put(char(0x80 + ((c >> 10)&0x3f))); out.put(char(0xed)); out.put(char(0xb0 + ((c >> 6)&0xf))); out.put(char(0x80 + ((c >> 0)&0x3f))); } } // Decoding is the reverse of encoding, but there // is a special gotcha because the position encoding bits // are not unique. Both the 3 and 6 points codes share // the position encoding 0xED for the first byte. // // The unicde packing solves this by removing a portion of // the encoding such that 0xD800-0xDFFF are not valid unicode code // points. Thus these invalid code points are used to represent // the upper and lower encoding for the extended unicode // code points. // // Thus the procedure is decode the top 3 and check to see if // it is in a valid range. If it isn't then decode the // second set of 3. So long as the bytes are coded through the // java encoder it will be valid. However, a user could // manually encode a string with an invalid coding. // unsigned int JPEncodingJavaUTF8::fetch(std::istream& in) const { unsigned int out = 0; unsigned int c0 = in.get(); if (in.eof()) return -1; if ((c0 & 0x80) == 0) return c0; unsigned int c1 = in.get(); if (in.eof()) return -1; if ((c0 & 0xe0) == 0xc0) { if ((c1 & 0xc0) == 0x80) return ((c0 & 0x1f) << 6) + (c1 & 0x3f); else return -1; // bad 2 byte format } unsigned int c2 = in.get(); if (in.eof()) return -1; if ((c0 & 0xf0) == 0xe0 && (c1 & 0xc0) == 0x80 && (c2 & 0xc0) == 0x80) { out = ((c0 & 0xf) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f); // 0xD800-0xDF00 are surrogate codes for 6 byte encoding // High code is 0xD800-0xDBFF, Low code is 0xDC00-0xDFFF // Plain old code if between 0x0000-0xD7FF, 0xE000-0xFFFF if ((out & 0xf800) != 0xd800) return out; } else { return -1; } // Surrogate code should be followed by 3 byte utf8 code unsigned int next = in.peek(); if (next == (unsigned int) (-1) || (next & 0xf0) != 0xe0) return out; // unpaired surrogate error. // Grab the low word for surrogate code c0 = in.get(); // Note: we should technically check to see what the first byte is here. // for a valid code point it must be 0xED. But if the user has // manually encoded an invalid string there is not much we can // do as we already are pulling bytes for the next character assuming // it was a valid coding. We don't really want to throw an exception // because this routine can happen at any point that string is being // passed, including reporting of an exception. // // Manually encoding invalid code points and asking them to be // converted is undefined behavior, but I don't know the path // to nethack. c1 = in.get(); c2 = in.get(); next = ((c0 & 0xf) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f); if (in.eof()) return -1; unsigned int q1 = (out & 0x3ff); unsigned int q2 = (next & 0x3ff); return 0x10000 + (q1 << 10) + q2; } jpype-1.6.0/native/common/jp_exception.cpp000066400000000000000000000370731501674766700206530ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include "jpype.h" #include "jp_exception.h" #include "pyjp.h" static_assert(std::is_nothrow_copy_constructible::value, "S must be nothrow copy constructible"); PyObject* PyTrace_FromJPStackTrace(JPStackTrace& trace); JPypeException::JPypeException(JPJavaFrame &frame, jthrowable th, const JPStackInfo& stackInfo) : std::runtime_error(frame.toString(th)), m_Type(JPError::_java_error), m_Throwable(frame, th) { JP_TRACE("JAVA EXCEPTION THROWN with java throwable"); m_Error.l = nullptr; from(stackInfo); } JPypeException::JPypeException(int type, void* error, const JPStackInfo& stackInfo) : std::runtime_error("None"), m_Type(type) { JP_TRACE("EXCEPTION THROWN with error", error); m_Error.l = error; from(stackInfo); } JPypeException::JPypeException(int type, void* errType, const string& msn, const JPStackInfo& stackInfo) : std::runtime_error(msn), m_Type(type) { JP_TRACE("EXCEPTION THROWN", errType, msn); m_Error.l = errType; //m_Message = msn; from(stackInfo); } // GCOVR_EXCL_START // This is only used during startup for OSError JPypeException::JPypeException(int type, const string& msn, int errType, const JPStackInfo& stackInfo) : std::runtime_error(msn), m_Type(type) { JP_TRACE("EXCEPTION THROWN", errType, msn); m_Error.i = errType; from(stackInfo); } JPypeException::JPypeException(const JPypeException &ex) noexcept : runtime_error(ex.what()), m_Type(ex.m_Type), m_Error(ex.m_Error), m_Trace(ex.m_Trace), m_Throwable(ex.m_Throwable) { } JPypeException& JPypeException::operator = (const JPypeException& ex) { if(this == &ex) { return *this; } m_Type = ex.m_Type; m_Trace = ex.m_Trace; m_Throwable = ex.m_Throwable; m_Error = ex.m_Error; return *this; } // GCOVR_EXCL_STOP void JPypeException::from(const JPStackInfo& info) { JP_TRACE("EXCEPTION FROM: ", info.getFile(), info.getLine()); m_Trace.push_back(info); } bool isJavaThrowable(PyObject* exceptionClass) { JPClass* cls = PyJPClass_getJPClass(exceptionClass); if (cls == nullptr) return false; return cls->isThrowable(); } void JPypeException::convertJavaToPython() { // Welcome to paranoia land, where they really are out to get you! JP_TRACE_IN("JPypeException::convertJavaToPython"); // GCOVR_EXCL_START JPContext* context = JPContext_global; if (context == nullptr) { PyErr_SetString(PyExc_RuntimeError, "Unable to convert java error, context is null."); return; } // GCOVR_EXCL_STOP // Okay we can get to a frame to talk to the object JPJavaFrame frame = JPJavaFrame::external(context->getEnv()); jthrowable th = m_Throwable.get(); jvalue v; v.l = th; // GCOVR_EXCL_START // This is condition is only hit if something fails during the initial boot if (context->getJavaContext() == nullptr || context->m_Context_GetExcClassID == nullptr) { PyErr_SetString(PyExc_SystemError, frame.toString(th).c_str()); return; } // GCOVR_EXCL_STOP jlong pycls = frame.CallLongMethodA(context->getJavaContext(), context->m_Context_GetExcClassID, &v); if (pycls != 0) { jlong value = frame.CallLongMethodA(context->getJavaContext(), context->m_Context_GetExcValueID, &v); PyErr_SetObject((PyObject*) pycls, (PyObject*) value); return; } JP_TRACE("Check typemanager"); // GCOVR_EXCL_START if (!context->isRunning()) { PyErr_SetString(PyExc_RuntimeError, frame.toString((jobject) th).c_str()); return; } // GCOVR_EXCL_STOP // Convert to Python object JP_TRACE("Convert to python"); JPClass* cls = frame.findClassForObject((jobject) th); // GCOVR_EXCL_START // This sanity check can only fail if the type system fails to find a // class for the current exception. if (cls == nullptr) { // Nope, no class found PyErr_SetString(PyExc_RuntimeError, frame.toString(th).c_str()); return; } // GCOVR_EXCL_STOP // Create the exception object (this may fail) v.l = th; JPPyObject pyvalue = cls->convertToPythonObject(frame, v, false); // GCOVR_EXCL_START // This sanity check can only be hit if the exception failed during // conversion in some extraordinary way. if (pyvalue.isNull()) { PyErr_SetString(PyExc_RuntimeError, frame.toString(th).c_str()); return; } // GCOVR_EXCL_STOP PyObject *type = (PyObject*) Py_TYPE(pyvalue.get()); Py_INCREF(type); // Add cause to the exception JPPyObject args = JPPyObject::call(Py_BuildValue("(s)", "Java Exception")); JPPyObject cause = JPPyObject::call(PyObject_Call(PyExc_Exception, args.get(), nullptr)); JPPyObject trace = PyTrace_FromJavaException(frame, th, nullptr); // Attach Java causes as well. try { jthrowable jcause = frame.getCause(th); if (jcause != nullptr) { jvalue a; a.l = (jobject) jcause; JPPyObject prev = context->_java_lang_Object->convertToPythonObject(frame, a, false); PyJPException_normalize(frame, prev, jcause, th); PyException_SetCause(cause.get(), prev.keep()); } if (trace.get() != nullptr) PyException_SetTraceback(cause.get(), trace.get()); PyException_SetCause(pyvalue.get(), cause.keep()); } catch (JPypeException& ex) { (void) ex; JP_TRACE("FAILURE IN CAUSE"); // Any failures in this optional action should be ignored. // worst case we don't print as much diagnostics. } // Transfer to Python PyErr_SetObject(type, pyvalue.get()); JP_TRACE_OUT; // GCOVR_EXCL_LINE } void JPypeException::convertPythonToJava() { JP_TRACE_IN("JPypeException::convertPythonToJava"); JPJavaFrame frame = JPJavaFrame::outer(); JPContext *context = frame.getContext(); jthrowable th; JPPyErrFrame eframe; if (eframe.good && isJavaThrowable(eframe.m_ExceptionClass.get())) { eframe.good = false; JPValue* javaExc = PyJPValue_getJavaSlot(eframe.m_ExceptionValue.get()); if (javaExc != nullptr) { th = (jthrowable) javaExc->getJavaObject(); JP_TRACE("Throwing Java", frame.toString(th)); frame.Throw(th); return; } } if (context->m_Context_CreateExceptionID == nullptr) { frame.ThrowNew(frame.FindClass("java/lang/RuntimeException"), std::runtime_error::what()); return; } // Otherwise jvalue v[2]; v[0].j = (jlong) eframe.m_ExceptionClass.get(); v[1].j = (jlong) eframe.m_ExceptionValue.get(); th = (jthrowable) frame.CallObjectMethodA(context->getJavaContext(), context->m_Context_CreateExceptionID, v); frame.registerRef((jobject) th, eframe.m_ExceptionClass.get()); frame.registerRef((jobject) th, eframe.m_ExceptionValue.get()); eframe.clear(); frame.Throw(th); JP_TRACE_OUT; // GCOVR_EXCL_LINE } void JPypeException::toPython() { const char* mesg = nullptr; JP_TRACE_IN("JPypeException::toPython"); JP_TRACE("err", PyErr_Occurred()); try { // Check the signals before processing the exception // It may be a signal when interrupted Java in which case // the signal takes precedence. if (PyErr_CheckSignals()!=0) return; mesg = std::runtime_error::what(); JP_TRACE(m_Error.l); JP_TRACE(mesg); // We already have a Python error on the stack. if (PyErr_Occurred()) return; if (m_Type == JPError::_java_error) { JP_TRACE("Java exception"); JPypeException::convertJavaToPython(); return; } else if (m_Type == JPError::_python_error) { // Already on the stack }// This section is only reachable during startup of the JVM. // GCOVR_EXCL_START else if (m_Type == JPError::_os_error_unix) { std::stringstream ss; ss << "JVM DLL not found: " << mesg; PyObject* val = Py_BuildValue("(iz)", m_Error.i, ss.str().c_str()); if (val != nullptr) { PyObject* exc = PyObject_Call(PyExc_OSError, val, nullptr); Py_DECREF(val); if (exc != nullptr) { PyErr_SetObject(PyExc_OSError, exc); Py_DECREF(exc); } } } else if (m_Type == JPError::_os_error_windows) { std::stringstream ss; ss << "JVM DLL not found: " << mesg; PyObject* val = Py_BuildValue("(izzi)", 2, ss.str().c_str(), NULL, m_Error.i); if (val != nullptr) { PyObject* exc = PyObject_Call(PyExc_OSError, val, nullptr); Py_DECREF(val); if (exc != nullptr) { PyErr_SetObject(PyExc_OSError, exc); Py_DECREF(exc); } } }// GCOVR_EXCL_STOP else if (m_Type == JPError::_python_exc) { // All others are Python errors JP_TRACE(Py_TYPE(m_Error.l)->tp_name); PyErr_SetString((PyObject*) m_Error.l, mesg); } else { // This should not be possible unless we failed to cover one of the // exception type codes. JP_TRACE("Unknown error"); PyErr_SetString(PyExc_RuntimeError, mesg); // GCOVR_EXCL_LINE } // Attach our info as the cause if (_jp_cpp_exceptions) { JPPyErrFrame eframe; eframe.normalize(); JPPyObject args = JPPyObject::call(Py_BuildValue("(s)", "C++ Exception")); JPPyObject trace = JPPyObject::call(PyTrace_FromJPStackTrace(m_Trace)); JPPyObject cause = JPPyObject::accept(PyObject_Call(PyExc_Exception, args.get(), nullptr)); if (!cause.isNull()) { PyException_SetTraceback(cause.get(), trace.get()); PyException_SetCause(eframe.m_ExceptionValue.get(), cause.keep()); } } }// GCOVR_EXCL_START catch (JPypeException& ex) { // Print our parting words JPTracer::trace("Fatal error in exception handling"); JPTracer::trace("Handling:", mesg); JPTracer::trace("Type:", m_Error.l); if (ex.m_Type == JPError::_python_error) { JPPyErrFrame eframe; JPTracer::trace("Inner Python:", ((PyTypeObject*) eframe.m_ExceptionClass.get())->tp_name); return; // Let these go to Python, so we can see the error } else if (ex.m_Type == JPError::_java_error) JPTracer::trace("Inner Java:", ex.what()); else JPTracer::trace("Inner:", ex.what()); JPStackInfo info = ex.m_Trace.front(); JPTracer::trace(info.getFile(), info.getFunction(), info.getLine()); // Heghlu'meH QaQ jajvam! PyErr_SetString(PyExc_RuntimeError, "Fatal error occurred"); return; } catch (...) { // urp?! JPTracer::trace("Fatal error in exception handling"); // You shall not pass! int *i = nullptr; *i = 0; } // GCOVR_EXCL_STOP JP_TRACE_OUT; // GCOVR_EXCL_LINE } void JPypeException::toJava() { JP_TRACE_IN("JPypeException::toJava"); JPContext* context = JPContext_global; try { const char* mesg = what(); JPJavaFrame frame = JPJavaFrame::external(context->getEnv()); if (m_Type == JPError::_java_error) { JP_TRACE("Java exception"); //JP_TRACE(context->toString((jobject) frame.ExceptionOccurred())); if (m_Throwable.get() != 0) { JP_TRACE("Java rethrow"); frame.Throw(m_Throwable.get()); return; } return; } if (m_Type == JPError::_python_error) { JPPyCallAcquire callback; JP_TRACE("Python exception"); convertPythonToJava(); return; } if (m_Type == JPError::_python_exc) { JPPyCallAcquire callback; // All others are Python errors JP_TRACE(Py_TYPE(m_Error.l)->tp_name); PyErr_SetString((PyObject*) m_Error.l, mesg); convertPythonToJava(); return; } // All others are issued as RuntimeExceptions JP_TRACE("String exception"); frame.ThrowNew(context->m_RuntimeException.get(), mesg); return; } catch (JPypeException& ex) // GCOVR_EXCL_LINE { // GCOVR_EXCL_START // Print our parting words. JPTracer::trace("Fatal error in exception handling"); JPStackInfo info = ex.m_Trace.front(); JPTracer::trace(info.getFile(), info.getFunction(), info.getLine()); // Take one for the team. int *i = nullptr; *i = 0; // GCOVR_EXCL_STOP } catch (...) // GCOVR_EXCL_LINE { // GCOVR_EXCL_START // urp?! JPTracer::trace("Fatal error in exception handling"); // It is pointless, I can't go on. int *i = nullptr; *i = 0; // GCOVR_EXCL_STOP } JP_TRACE_OUT; // GCOVR_EXCL_LINE } PyObject *tb_create( PyObject *last_traceback, PyObject *dict, const char* filename, const char* funcname, int linenum) { // Create a code for this frame. (ref count is 1) JPPyObject code = JPPyObject::accept((PyObject*)PyCode_NewEmpty(filename, funcname, linenum)); // If we don't get the code object there is no point if (code.get() == nullptr) return nullptr; // Create a frame for the traceback. PyThreadState *state = PyThreadState_GET(); PyFrameObject *pframe = PyFrame_New(state, (PyCodeObject*) code.get(), dict, NULL); JPPyObject frame = JPPyObject::accept((PyObject*)pframe); // If we don't get the frame object there is no point if (frame.get() == nullptr) return nullptr; // Create a traceback #if PY_MINOR_VERSION<11 JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti)); #else JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe))); #endif JPPyObject linenuma = JPPyObject::claim(PyLong_FromLong(linenum)); JPPyObject tuple = JPPyTuple_Pack(Py_None, frame.get(), lasti.get(), linenuma.get()); JPPyObject traceback = JPPyObject::accept(PyObject_Call((PyObject*) &PyTraceBack_Type, tuple.get(), NULL)); // We could fail in process if (traceback.get() == nullptr) { return nullptr; } return traceback.keep(); } PyObject* PyTrace_FromJPStackTrace(JPStackTrace& trace) { PyObject *last_traceback = nullptr; PyObject *dict = PyModule_GetDict(PyJPModule); for (auto& iter : trace) { last_traceback = tb_create(last_traceback, dict, iter.getFile(), iter.getFunction(), iter.getLine()); } if (last_traceback == nullptr) Py_RETURN_NONE; return (PyObject*) last_traceback; } JPPyObject PyTrace_FromJavaException(JPJavaFrame& frame, jthrowable th, jthrowable prev) { PyObject *last_traceback = NULL; JPContext *context = frame.getContext(); jvalue args[2]; args[0].l = th; args[1].l = prev; if (context->m_Context_GetStackFrameID == nullptr) return {}; JNIEnv* env = frame.getEnv(); jobjectArray obj = static_cast(env->CallObjectMethodA(context->getJavaContext(), context->m_Context_GetStackFrameID, args)); // Eat any exceptions that were generated if (env->ExceptionCheck() == JNI_TRUE) env->ExceptionClear(); if (obj == nullptr) return {}; jsize sz = frame.GetArrayLength(obj); PyObject *dict = PyModule_GetDict(PyJPModule); for (jsize i = 0; i < sz; i += 4) { string filename, method; auto jclassname = static_cast(frame.GetObjectArrayElement(obj, i)); auto jmethodname = static_cast(frame.GetObjectArrayElement(obj, i + 1)); auto jfilename = static_cast(frame.GetObjectArrayElement(obj, i + 2)); if (jfilename != nullptr) filename = frame.toStringUTF8(jfilename); else filename = frame.toStringUTF8(jclassname) + ".java"; if (jmethodname != nullptr) method = frame.toStringUTF8(jclassname) + "." + frame.toStringUTF8(jmethodname); jint lineNum = frame.CallIntMethodA(frame.GetObjectArrayElement(obj, i + 3), context->_java_lang_Integer->m_IntValueID, nullptr); // sending -1 will cause issues on Windows if (lineNum<0) lineNum = 0; last_traceback = tb_create(last_traceback, dict, filename.c_str(), method.c_str(), lineNum); frame.DeleteLocalRef(jclassname); frame.DeleteLocalRef(jmethodname); frame.DeleteLocalRef(jfilename); } if (last_traceback == nullptr) return {}; return JPPyObject::call((PyObject*) last_traceback); } jpype-1.6.0/native/common/jp_field.cpp000066400000000000000000000037361501674766700177370ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "jp_field.h" JPField::JPField(JPJavaFrame& frame, JPClass* cls, const string& name, jobject field, jfieldID fid, JPClass* fieldType, jint modifiers) : m_Field(frame, field) { m_Class = cls; m_Name = name; m_FieldID = fid; m_Type = fieldType; m_Modifiers = modifiers; } JPField::~JPField() = default; JPPyObject JPField::getStaticField() { JP_TRACE_IN("JPField::getStaticAttribute"); JPJavaFrame frame = JPJavaFrame::outer(); return m_Type->getStaticField(frame, m_Class->getJavaClass(), m_FieldID); JP_TRACE_OUT; } void JPField::setStaticField(PyObject *pyobj) { JP_TRACE_IN("JPField::setStaticAttribute"); JPJavaFrame frame = JPJavaFrame::outer(); m_Type->setStaticField(frame, m_Class->getJavaClass(), m_FieldID, pyobj); JP_TRACE_OUT; } JPPyObject JPField::getField(jobject inst) { JP_TRACE_IN("JPField::getAttribute"); JPJavaFrame frame = JPJavaFrame::outer(); ASSERT_NOT_NULL(m_Type); JP_TRACE("field type", m_Type->getCanonicalName()); return m_Type->getField(frame, inst, m_FieldID); JP_TRACE_OUT; } void JPField::setField(jobject inst, PyObject *pyobj) { JP_TRACE_IN("JPField::setAttribute"); JPJavaFrame frame = JPJavaFrame::outer(); m_Type->setField(frame, inst, m_FieldID, pyobj); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_floattype.cpp000066400000000000000000000222671501674766700206630ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_floattype.h" #include "jp_boxedtype.h" JPFloatType::JPFloatType() : JPPrimitiveType("float") { } JPFloatType::~JPFloatType() = default; JPClass* JPFloatType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Float; } JPPyObject JPFloatType::convertToPythonObject(JPJavaFrame& frame, jvalue value, bool cast) { PyTypeObject * wrapper = getHost(); JPPyObject obj = JPPyObject::call(wrapper->tp_alloc(wrapper, 0)); ((PyFloatObject*) obj.get())->ob_fval = value.f; PyJPValue_assignJavaSlot(frame, obj.get(), JPValue(this, value)); return obj; } JPValue JPFloatType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; jobject jo = obj.getValue().l; auto* jb = dynamic_cast( frame.findClassForObject(jo)); field(v) = (type_t) frame.CallFloatMethodA(jo, jb->m_FloatValueID, nullptr); return JPValue(this, v); } static JPConversionAsFloat asFloatConversion; static JPConversionLongAsFloat asFloatLongConversion; static JPConversionFloatWiden floatWidenConversion; class JPConversionAsJFloat : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == nullptr) return match.type = JPMatch::_none; match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Consider widening JPClass *cls2 = value->getClass(); if (cls2->isPrimitive()) { // https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2 auto *prim = dynamic_cast( cls2); switch (prim->getTypeCode()) { case 'B': case 'S': case 'C': case 'I': case 'J': match.conversion = &floatWidenConversion; return match.type = JPMatch::_implicit; default: break; } } // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; // stop search } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_float->getHost()); PyList_Append(info.implicit, (PyObject*) context->_byte->getHost()); PyList_Append(info.implicit, (PyObject*) context->_char->getHost()); PyList_Append(info.implicit, (PyObject*) context->_short->getHost()); PyList_Append(info.implicit, (PyObject*) context->_int->getHost()); PyList_Append(info.implicit, (PyObject*) context->_long->getHost()); unboxConversion->getInfo(cls, info); } } asJFloatConversion; JPMatch::Type JPFloatType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPFloatType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (asJFloatConversion.matches(this, match) || asFloatLongConversion.matches(this, match) || asFloatConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPFloatType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); asJFloatConversion.getInfo(this, info); asFloatLongConversion.getInfo(this, info); asFloatConversion.getInfo(this, info); PyList_Append(info.ret, (PyObject*) JPContext_global->_float->getHost()); } jarray JPFloatType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewFloatArray(sz); } JPPyObject JPFloatType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticFloatField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPFloatType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetFloatField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPFloatType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue *val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticFloatMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPFloatType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue *val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallFloatMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualFloatMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPFloatType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject *obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java float"); type_t val = field(match.convert()); frame.SetStaticFloatField(c, fid, val); } void JPFloatType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject *obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java float"); type_t val = field(match.convert()); frame.SetFloatField(c, fid, val); } void JPFloatType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPFloatType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetFloatArrayElements, &JPJavaFrame::ReleaseFloatArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "f"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.f; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { double v = PyFloat_AsDouble(seq[i].get()); if (v == -1.) JP_PY_CHECK(); val[index] = (type_t) v; } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPFloatType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetFloatArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPFloatType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java float"); type_t val = field(match.convert()); frame.SetFloatArrayRegion((array_t) a, ndx, 1, &val); } void JPFloatType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetFloatArrayElements( (jfloatArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "f"; view.m_Buffer.itemsize = sizeof (jfloat); } void JPFloatType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseFloatArrayElements((jfloatArray) view.m_Array->getJava(), (jfloat*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPFloatType::getBufferFormat() { return "f"; } Py_ssize_t JPFloatType::getItemSize() { return sizeof (jfloat); } void JPFloatType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jfloat*) ((char*) memory + offset); frame.GetFloatArrayRegion((jfloatArray) a, start, len, b); } static void pack(jfloat* d, jvalue v) { *d = v.f; } PyObject *JPFloatType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPFloatType::newMultiArray"); return convertMultiArray( frame, this, &pack, "f", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_functional.cpp000066400000000000000000000122041501674766700210040ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_functional.h" #include "jp_proxy.h" JPFunctional::JPFunctional(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers) : JPClass(frame, clss, name, super, interfaces, modifiers) { m_Method = frame.getFunctional(clss); } JPFunctional::~JPFunctional() = default; class JPConversionFunctional : public JPConversion { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyCallable_Check(match.object)) return match.type = JPMatch::_none; // def my_func(x, y=None) should be both a Function and a BiFunction // i.e. the number of parameters accepted by the interface MUST // 1. Be at most the maximum number of parameters accepted by the python function (parameter_count) // (Unless the function accept a variable number of arguments, then this restriction does not // apply). // 2. Be at least the minumum number of parameters accepted by the python function // (parameter_count - optional_parameter_count = number of required parameters). // Notes: // - keywords vargs does not remove restriction 1 // - keyword only arguments are not counted. if (PyFunction_Check(match.object)) { PyObject* func = match.object; auto* code = (PyCodeObject*) PyFunction_GetCode(func); // borrowed Py_ssize_t args = code->co_argcount; bool is_varargs = ((code->co_flags&CO_VARARGS)==CO_VARARGS); Py_ssize_t optional = 0; JPPyObject defaults = JPPyObject::accept(PyObject_GetAttrString(func, "__defaults__")); if (!defaults.isNull() && defaults.get() != Py_None) optional = PyTuple_Size(defaults.get()); const int jargs = JPContext_global->getTypeManager()->interfaceParameterCount(cls); // Too few arguments if (!is_varargs && args < jargs) return match.type = JPMatch::_none; // Too many arguments if (args - optional > jargs) return match.type = JPMatch::_none; } else if (PyMethod_Check(match.object)) { PyObject* func = PyMethod_Function(match.object); // borrowed auto* code = (PyCodeObject*) PyFunction_GetCode(func); // borrowed Py_ssize_t args = code->co_argcount; bool is_varargs = ((code->co_flags&CO_VARARGS)==CO_VARARGS); Py_ssize_t optional = 0; JPPyObject defaults = JPPyObject::accept(PyObject_GetAttrString(func, "__defaults__")); if (!defaults.isNull() && defaults.get() != Py_None) optional = PyTuple_Size(defaults.get()); const int jargs = JPContext_global->getTypeManager()->interfaceParameterCount(cls); // Bound self argument removes one argument if ((PyMethod_Self(match.object))!=nullptr) // borrowed args--; // Too few arguments if (!is_varargs && args < jargs) return match.type = JPMatch::_none; // Too many arguments if (args - optional > jargs) return match.type = JPMatch::_none; } match.conversion = this; match.closure = cls; return match.type = JPMatch::_implicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { PyObject *typing = PyImport_AddModule("jpype.protocol"); JPPyObject proto = JPPyObject::call(PyObject_GetAttrString(typing, "Callable")); PyList_Append(info.implicit, proto.get()); } jvalue convert(JPMatch &match) override { auto *cls = (JPFunctional*) match.closure; JP_TRACE_IN("JPConversionFunctional::convert"); JPJavaFrame frame = JPJavaFrame::inner(); auto *self = (PyJPProxy*) PyJPProxy_Type->tp_alloc(PyJPProxy_Type, 0); JP_PY_CHECK(); JPClassList cl; cl.push_back(cls); self->m_Proxy = new JPProxyFunctional(self, cl); self->m_Target = match.object; self->m_Dispatch = match.object; self->m_Convert = true; Py_INCREF(self->m_Target); Py_INCREF(self->m_Dispatch); jvalue v = self->m_Proxy->getProxy(); v.l = frame.keep(v.l); Py_DECREF(self); return v; JP_TRACE_OUT; // GCOVR_EXCL_LINE } } functional_conversion; JPMatch::Type JPFunctional::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPJPFunctional::findJavaConversiocdn"); JPClass::findJavaConversion(match); if (match.type != JPMatch::_none) return match.type; if (functional_conversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; // GCOVR_EXCL_LINE } void JPFunctional::getConversionInfo(JPConversionInfo &info) { JP_TRACE_IN("JPJPFunctional::getConversionInfo"); JPClass::getConversionInfo(info); functional_conversion.getInfo(this, info); JP_TRACE_OUT; // GCOVR_EXCL_LINE } jpype-1.6.0/native/common/jp_gc.cpp000066400000000000000000000163371501674766700172460ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include "jpype.h" #include "pyjp.h" #include "jp_reference_queue.h" #include "jp_gc.h" #ifdef WIN32 #define USE_PROCESS_INFO #include #include #elif __APPLE__ #define USE_TASK_INFO #include #include #include #elif __GLIBC__ // Linux doesn't have an available rss tally so use mallinfo #define USE_MALLINFO #include #elif __linux__ #define USE_PROC_INFO #include #include #include #include static int statm_fd; static int page_size; #else #define USE_NONE #endif #define DELTA_LIMIT 20*1024*1024l size_t getWorkingSize() { size_t current = 0; #if defined(USE_PROCESS_INFO) PROCESS_MEMORY_COUNTERS pmc; GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof (pmc)); current = (size_t) pmc.WorkingSetSize; #elif defined(USE_TASK_INFO) struct mach_task_basic_info info; mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) & info, &count) == KERN_SUCCESS) current = (size_t) info.resident_size; #elif defined(USE_PROC_INFO) char bytes[32]; lseek(statm_fd, SEEK_SET, 0); int len = read(statm_fd, bytes, 32); long long sz = 0; int i = 0; for (; i < len; i++) { if (bytes[i] == ' ') break; } i++; for (; i < len; i++) { if (bytes[i] == ' ') return sz * page_size; sz *= 10; sz += bytes[i] - '0'; } return sz * page_size; #elif defined(USE_MALLINFO) struct mallinfo mi; mi = mallinfo(); current = (size_t) mi.uordblks; #endif return current; } void triggerPythonGC(); void JPGarbageCollection::triggered() { // If we were triggered from Java call a Python cleanup if (!in_python_gc) { // trigger Python gc in_python_gc = true; java_triggered = true; java_count++; // Lock Python so we call trigger a GC JPPyCallAcquire callback; PyGC_Collect(); } } JPGarbageCollection::JPGarbageCollection() { running = false; in_python_gc = false; java_triggered = false; python_gc = nullptr; _SystemClass = nullptr; _gcMethodID = nullptr; last_python = 0; last_java = 0; low_water = 0; high_water = 0; limit = 0; last = 0; java_count = 0; python_count = 0; python_triggered = 0; } void JPGarbageCollection::init(JPJavaFrame& frame) { #if defined(USE_PROC_INFO) statm_fd = open("/proc/self/statm", O_RDONLY); page_size = getpagesize(); #endif // Get the Python garbage collector JPPyObject gc = JPPyObject::call(PyImport_ImportModule("gc")); python_gc = gc.keep(); // Find the callbacks JPPyObject callbacks = JPPyObject::call(PyObject_GetAttrString(python_gc, "callbacks")); // Hook up our callback JPPyObject collect = JPPyObject::call(PyObject_GetAttrString(PyJPModule, "_collect")); PyList_Append(callbacks.get(), collect.get()); JP_PY_CHECK(); // Get the Java System gc so we can trigger _SystemClass = (jclass) frame.NewGlobalRef(frame.FindClass("java/lang/System")); _gcMethodID = frame.GetStaticMethodID(_SystemClass, "gc", "()V"); jclass ctxt = JPContext_global->m_ContextClass.get(); _ContextClass = ctxt; _totalMemoryID = frame.GetStaticMethodID(ctxt, "getTotalMemory", "()J"); _freeMemoryID = frame.GetStaticMethodID(ctxt, "getFreeMemory", "()J"); _maxMemoryID = frame.GetStaticMethodID(ctxt, "getMaxMemory", "()J"); _usedMemoryID = frame.GetStaticMethodID(ctxt, "getUsedMemory", "()J"); _heapMemoryID = frame.GetStaticMethodID(ctxt, "getHeapMemory", "()J"); running = true; high_water = getWorkingSize(); limit = high_water + DELTA_LIMIT; } void JPGarbageCollection::shutdown() { running = false; #if defined(USE_PROC_INFO) close(statm_fd); #endif } void JPGarbageCollection::onStart() { // GCOVR_EXCL_START // GC is triggered outside of user control. Including it in // coverage just creates random statistics. if (!running) return; getWorkingSize(); in_python_gc = true; // GCOVR_EXCL_STOP } void JPGarbageCollection::onEnd() { // GCOVR_EXCL_START // GC is triggered outside of user control. Including it in // coverage just creates random statistics. if (!running) return; if (java_triggered) { // Remove our lock so that we can watch for triggers java_triggered = false; return; } if (in_python_gc) { in_python_gc = false; python_count++; int run_gc = 0; size_t current = getWorkingSize(); if (current > high_water) high_water = current; if (current < low_water) low_water = current; if (java_triggered) last_java = current; else last_python = current; // Things are getting better so use high water as limit if (current == low_water) { limit = (limit + high_water) / 2; if ( high_water > low_water + 4 * DELTA_LIMIT) high_water = low_water + 4 * DELTA_LIMIT; } if (last_python > current) last_python = current; if (current < last) { last = current; return; } // Decide the policy if (current > limit) { limit = high_water + DELTA_LIMIT; run_gc = 1; } // Predict if we will cross the limit soon. Py_ssize_t pred = current + 2 * (current - last); last = current; if ((Py_ssize_t) pred > (Py_ssize_t) limit) { run_gc = 2; limit = high_water + (high_water>>3) + 8 * (current - last); } #if 0 { JPJavaFrame frame = JPJavaFrame::outer(); jlong totalMemory = frame.CallStaticLongMethodA(_ContextClass, _totalMemoryID, nullptr); jlong freeMemory = frame.CallStaticLongMethodA(_ContextClass, _freeMemoryID, nullptr); jlong maxMemory = frame.CallStaticLongMethodA(_ContextClass, _maxMemoryID, nullptr); jlong usedMemory = frame.CallStaticLongMethodA(_ContextClass, _usedMemoryID, nullptr); jlong heapMemory = frame.CallStaticLongMethodA(_ContextClass, _heapMemoryID, nullptr); printf("consider gc run=%d (current=%ld, low=%ld, high=%ld, limit=%ld) %ld\n", run_gc, current, low_water, high_water, limit, limit - pred); printf(" java total=%ld free=%ld max=%ld used=%ld heap=%ld\n", totalMemory, freeMemory, maxMemory, usedMemory, heapMemory); } #endif if (run_gc > 0) { // Move up the low water low_water = (low_water + high_water) / 2; // Don't reset the limit if it was count triggered JPJavaFrame frame = JPJavaFrame::outer(); frame.CallStaticVoidMethodA(_SystemClass, _gcMethodID, nullptr); python_triggered++; } } // GCOVR_EXCL_STOP } void JPGarbageCollection::getStats(JPGCStats& stats) { // GCOVR_EXCL_START stats.current_rss = getWorkingSize(); stats.min_rss = low_water; stats.max_rss = high_water; stats.java_rss = last_java; stats.python_rss = last_python; stats.python_triggered = python_triggered; // GCOVR_EXCL_STOP } jpype-1.6.0/native/common/jp_inttype.cpp000066400000000000000000000220731501674766700203430ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_inttype.h" JPIntType::JPIntType() : JPPrimitiveType("int") { } JPIntType::~JPIntType() = default; JPClass* JPIntType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Integer; } JPPyObject JPIntType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { JPPyObject tmp = JPPyObject::call(PyLong_FromLong(field(val))); if (getHost() == nullptr) return tmp; JPPyObject out = JPPyObject::call(convertLong(getHost(), (PyLongObject*) tmp.get())); PyJPValue_assignJavaSlot(frame, out.get(), JPValue(this, val)); return out; } JPValue JPIntType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; jobject jo = obj.getValue().l; auto* jb = dynamic_cast( frame.findClassForObject(jo)); field(v) = (type_t) frame.CallIntMethodA(jo, jb->m_IntValueID, nullptr); return JPValue(this, v); } JPConversionLong intConversion; JPConversionLongNumber intNumberConversion; JPConversionLongWiden intWidenConversion; class JPConversionJInt : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == nullptr) return JPMatch::_none; match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Consider widening JPClass *cls2 = value->getClass(); if (cls2->isPrimitive()) { // https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2 auto *prim = dynamic_cast( cls2); switch (prim->getTypeCode()) { case 'C': case 'S': case 'B': match.conversion = &intWidenConversion; return match.type = JPMatch::_implicit; default: break; } } // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; //short cut further checks } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_int->getHost()); PyList_Append(info.implicit, (PyObject*) context->_byte->getHost()); PyList_Append(info.implicit, (PyObject*) context->_char->getHost()); PyList_Append(info.implicit, (PyObject*) context->_short->getHost()); unboxConversion->getInfo(cls, info); } } jintConversion; JPMatch::Type JPIntType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPIntType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (jintConversion.matches(this, match) || intConversion.matches(this, match) || intNumberConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPIntType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); jintConversion.getInfo(this, info); intConversion.getInfo(this, info); intNumberConversion.getInfo(this, info); PyList_Append(info.ret, (PyObject*) JPContext_global->_int->getHost()); } jarray JPIntType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewIntArray(sz); } JPPyObject JPIntType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticIntField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPIntType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetIntField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPIntType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticIntMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPIntType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallIntMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualIntMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPIntType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java int"); type_t val = field(match.convert()); frame.SetStaticIntField(c, fid, val); } void JPIntType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java int"); type_t val = field(match.convert()); frame.SetIntField(c, fid, val); } void JPIntType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPIntType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetIntArrayElements, &JPJavaFrame::ReleaseIntArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "i"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.i; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { PyObject *item = seq[i].get(); if (!PyIndex_Check(item)) { PyErr_Format(PyExc_TypeError, "Unable to implicitly convert '%s' to int", Py_TYPE(item)->tp_name); JP_RAISE_PYTHON(); } jlong v = PyLong_AsLongLong(item); if (v == -1) JP_PY_CHECK(); val[index] = (type_t) assertRange(v); } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPIntType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetIntArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPIntType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java int"); type_t val = field(match.convert()); frame.SetIntArrayRegion((array_t) a, ndx, 1, &val); } void JPIntType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_IsCopy = false; view.m_Memory = (void*) frame.GetIntArrayElements( (jintArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "=i"; view.m_Buffer.itemsize = sizeof (jint); } void JPIntType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseIntArrayElements((jintArray) view.m_Array->getJava(), (jint*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPIntType::getBufferFormat() { return "=i"; } Py_ssize_t JPIntType::getItemSize() { return sizeof (jfloat); } void JPIntType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { jint* b = (jint*) ((char*) memory + offset); frame.GetIntArrayRegion((jintArray) a, start, len, b); } static void pack(jint* d, jvalue v) { *d = v.i; } PyObject *JPIntType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPIntType::newMultiArray"); return convertMultiArray( frame, this, &pack, "i", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_javaframe.cpp000066400000000000000000001072471501674766700206120ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include "jpype.h" #include "jp_reference_queue.h" /*****************************************************************************/ // Local frames represent the JNIEnv for memory handling all java // resources should be created within them. // #if defined(JP_TRACING_ENABLE) || defined(JP_INSTRUMENTATION) static void jpype_frame_check(int popped) { if (popped) JP_RAISE(PyExc_SystemError, "Local reference outside of frame"); } #define JP_FRAME_CHECK() jpype_frame_check(m_Popped) #else #define JP_FRAME_CHECK() if (false) while (false) #endif JPJavaFrame::JPJavaFrame(JNIEnv* p_env, int size, bool outer) : m_Env(p_env), m_Popped(false), m_Outer(outer) { JPContext* context = JPContext_global; if (p_env == nullptr) { if (outer) { #ifdef JP_INSTRUMENTATION PyJPModuleFault_throw(compile_hash("PyJPModule_getContext")); #endif assertJVMRunning((JPContext*)context, JP_STACKINFO()); } m_Env = context->getEnv(); } // Create a memory management frame to live in m_Env->PushLocalFrame(size); JP_TRACE_JAVA("JavaFrame", (jobject) - 1); } JPJavaFrame::JPJavaFrame(const JPJavaFrame& frame) : m_Env(frame.m_Env), m_Popped(false), m_Outer(false) { // Create a memory management frame to live in m_Env->PushLocalFrame(LOCAL_FRAME_DEFAULT); JP_TRACE_JAVA("JavaFrame (copy)", (jobject) - 1); } JPContext* JPJavaFrame::getContext() { // We can add guard statements here. return JPContext_global; } jobject JPJavaFrame::keep(jobject obj) { if (m_Outer) JP_RAISE(PyExc_SystemError, "Keep on outer frame"); JP_FRAME_CHECK(); m_Popped = true; JP_TRACE_JAVA("Keep", obj); JP_TRACE_JAVA("~JavaFrame (keep)", (jobject) - 2); obj = m_Env->PopLocalFrame(obj); JP_TRACE_JAVA("Return", obj); return obj; } JPJavaFrame::~JPJavaFrame() { // Check if we have already closed the frame. if (!m_Popped) { JP_TRACE_JAVA("~JavaFrame", (jobject) - 2); m_Env->PopLocalFrame(nullptr); JP_FRAME_CHECK(); } // It is not safe to detach as we would lose all local references including // any we want to keep. } void JPJavaFrame::DeleteLocalRef(jobject obj) { JP_TRACE_JAVA("Delete local", obj); m_Env->DeleteLocalRef(obj); } void JPJavaFrame::DeleteGlobalRef(jobject obj) { JP_TRACE_JAVA("Delete global", obj); m_Env->DeleteGlobalRef(obj); } jweak JPJavaFrame::NewWeakGlobalRef(jobject obj) { JP_FRAME_CHECK(); JP_TRACE_JAVA("New weak", obj); jweak obj2 = m_Env->NewWeakGlobalRef(obj); JP_TRACE_JAVA("Weak", obj2); return obj2; } void JPJavaFrame::DeleteWeakGlobalRef(jweak obj) { JP_TRACE_JAVA("Delete weak", obj); return m_Env->DeleteWeakGlobalRef(obj); } jobject JPJavaFrame::NewLocalRef(jobject obj) { JP_FRAME_CHECK(); JP_TRACE_JAVA("New local", obj); obj = m_Env->NewLocalRef(obj); JP_TRACE_JAVA("Local", obj); return obj; } jobject JPJavaFrame::NewGlobalRef(jobject obj) { JP_TRACE_JAVA("New Global", obj); obj = m_Env->NewGlobalRef(obj); JP_TRACE_JAVA("Global", obj); return obj; } /*****************************************************************************/ // Exceptions // TODO: why is this never used? Should be deleted if obsolete. bool JPJavaFrame::ExceptionCheck() { return m_Env->ExceptionCheck() != 0; } void JPJavaFrame::ExceptionDescribe() { m_Env->ExceptionDescribe(); } void JPJavaFrame::ExceptionClear() { m_Env->ExceptionClear(); } jint JPJavaFrame::ThrowNew(jclass clazz, const char* msg) { return m_Env->ThrowNew(clazz, msg); } jint JPJavaFrame::Throw(jthrowable th) { return m_Env->Throw(th); } jthrowable JPJavaFrame::ExceptionOccurred() { JP_FRAME_CHECK(); jthrowable obj; obj = m_Env->ExceptionOccurred(); #ifdef JP_TRACING_ENABLE if (obj) { m_Env->ExceptionDescribe(); } #endif JP_TRACE_JAVA("Exception", obj); return obj; } /*****************************************************************************/ #ifdef JP_INSTRUMENTATION #define JAVA_RETURN(X,Y,Z) \ PyJPModuleFault_throw(compile_hash(Y)); \ X ret = Z; \ check(); \ return ret; #define JAVA_RETURN_OBJ(X,Y,Z) \ PyJPModuleFault_throw(compile_hash(Y)); \ X ret = Z; \ check(); \ return ret; #define JAVA_CHECK(Y,Z) \ PyJPModuleFault_throw(compile_hash(Y)); \ Z; \ check(); #else #define JAVA_RETURN(X,Y,Z) \ X ret = Z; \ JP_TRACE_JAVA(Y, 0); \ check(); \ return ret; #define JAVA_RETURN_OBJ(X,Y,Z) \ JP_FRAME_CHECK(); \ X ret = Z; \ JP_TRACE_JAVA(Y, ret); \ check(); \ return ret; #define JAVA_CHECK(Y,Z) \ Z; \ JP_TRACE_JAVA(Y, 0); \ check(); #endif void JPJavaFrame::check() { JP_FRAME_CHECK(); if (m_Env && m_Env->ExceptionCheck() == JNI_TRUE) { jthrowable th = m_Env->ExceptionOccurred(); JP_TRACE_JAVA("ExceptionOccurred", th); m_Env->ExceptionClear(); JP_TRACE_JAVA("ExceptionClear", 0); throw JPypeException(*this, th, JP_STACKINFO()); } } jobject JPJavaFrame::NewObjectA(jclass a0, jmethodID a1, jvalue* a2) { jobject res; JP_FRAME_CHECK(); // Allocate the object JAVA_CHECK("JPJavaFrame::JPJavaFrame::NewObjectA", res = m_Env->AllocObject(a0)); JAVA_CHECK("JPJavaFrame::NewObjectA::AllocObject", m_Env->CallVoidMethodA(res, a1, a2)); JP_TRACE_JAVA("NewObjectA", res); // Initialize the object return res; } jobject JPJavaFrame::NewDirectByteBuffer(void* address, jlong capacity) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::NewDirectByteBuffer", m_Env->NewDirectByteBuffer(address, capacity)); } /*****************************************************************************/ jbyte JPJavaFrame::GetStaticByteField(jclass clazz, jfieldID fid) { JAVA_RETURN(jbyte, "JPJavaFrame::GetStaticByteField", m_Env->GetStaticByteField(clazz, fid)); } jbyte JPJavaFrame::GetByteField(jobject clazz, jfieldID fid) { JAVA_RETURN(jbyte, "JPJavaFrame::GetByteField", m_Env->GetByteField(clazz, fid)); } void JPJavaFrame::SetStaticByteField(jclass clazz, jfieldID fid, jbyte val) { JAVA_CHECK("JPJavaFrame::SetStaticByteField", m_Env->SetStaticByteField(clazz, fid, val)); } void JPJavaFrame::SetByteField(jobject clazz, jfieldID fid, jbyte val) { JAVA_CHECK("JPJavaFrame::SetByteField", m_Env->SetByteField(clazz, fid, val)); } jbyte JPJavaFrame::CallStaticByteMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jbyte, "JPJavaFrame::CallStaticByteMethodA", m_Env->CallStaticByteMethodA(clazz, mid, val)); } jbyte JPJavaFrame::CallByteMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jbyte, "JPJavaFrame::CallByteMethodA", m_Env->CallByteMethodA(obj, mid, val)); } jbyte JPJavaFrame::CallNonvirtualByteMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jbyte, "JPJavaFrame::CallNonvirtualByteMethodA", m_Env->CallNonvirtualByteMethodA(obj, claz, mid, val)); } jshort JPJavaFrame::GetStaticShortField(jclass clazz, jfieldID fid) { JAVA_RETURN(jshort, "JPJavaFrame::GetStaticShortField", m_Env->GetStaticShortField(clazz, fid)); } jshort JPJavaFrame::GetShortField(jobject clazz, jfieldID fid) { JAVA_RETURN(jshort, "JPJavaFrame::GetShortField", m_Env->GetShortField(clazz, fid)); } void JPJavaFrame::SetStaticShortField(jclass clazz, jfieldID fid, jshort val) { JAVA_CHECK("JPJavaFrame::SetStaticShortField", m_Env->SetStaticShortField(clazz, fid, val)); } void JPJavaFrame::SetShortField(jobject clazz, jfieldID fid, jshort val) { JAVA_CHECK("JPJavaFrame::SetShortField", m_Env->SetShortField(clazz, fid, val)); } jshort JPJavaFrame::CallStaticShortMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jshort, "JPJavaFrame::CallStaticShortMethodA", m_Env->CallStaticShortMethodA(clazz, mid, val)); } jshort JPJavaFrame::CallShortMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jshort, "JPJavaFrame::CallShortMethodA", m_Env->CallShortMethodA(obj, mid, val)); } jshort JPJavaFrame::CallNonvirtualShortMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jshort, "JPJavaFrame::CallNonvirtualShortMethodA", m_Env->CallNonvirtualShortMethodA(obj, claz, mid, val)); } jint JPJavaFrame::GetStaticIntField(jclass clazz, jfieldID fid) { JAVA_RETURN(jint, "JPJavaFrame::GetStaticIntField", m_Env->GetStaticIntField(clazz, fid)); } jint JPJavaFrame::GetIntField(jobject clazz, jfieldID fid) { JAVA_RETURN(jint, "JPJavaFrame::GetIntField", m_Env->GetIntField(clazz, fid)); } void JPJavaFrame::SetStaticIntField(jclass clazz, jfieldID fid, jint val) { JAVA_CHECK("JPJavaFrame::SetStaticIntField", m_Env->SetStaticIntField(clazz, fid, val)); } void JPJavaFrame::SetIntField(jobject clazz, jfieldID fid, jint val) { JAVA_CHECK("JPJavaFrame::SetIntField", m_Env->SetIntField(clazz, fid, val)); } jint JPJavaFrame::CallStaticIntMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jint, "JPJavaFrame::CallStaticIntMethodA", m_Env->CallStaticIntMethodA(clazz, mid, val)); } jint JPJavaFrame::CallIntMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jint, "JPJavaFrame::CallIntMethodA", m_Env->CallIntMethodA(obj, mid, val)); } jint JPJavaFrame::CallNonvirtualIntMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jint, "JPJavaFrame::CallNonvirtualIntMethodA", m_Env->CallNonvirtualIntMethodA(obj, claz, mid, val)); } jlong JPJavaFrame::GetStaticLongField(jclass clazz, jfieldID fid) { JAVA_RETURN(jlong, "JPJavaFrame::GetStaticLongField", m_Env->GetStaticLongField(clazz, fid)); } jlong JPJavaFrame::GetLongField(jobject clazz, jfieldID fid) { JAVA_RETURN(jlong, "JPJavaFrame::GetLongField", m_Env->GetLongField(clazz, fid)); } void JPJavaFrame::SetStaticLongField(jclass clazz, jfieldID fid, jlong val) { JAVA_CHECK("JPJavaFrame::SetStaticLongField", m_Env->SetStaticLongField(clazz, fid, val)); } void JPJavaFrame::SetLongField(jobject clazz, jfieldID fid, jlong val) { JAVA_CHECK("JPJavaFrame::SetLongField", m_Env->SetLongField(clazz, fid, val)); } jlong JPJavaFrame::CallStaticLongMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jlong, "JPJavaFrame::CallStaticLongMethodA", m_Env->CallStaticLongMethodA(clazz, mid, val)); } jlong JPJavaFrame::CallLongMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jlong, "JPJavaFrame::CallLongMethodA", m_Env->CallLongMethodA(obj, mid, val)); } jlong JPJavaFrame::CallNonvirtualLongMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jlong, "JPJavaFrame::CallNonvirtualLongMethodA", m_Env->CallNonvirtualLongMethodA(obj, claz, mid, val)); } jfloat JPJavaFrame::GetStaticFloatField(jclass clazz, jfieldID fid) { JAVA_RETURN(jfloat, "JPJavaFrame::GetStaticFloatField", m_Env->GetStaticFloatField(clazz, fid)); } jfloat JPJavaFrame::GetFloatField(jobject clazz, jfieldID fid) { JAVA_RETURN(jfloat, "JPJavaFrame::GetFloatField", m_Env->GetFloatField(clazz, fid)); } void JPJavaFrame::SetStaticFloatField(jclass clazz, jfieldID fid, jfloat val) { JAVA_CHECK("JPJavaFrame::SetStaticFloatField", m_Env->SetStaticFloatField(clazz, fid, val)); } void JPJavaFrame::SetFloatField(jobject clazz, jfieldID fid, jfloat val) { JAVA_CHECK("JPJavaFrame::SetFloatField", m_Env->SetFloatField(clazz, fid, val)); } jfloat JPJavaFrame::CallStaticFloatMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jfloat, "JPJavaFrame::CallStaticFloatMethodA", m_Env->CallStaticFloatMethodA(clazz, mid, val)); } jfloat JPJavaFrame::CallFloatMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jfloat, "JPJavaFrame::CallFloatMethodA", m_Env->CallFloatMethodA(obj, mid, val)); } jfloat JPJavaFrame::CallNonvirtualFloatMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jfloat, "JPJavaFrame::CallNonvirtualFloatMethodA", m_Env->CallNonvirtualFloatMethodA(obj, claz, mid, val)); } jdouble JPJavaFrame::GetStaticDoubleField(jclass clazz, jfieldID fid) { JAVA_RETURN(jdouble, "JPJavaFrame::GetStaticDoubleField", m_Env->GetStaticDoubleField(clazz, fid)); } jdouble JPJavaFrame::GetDoubleField(jobject clazz, jfieldID fid) { JAVA_RETURN(jdouble, "JPJavaFrame::GetDoubleField", m_Env->GetDoubleField(clazz, fid)); } void JPJavaFrame::SetStaticDoubleField(jclass clazz, jfieldID fid, jdouble val) { JAVA_CHECK("JPJavaFrame::SetStaticDoubleField", m_Env->SetStaticDoubleField(clazz, fid, val)); } void JPJavaFrame::SetDoubleField(jobject clazz, jfieldID fid, jdouble val) { JAVA_CHECK("JPJavaFrame::SetDoubleField", m_Env->SetDoubleField(clazz, fid, val)); } jdouble JPJavaFrame::CallStaticDoubleMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jdouble, "JPJavaFrame::CallStaticDoubleMethodA", m_Env->CallStaticDoubleMethodA(clazz, mid, val)); } jdouble JPJavaFrame::CallDoubleMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jdouble, "JPJavaFrame::CallDoubleMethodA", m_Env->CallDoubleMethodA(obj, mid, val)); } jdouble JPJavaFrame::CallNonvirtualDoubleMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jdouble, "JPJavaFrame::CallNonvirtualDoubleMethodA", m_Env->CallNonvirtualDoubleMethodA(obj, claz, mid, val)); } jchar JPJavaFrame::GetStaticCharField(jclass clazz, jfieldID fid) { JAVA_RETURN(jchar, "JPJavaFrame::GetStaticCharField", m_Env->GetStaticCharField(clazz, fid)); } jchar JPJavaFrame::GetCharField(jobject clazz, jfieldID fid) { JAVA_RETURN(jchar, "JPJavaFrame::GetCharField", m_Env->GetCharField(clazz, fid)); } void JPJavaFrame::SetStaticCharField(jclass clazz, jfieldID fid, jchar val) { JAVA_CHECK("JPJavaFrame::SetStaticCharField", m_Env->SetStaticCharField(clazz, fid, val)); } void JPJavaFrame::SetCharField(jobject clazz, jfieldID fid, jchar val) { JAVA_CHECK("JPJavaFrame::SetCharField", m_Env->SetCharField(clazz, fid, val)); } jchar JPJavaFrame::CallStaticCharMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jchar, "JPJavaFrame::CallStaticCharMethodA", m_Env->CallStaticCharMethodA(clazz, mid, val)); } jchar JPJavaFrame::CallCharMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jchar, "JPJavaFrame::CallCharMethodA", m_Env->CallCharMethodA(obj, mid, val)); } jchar JPJavaFrame::CallNonvirtualCharMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jchar, "JPJavaFrame::CallNonvirtualCharMethodA", m_Env->CallNonvirtualCharMethodA(obj, claz, mid, val)); } jboolean JPJavaFrame::GetStaticBooleanField(jclass clazz, jfieldID fid) { JAVA_RETURN(jboolean, "JPJavaFrame::GetStaticBooleanField", m_Env->GetStaticBooleanField(clazz, fid)); } jboolean JPJavaFrame::GetBooleanField(jobject clazz, jfieldID fid) { JAVA_RETURN(jboolean, "JPJavaFrame::GetBooleanField", m_Env->GetBooleanField(clazz, fid)); } void JPJavaFrame::SetStaticBooleanField(jclass clazz, jfieldID fid, jboolean val) { JAVA_CHECK("JPJavaFrame::SetStaticBooleanField", m_Env->SetStaticBooleanField(clazz, fid, val)); } void JPJavaFrame::SetBooleanField(jobject clazz, jfieldID fid, jboolean val) { JAVA_CHECK("JPJavaFrame::SetBooleanField", m_Env->SetBooleanField(clazz, fid, val)); } jboolean JPJavaFrame::CallStaticBooleanMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN(jboolean, "JPJavaFrame::CallStaticBooleanMethodA", m_Env->CallStaticBooleanMethodA(clazz, mid, val)); } jboolean JPJavaFrame::CallBooleanMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN(jboolean, "JPJavaFrame::CallBooleanMethodA", m_Env->CallBooleanMethodA(obj, mid, val)); } jboolean JPJavaFrame::CallNonvirtualBooleanMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN(jboolean, "JPJavaFrame::CallNonvirtualBooleanMethodA", m_Env->CallNonvirtualBooleanMethodA(obj, claz, mid, val)); } jclass JPJavaFrame::GetObjectClass(jobject obj) { JAVA_RETURN_OBJ(jclass, "JPJavaFrame::GetObjectClass", m_Env->GetObjectClass(obj)); } jobject JPJavaFrame::GetStaticObjectField(jclass clazz, jfieldID fid) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::GetStaticObjectField", m_Env->GetStaticObjectField(clazz, fid)); } jobject JPJavaFrame::GetObjectField(jobject clazz, jfieldID fid) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::GetObjectField", m_Env->GetObjectField(clazz, fid)); } void JPJavaFrame::SetStaticObjectField(jclass clazz, jfieldID fid, jobject val) { JAVA_CHECK("JPJavaFrame::SetStaticObjectField", m_Env->SetStaticObjectField(clazz, fid, val)); } void JPJavaFrame::SetObjectField(jobject clazz, jfieldID fid, jobject val) { JAVA_CHECK("JPJavaFrame::SetObjectField", m_Env->SetObjectField(clazz, fid, val)); } jobject JPJavaFrame::CallStaticObjectMethodA(jclass clazz, jmethodID mid, jvalue* val) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::CallStaticObjectMethodA", m_Env->CallStaticObjectMethodA(clazz, mid, val)); } jobject JPJavaFrame::CallObjectMethodA(jobject obj, jmethodID mid, jvalue* val) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::CallObjectMethodA", m_Env->CallObjectMethodA(obj, mid, val)); } jobject JPJavaFrame::CallNonvirtualObjectMethodA(jobject obj, jclass claz, jmethodID mid, jvalue* val) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::CallNonvirtualObjectMethodA", m_Env->CallNonvirtualObjectMethodA(obj, claz, mid, val)); } jbyteArray JPJavaFrame::NewByteArray(jsize len) { JAVA_RETURN_OBJ(jbyteArray, "JPJavaFrame::NewByteArray", m_Env->NewByteArray(len)); } void JPJavaFrame::SetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* vals) { JAVA_CHECK("JPJavaFrame::SetByteArrayRegion", m_Env->SetByteArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* vals) { JAVA_CHECK("JPJavaFrame::GetByteArrayRegion", m_Env->GetByteArrayRegion(array, start, len, vals)); } jbyte* JPJavaFrame::GetByteArrayElements(jbyteArray array, jboolean* isCopy) { JAVA_RETURN(jbyte*, "JPJavaFrame::GetByteArrayElements", m_Env->GetByteArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseByteArrayElements(jbyteArray array, jbyte* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseByteArrayElements", m_Env->ReleaseByteArrayElements(array, v, mode)); } jshortArray JPJavaFrame::NewShortArray(jsize len) { JAVA_RETURN_OBJ(jshortArray, "JPJavaFrame::NewShortArray", m_Env->NewShortArray(len)); } void JPJavaFrame::SetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* vals) { JAVA_CHECK("JPJavaFrame::SetShortArrayRegion", m_Env->SetShortArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* vals) { JAVA_CHECK("JPJavaFrame::GetShortArrayRegion", m_Env->GetShortArrayRegion(array, start, len, vals)); } jshort* JPJavaFrame::GetShortArrayElements(jshortArray array, jboolean* isCopy) { JAVA_RETURN(jshort*, "JPJavaFrame::GetShortArrayElements", m_Env->GetShortArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseShortArrayElements(jshortArray array, jshort* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseShortArrayElements", m_Env->ReleaseShortArrayElements(array, v, mode)); } jintArray JPJavaFrame::NewIntArray(jsize len) { JAVA_RETURN_OBJ(jintArray, "JPJavaFrame::NewIntArray", m_Env->NewIntArray(len)); } void JPJavaFrame::SetIntArrayRegion(jintArray array, jsize start, jsize len, jint* vals) { JAVA_CHECK("JPJavaFrame::SetIntArrayRegion", m_Env->SetIntArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* vals) { JAVA_CHECK("JPJavaFrame::GetIntArrayRegion", m_Env->GetIntArrayRegion(array, start, len, vals)); } jint* JPJavaFrame::GetIntArrayElements(jintArray array, jboolean* isCopy) { JAVA_RETURN(jint*, "JPJavaFrame::GetIntArrayElements", m_Env->GetIntArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseIntArrayElements(jintArray array, jint* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseIntArrayElements", m_Env->ReleaseIntArrayElements(array, v, mode)); } jlongArray JPJavaFrame::NewLongArray(jsize len) { JAVA_RETURN_OBJ(jlongArray, "JPJavaFrame::NewLongArray", m_Env->NewLongArray(len)); } void JPJavaFrame::SetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* vals) { JAVA_CHECK("JPJavaFrame::SetLongArrayRegion", m_Env->SetLongArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* vals) { JAVA_CHECK("JPJavaFrame::GetLongArrayRegion", m_Env->GetLongArrayRegion(array, start, len, vals)); } jlong* JPJavaFrame::GetLongArrayElements(jlongArray array, jboolean* isCopy) { JAVA_RETURN(jlong*, "JPJavaFrame::GetLongArrayElements", m_Env->GetLongArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseLongArrayElements(jlongArray array, jlong* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseLongArrayElements", m_Env->ReleaseLongArrayElements(array, v, mode)); } jfloatArray JPJavaFrame::NewFloatArray(jsize len) { JAVA_RETURN_OBJ(jfloatArray, "JPJavaFrame::NewFloatArray", m_Env->NewFloatArray(len)); } void JPJavaFrame::SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* vals) { JAVA_CHECK("JPJavaFrame::SetFloatArrayRegion", m_Env->SetFloatArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* vals) { JAVA_CHECK("JPJavaFrame::GetFloatArrayRegion", m_Env->GetFloatArrayRegion(array, start, len, vals)); } jfloat* JPJavaFrame::GetFloatArrayElements(jfloatArray array, jboolean* isCopy) { JAVA_RETURN(jfloat*, "JPJavaFrame::GetFloatArrayElements", m_Env->GetFloatArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseFloatArrayElements(jfloatArray array, jfloat* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseFloatArrayElements", m_Env->ReleaseFloatArrayElements(array, v, mode)); } jdoubleArray JPJavaFrame::NewDoubleArray(jsize len) { JAVA_RETURN_OBJ(jdoubleArray, "JPJavaFrame::NewDoubleArray", m_Env->NewDoubleArray(len)); } void JPJavaFrame::SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* vals) { JAVA_CHECK("JPJavaFrame::SetDoubleArrayRegion", m_Env->SetDoubleArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* vals) { JAVA_CHECK("JPJavaFrame::GetDoubleArrayRegion", m_Env->GetDoubleArrayRegion(array, start, len, vals)); } jdouble* JPJavaFrame::GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy) { JAVA_RETURN(jdouble*, "JPJavaFrame::GetDoubleArrayElements", m_Env->GetDoubleArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseDoubleArrayElements(jdoubleArray array, jdouble* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseDoubleArrayElements", m_Env->ReleaseDoubleArrayElements(array, v, mode)); } jcharArray JPJavaFrame::NewCharArray(jsize len) { JAVA_RETURN_OBJ(jcharArray, "JPJavaFrame::NewCharArray", m_Env->NewCharArray(len)); } void JPJavaFrame::SetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* vals) { JAVA_CHECK("JPJavaFrame::SetCharArrayRegion", m_Env->SetCharArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* vals) { JAVA_CHECK("JPJavaFrame::GetCharArrayRegion", m_Env->GetCharArrayRegion(array, start, len, vals)); } jchar* JPJavaFrame::GetCharArrayElements(jcharArray array, jboolean* isCopy) { JAVA_RETURN(jchar*, "JPJavaFrame::GetCharArrayElements", m_Env->GetCharArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseCharArrayElements(jcharArray array, jchar* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseCharArrayElements", m_Env->ReleaseCharArrayElements(array, v, mode)); } jbooleanArray JPJavaFrame::NewBooleanArray(jsize len) { JAVA_RETURN_OBJ(jbooleanArray, "JPJavaFrame::NewBooleanArray", m_Env->NewBooleanArray(len)); } void JPJavaFrame::SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* vals) { JAVA_CHECK("JPJavaFrame::SetBooleanArrayRegion", m_Env->SetBooleanArrayRegion(array, start, len, vals)); } void JPJavaFrame::GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* vals) { JAVA_CHECK("JPJavaFrame::GetBooleanArrayRegion", m_Env->GetBooleanArrayRegion(array, start, len, vals)); } jboolean* JPJavaFrame::GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy) { JAVA_RETURN(jboolean*, "JPJavaFrame::GetBooleanArrayElements", m_Env->GetBooleanArrayElements(array, isCopy)); } void JPJavaFrame::ReleaseBooleanArrayElements(jbooleanArray array, jboolean* v, jint mode) { JAVA_CHECK("JPJavaFrame::ReleaseBooleanArrayElements", m_Env->ReleaseBooleanArrayElements(array, v, mode)); } int JPJavaFrame::MonitorEnter(jobject a0) { JAVA_RETURN(int, "JPJavaFrame::MonitorEnter", m_Env->MonitorEnter(a0)); } int JPJavaFrame::MonitorExit(jobject a0) { JAVA_RETURN(int, "JPJavaFrame::MonitorExit", m_Env->MonitorExit(a0)); } jmethodID JPJavaFrame::FromReflectedMethod(jobject a0) { JAVA_RETURN(jmethodID, "JPJavaFrame::FromReflectedMethod", m_Env->FromReflectedMethod(a0)); } jfieldID JPJavaFrame::FromReflectedField(jobject a0) { JAVA_RETURN(jfieldID, "JPJavaFrame::FromReflectedField", m_Env->FromReflectedField(a0)); } jclass JPJavaFrame::FindClass(const string& a0) { JAVA_RETURN(jclass, "JPJavaFrame::FindClass", m_Env->FindClass(a0.c_str())); } jobjectArray JPJavaFrame::NewObjectArray(jsize a0, jclass elementClass, jobject initialElement) { JAVA_RETURN(jobjectArray, "JPJavaFrame::NewObjectArray", m_Env->NewObjectArray(a0, elementClass, initialElement)); } void JPJavaFrame::SetObjectArrayElement(jobjectArray a0, jsize a1, jobject a2) { JAVA_CHECK("JPJavaFrame::SetObjectArrayElement", m_Env->SetObjectArrayElement(a0, a1, a2)); } void JPJavaFrame::CallStaticVoidMethodA(jclass a0, jmethodID a1, jvalue* a2) { JAVA_CHECK("JPJavaFrame::CallStaticVoidMethodA", m_Env->CallStaticVoidMethodA(a0, a1, a2)); } void JPJavaFrame::CallVoidMethodA(jobject a0, jmethodID a1, jvalue* a2) { JAVA_CHECK("JPJavaFrame::CallVoidMethodA", m_Env->CallVoidMethodA(a0, a1, a2)); } void JPJavaFrame::CallNonvirtualVoidMethodA(jobject a0, jclass a1, jmethodID a2, jvalue* a3) { JAVA_CHECK("JPJavaFrame::CallVoidMethodA", m_Env->CallNonvirtualVoidMethodA(a0, a1, a2, a3)); } jboolean JPJavaFrame::IsInstanceOf(jobject a0, jclass a1) { JAVA_RETURN(jboolean, "JPJavaFrame::IsInstanceOf", m_Env->IsInstanceOf(a0, a1)); } jboolean JPJavaFrame::IsAssignableFrom(jclass a0, jclass a1) { JAVA_RETURN(jboolean, "JPJavaFrame::IsAssignableFrom", m_Env->IsAssignableFrom(a0, a1)); } jstring JPJavaFrame::NewStringUTF(const char* a0) { JAVA_RETURN_OBJ(jstring, "JPJavaFrame::NewString", m_Env->NewStringUTF(a0)); } const char* JPJavaFrame::GetStringUTFChars(jstring a0, jboolean* a1) { JAVA_RETURN(const char*, "JPJavaFrame::GetStringUTFChars", m_Env->GetStringUTFChars(a0, a1)); } void JPJavaFrame::ReleaseStringUTFChars(jstring a0, const char* a1) { JAVA_CHECK("JPJavaFrame::ReleaseStringUTFChars", m_Env->ReleaseStringUTFChars(a0, a1)); } jsize JPJavaFrame::GetArrayLength(jarray a0) { JAVA_RETURN(jsize, "JPJavaFrame::GetArrayLength", m_Env->GetArrayLength(a0)); } jobject JPJavaFrame::GetObjectArrayElement(jobjectArray a0, jsize a1) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::GetObjectArrayElement", m_Env->GetObjectArrayElement(a0, a1)); } jmethodID JPJavaFrame::GetMethodID(jclass a0, const char* a1, const char* a2) { JAVA_RETURN(jmethodID, "JPJavaFrame::GetMethodID", m_Env->GetMethodID(a0, a1, a2)); } jmethodID JPJavaFrame::GetStaticMethodID(jclass a0, const char* a1, const char* a2) { JAVA_RETURN(jmethodID, "JPJavaFrame::GetStaticMethodID", m_Env->GetStaticMethodID(a0, a1, a2)); } jfieldID JPJavaFrame::GetFieldID(jclass a0, const char* a1, const char* a2) { JAVA_RETURN(jfieldID, "JPJavaFrame::GetFieldID", m_Env->GetFieldID(a0, a1, a2)); } jfieldID JPJavaFrame::GetStaticFieldID(jclass a0, const char* a1, const char* a2) { JAVA_RETURN(jfieldID, "JPJavaFrame::GetStaticFieldID", m_Env->GetStaticFieldID(a0, a1, a2)); } jsize JPJavaFrame::GetStringUTFLength(jstring a0) { JAVA_RETURN(jsize, "JPJavaFrame::GetStringUTFLength", m_Env->GetStringUTFLength(a0)); } jclass JPJavaFrame::DefineClass(const char* a0, jobject a1, const jbyte* a2, jsize a3) { JAVA_RETURN(jclass, "JPJavaFrame::DefineClass", m_Env->DefineClass(a0, a1, a2, a3)); } jint JPJavaFrame::RegisterNatives(jclass a0, const JNINativeMethod* a1, jint a2) { JAVA_RETURN(jint, "JPJavaFrame::RegisterNatives", m_Env->RegisterNatives(a0, a1, a2)); } void* JPJavaFrame::GetDirectBufferAddress(jobject obj) { JAVA_RETURN(void*, "JPJavaFrame::GetDirectBufferAddress", m_Env->GetDirectBufferAddress(obj)); } jlong JPJavaFrame::GetDirectBufferCapacity(jobject obj) { JAVA_RETURN(jlong, "JPJavaFrame::GetDirectBufferAddress", m_Env->GetDirectBufferCapacity(obj)); } jboolean JPJavaFrame::isBufferReadOnly(jobject obj) { return CallBooleanMethodA(obj, getContext()->m_Buffer_IsReadOnlyID, nullptr); } jobject JPJavaFrame::asReadOnlyBuffer(jobject obj) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::asReadOnlyBuffer", m_Env->CallObjectMethod(obj, getContext()->m_Buffer_AsReadOnlyID)); } jboolean JPJavaFrame::orderBuffer(jobject obj) { jvalue arg; arg.l = obj; JPContext *context = getContext(); return CallBooleanMethodA(context->m_JavaContext.get(), context->m_Context_OrderID, &arg); } // GCOVR_EXCL_START // This is used when debugging reference counting problems. jclass JPJavaFrame::getClass(jobject obj) { return (jclass) CallObjectMethodA(obj, getContext()->m_Object_GetClassID, nullptr); } // GCOVR_EXCL_STOP class JPStringAccessor { JPJavaFrame& frame_; jboolean isCopy; public: const char* cstr; int length; jstring jstr_; JPStringAccessor(JPJavaFrame& frame, jstring jstr) : frame_(frame), jstr_(jstr) { cstr = frame_.GetStringUTFChars(jstr, &isCopy); length = frame_.GetStringUTFLength(jstr); } ~JPStringAccessor() { try { frame_.ReleaseStringUTFChars(jstr_, cstr); } catch (JPypeException&) { // Error during release must be eaten. // If Java does not accept a release of the buffer it // is Java's issue, not ours. } } } ; string JPJavaFrame::toString(jobject o) { auto str = (jstring) CallObjectMethodA(o, getContext()->m_Object_ToStringID, nullptr); if (str == nullptr) return "null"; return toStringUTF8(str); } string JPJavaFrame::toStringUTF8(jstring str) { JPStringAccessor contents(*this, str); #ifdef ANDROID return string(contents.cstr, contents.length); #else return transcribe(contents.cstr, contents.length, JPEncodingJavaUTF8(), JPEncodingUTF8()); #endif } jstring JPJavaFrame::fromStringUTF8(const string& str) { #ifdef ANDROID return (jstring) NewStringUTF(str.c_str()); #else string mstr = transcribe(str.c_str(), str.size(), JPEncodingUTF8(), JPEncodingJavaUTF8()); return (jstring) NewStringUTF(mstr.c_str()); #endif } jobject JPJavaFrame::toCharArray(jstring jstr) { return CallObjectMethodA(jstr, getContext()->m_String_ToCharArrayID, nullptr); } bool JPJavaFrame::equals(jobject o1, jobject o2 ) { jvalue args; args.l = o2; return CallBooleanMethodA(o1, getContext()->m_Object_EqualsID, &args) != 0; } jint JPJavaFrame::hashCode(jobject o) { return CallIntMethodA(o, getContext()->m_Object_HashCodeID, nullptr); } jobject JPJavaFrame::collectRectangular(jarray obj) { JPContext* context = getContext(); if (context->m_Context_collectRectangularID == nullptr) return nullptr; jvalue v; v.l = (jobject) obj; JAVA_RETURN(jobject, "JPJavaFrame::collectRectangular", CallObjectMethodA( context->m_JavaContext.get(), context->m_Context_collectRectangularID, &v)); } jobject JPJavaFrame::assemble(jobject dims, jobject parts) { JPContext* context = getContext(); if (context->m_Context_collectRectangularID == nullptr) return nullptr; jvalue v[2]; v[0].l = (jobject) dims; v[1].l = (jobject) parts; JAVA_RETURN(jobject, "JPJavaFrame::assemble", CallObjectMethodA( context->m_JavaContext.get(), context->m_Context_assembleID, v)); } jobject JPJavaFrame::newArrayInstance(jclass c, jintArray dims) { JPContext* context = getContext(); jvalue v[2]; v[0].l = (jobject) c; v[1].l = (jobject) dims; JAVA_RETURN(jobject, "JPJavaFrame::newArrayInstance", CallStaticObjectMethodA( context->m_Array.get(), context->m_Array_NewInstanceID, v)); } jobject JPJavaFrame::callMethod(jobject method, jobject obj, jobject args) { JP_TRACE_IN("JPJavaFrame::callMethod"); JPContext* context = getContext(); if (context->m_CallMethodID == nullptr) return nullptr; JPJavaFrame frame(*this); jvalue v[3]; v[0].l = method; v[1].l = obj; v[2].l = args; return frame.keep(frame.CallObjectMethodA(context->m_Reflector.get(), context->m_CallMethodID, v)); JP_TRACE_OUT; } string JPJavaFrame::getFunctional(jclass c) { JPContext* context = getContext(); jvalue v; v.l = (jobject) c; return toStringUTF8((jstring) CallStaticObjectMethodA( context->m_ContextClass.get(), context->m_Context_GetFunctionalID, &v)); } JPClass *JPJavaFrame::findClass(jclass obj) { return getContext()->getTypeManager()->findClass(obj); } JPClass *JPJavaFrame::findClassByName(const string& name) { return getContext()->getTypeManager()->findClassByName(name); } JPClass *JPJavaFrame::findClassForObject(jobject obj) { return getContext()->getTypeManager()->findClassForObject(obj); } jint JPJavaFrame::compareTo(jobject obj, jobject obj2) { jvalue v; v.l = obj2; jint ret = m_Env->CallIntMethodA(obj, getContext()->m_CompareToID, &v); if (m_Env->ExceptionOccurred()) { m_Env->ExceptionClear(); JP_RAISE(PyExc_TypeError, "Unable to compare") } return ret; } jthrowable JPJavaFrame::getCause(jthrowable th) { return (jthrowable) CallObjectMethodA((jobject) th, getContext()->m_Throwable_GetCauseID, nullptr); } jstring JPJavaFrame::getMessage(jthrowable th) { return (jstring) CallObjectMethodA((jobject) th, getContext()->m_Throwable_GetMessageID, nullptr); } jboolean JPJavaFrame::isPackage(const string& str) { JPContext* context = getContext(); jvalue v; v.l = fromStringUTF8(str); JAVA_RETURN(jboolean, "JPJavaFrame::isPackage", CallBooleanMethodA(context->m_JavaContext.get(), context->m_Context_IsPackageID, &v)); } jobject JPJavaFrame::getPackage(const string& str) { JPContext* context = getContext(); jvalue v; v.l = fromStringUTF8(str); JAVA_RETURN(jobject, "JPJavaFrame::getPackage", CallObjectMethodA(context->m_JavaContext.get(), context->m_Context_GetPackageID, &v)); } jobject JPJavaFrame::getPackageObject(jobject pkg, const string& str) { jvalue v; v.l = fromStringUTF8(str); JAVA_RETURN(jobject, "JPJavaFrame::getPackageObject", CallObjectMethodA(pkg, getContext()->m_Package_GetObjectID, &v)); } jarray JPJavaFrame::getPackageContents(jobject pkg) { jvalue v; JAVA_RETURN(auto, "JPJavaFrame::getPackageContents", (jarray) CallObjectMethodA(pkg, getContext()->m_Package_GetContentsID, &v)); } void JPJavaFrame::newWrapper(JPClass* cls) { JPContext* context = getContext(); JPPyCallRelease call; jvalue jv; jv.j = (jlong) cls; CallVoidMethodA(context->getJavaContext(), context->m_Context_NewWrapperID, &jv); } void JPJavaFrame::registerRef(jobject obj, PyObject* hostRef) { JPReferenceQueue::registerRef(*this, obj, hostRef); } void JPJavaFrame::registerRef(jobject obj, void* ref, JCleanupHook cleanup) { JPReferenceQueue::registerRef(*this, obj, ref, cleanup); } void JPJavaFrame::clearInterrupt(bool throws) { JPContext* context = getContext(); JPPyCallRelease call; jvalue jv; jv.z = throws; CallVoidMethodA(context->m_ContextClass.get(), context->m_Context_ClearInterruptID, &jv); } jpype-1.6.0/native/common/jp_longtype.cpp000066400000000000000000000221721501674766700205100ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_longtype.h" JPLongType::JPLongType() : JPPrimitiveType("long") { } JPLongType::~JPLongType() = default; JPClass* JPLongType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Long; } JPPyObject JPLongType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { JPPyObject tmp = JPPyObject::call(PyLong_FromLongLong(field(val))); JPPyObject out = JPPyObject::call(convertLong(getHost(), (PyLongObject*) tmp.get())); PyJPValue_assignJavaSlot(frame, out.get(), JPValue(this, val)); return out; } JPValue JPLongType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; jobject jo = obj.getValue().l; auto* jb = dynamic_cast( frame.findClassForObject(jo)); field(v) = (type_t) frame.CallLongMethodA(jo, jb->m_LongValueID, nullptr); return JPValue(this, v); } JPConversionLong longConversion; JPConversionLongNumber longNumberConversion; JPConversionLongWiden longWidenConversion; class JPConversionJLong : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue* value = match.getJavaSlot(); if (value == nullptr) return match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Consider widening JPClass *cls2 = value->getClass(); if (cls2->isPrimitive()) { // https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2 auto *prim = dynamic_cast( cls2); switch (prim->getTypeCode()) { case 'I': case 'C': case 'S': case 'B': match.conversion = &longWidenConversion; return match.type = JPMatch::_implicit; default: break; } } // Unboxing must be to the from the exact boxed type (JLS 5.1.8) match.type = JPMatch::_none; return JPMatch::_implicit; } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_long->getHost()); PyList_Append(info.implicit, (PyObject*) context->_byte->getHost()); PyList_Append(info.implicit, (PyObject*) context->_char->getHost()); PyList_Append(info.implicit, (PyObject*) context->_short->getHost()); PyList_Append(info.implicit, (PyObject*) context->_int->getHost()); unboxConversion->getInfo(cls, info); } } jlongConversion; JPMatch::Type JPLongType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPLongType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (jlongConversion.matches(this, match) || longConversion.matches(this, match) || longNumberConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPLongType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); jlongConversion.getInfo(this, info); longConversion.getInfo(this, info); longNumberConversion.getInfo(this, info); PyList_Append(info.ret, (PyObject*) JPContext_global->_long->getHost()); } jarray JPLongType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewLongArray(sz); } JPPyObject JPLongType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticLongField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPLongType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetLongField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPLongType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticLongMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPLongType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallLongMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualLongMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPLongType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java int"); type_t val = field(match.convert()); frame.SetStaticLongField(c, fid, val); } void JPLongType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java int"); type_t val = field(match.convert()); frame.SetLongField(c, fid, val); } void JPLongType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPLongType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetLongArrayElements, &JPJavaFrame::ReleaseLongArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "j"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.j; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { PyObject *item = seq[i].get(); if (!PyIndex_Check(item)) { PyErr_Format(PyExc_TypeError, "Unable to implicitly convert '%s' to long", Py_TYPE(item)->tp_name); JP_RAISE_PYTHON(); } jlong v = PyLong_AsLongLong(item); if (v == -1) JP_PY_CHECK() val[index] = (type_t) v; } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPLongType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetLongArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPLongType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java int"); type_t val = field(match.convert()); frame.SetLongArrayRegion((array_t) a, ndx, 1, &val); } void JPLongType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetLongArrayElements( (jlongArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "=q"; view.m_Buffer.itemsize = sizeof (jlong); } void JPLongType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseLongArrayElements((jlongArray) view.m_Array->getJava(), (jlong*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPLongType::getBufferFormat() { return "=q"; } Py_ssize_t JPLongType::getItemSize() { return sizeof (jlong); } void JPLongType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jlong*) ((char*) memory + offset); frame.GetLongArrayRegion((jlongArray) a, start, len, b); } static void pack(jlong* d, jvalue v) { *d = v.j; } PyObject *JPLongType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPLongType::newMultiArray"); return convertMultiArray( frame, this, &pack, "j", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_method.cpp000066400000000000000000000240541501674766700201300ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "jp_arrayclass.h" #include "jp_method.h" #include "pyjp.h" JPMethod::JPMethod(JPJavaFrame& frame, JPClass* claz, const string& name, jobject mth, jmethodID mid, JPMethodList& moreSpecific, jint modifiers) : m_Method(frame, mth) { m_Class = claz; m_Name = name; m_MethodID = mid; m_MoreSpecificOverloads = moreSpecific; m_Modifiers = modifiers; m_ReturnType = (JPClass*) (-1); } JPMethod::~JPMethod() = default; void JPMethod::setParameters( JPClass *returnType, JPClassList&& parameterTypes) { m_ReturnType = returnType; m_ParameterTypes = parameterTypes; } string JPMethod::toString() const { return m_Name; } JPMatch::Type matchVars(JPJavaFrame &frame, JPMethodMatch& match, JPPyObjectVector &arg, size_t start, JPClass *vartype) { JP_TRACE_IN("JPMethod::matchVars"); auto *arraytype = dynamic_cast( vartype); JPClass *type = arraytype->getComponentType(); size_t len = arg.size(); JPMatch::Type lastMatch = JPMatch::_exact; for (size_t i = start; i < len; i++) { JPMatch::Type quality = type->findJavaConversion(match[i]); if (quality < JPMatch::_implicit) { return JPMatch::_none; } if (quality < lastMatch) { lastMatch = quality; } } return lastMatch; JP_TRACE_OUT; // GCOVR_EXCL_LINE } JPMatch::Type JPMethod::matches(JPJavaFrame &frame, JPMethodMatch& methodMatch, bool callInstance, JPPyObjectVector& arg) { ensureTypeCache(); JP_TRACE_IN("JPMethod::matches"); methodMatch.m_Overload = this; methodMatch.m_Offset = 0; methodMatch.m_Skip = 0; methodMatch.m_IsVarIndirect = false; methodMatch.m_Type = JPMatch::_exact; size_t len = arg.size(); size_t tlen = m_ParameterTypes.size(); JP_TRACE("Flags", isStatic(), isInstance(), callInstance); JP_TRACE("arguments length", len); JP_TRACE("types length", tlen); if (callInstance && isStatic()) { JP_TRACE("Skip this"); len--; methodMatch.m_Offset = 1; } if (callInstance || isInstance()) { JP_TRACE("Take this"); methodMatch.m_Skip = 1; } if (!JPModifier::isVarArgs(m_Modifiers)) { // Bypass if length does not match if (len != tlen) { JP_TRACE("Argument length mismatch", len, tlen); return methodMatch.m_Type = JPMatch::_none; } } else { JP_TRACE("Match vargs"); methodMatch.m_Type = JPMatch::_none; if (len < tlen - 1) { return methodMatch.m_Type; } JPClass* type = m_ParameterTypes[tlen - 1]; // Hard, could be direct array or an array. if (len == tlen) { // Try direct size_t last = tlen - 1 - methodMatch.m_Offset; methodMatch.m_Type = type->findJavaConversion(methodMatch.m_Arguments[last]); JP_TRACE("Direct vargs", methodMatch.m_Type); } if (methodMatch.m_Type < JPMatch::_implicit && len >= tlen) { // Must match the array type methodMatch.m_Type = matchVars(frame, methodMatch, arg, tlen - 1 + methodMatch.m_Offset, type); methodMatch.m_IsVarIndirect = true; JP_TRACE("Indirect vargs", methodMatch.m_Type); } else if (len < tlen) { methodMatch.m_IsVarIndirect = true; methodMatch.m_Type = JPMatch::_exact; JP_TRACE("Empty vargs"); } len = tlen - 1; if (methodMatch.m_Type < JPMatch::_implicit) { return methodMatch.m_Type; } } JP_TRACE("Match args"); for (size_t i = 0; i < len; i++) { size_t j = i + methodMatch.m_Offset; JPClass *type = m_ParameterTypes[i]; JPMatch::Type ematch = type->findJavaConversion(methodMatch.m_Arguments[j]); JP_TRACE("Result", ematch); if (ematch < methodMatch.m_Type) { methodMatch.m_Type = ematch; } if (methodMatch.m_Type < JPMatch::_implicit) { return methodMatch.m_Type; } } return methodMatch.m_Type; JP_TRACE_OUT; // GCOVR_EXCL_LINE } void JPMethod::packArgs(JPJavaFrame &frame, JPMethodMatch &match, vector &v, JPPyObjectVector &arg) { JP_TRACE_IN("JPMethod::packArgs"); size_t len = arg.size(); size_t tlen = m_ParameterTypes.size(); JP_TRACE("skip", match.m_Skip == 1); JP_TRACE("offset", match.m_Offset == 1); JP_TRACE("arguments length", len); JP_TRACE("types length", tlen); if (match.m_IsVarIndirect) { JP_TRACE("Pack indirect varargs"); len = tlen - 1; auto* type = dynamic_cast( m_ParameterTypes[tlen - 1]); v[tlen - 1 - match.m_Skip] = type->convertToJavaVector(frame, arg, (jsize) tlen - 1, (jsize) arg.size()); } JP_TRACE("Pack fixed total=", len - match.m_Offset); for (size_t i = match.m_Skip; i < len; i++) { v[i - match.m_Skip] = match.m_Arguments[i].convert(); } JP_TRACE_OUT; // GCOVR_EXCL_LINE } JPPyObject JPMethod::invoke(JPJavaFrame& frame, JPMethodMatch& match, JPPyObjectVector& arg, bool instance) { JP_TRACE_IN("JPMethod::invoke"); // Check if it is caller sensitive if (isCallerSensitive()) return invokeCallerSensitive(match, arg, instance); size_t alen = m_ParameterTypes.size(); JPClass* retType = m_ReturnType; // Pack the arguments vector v(alen + 1); packArgs(frame, match, v, arg); // Invoke the method (arg[0] = this) if (JPModifier::isStatic(m_Modifiers)) { JP_TRACE("invoke static", m_Name); jclass claz = m_Class->getJavaClass(); return retType->invokeStatic(frame, claz, m_MethodID, &v[0]); } else { JPValue* selfObj = PyJPValue_getJavaSlot(arg[0]); jobject c; if (selfObj == nullptr) { // This only can be hit by calling an instance method as a // class object. We already know it is safe to convert. auto val = match.m_Arguments[0].convert(); c = val.l; } else { c = selfObj->getJavaObject(); } jclass clazz = nullptr; if (!isAbstract() && !instance) { clazz = m_Class->getJavaClass(); JP_TRACE("invoke nonvirtual", m_Name); } else { JP_TRACE("invoke virtual", m_Name); } return retType->invoke(frame, c, clazz, m_MethodID, &v[0]); } JP_TRACE_OUT; // GCOVR_EXCL_LINE } JPPyObject JPMethod::invokeCallerSensitive(JPMethodMatch& match, JPPyObjectVector& arg, bool instance) { JP_TRACE_IN("JPMethod::invokeCallerSensitive"); size_t alen = m_ParameterTypes.size(); JPJavaFrame frame = JPJavaFrame::outer((int) (8 + alen)); JPContext *context = frame.getContext(); JPClass* retType = m_ReturnType; // Pack the arguments vector v(alen + 1); packArgs(frame, match, v, arg); //Proxy the call to // public static Object callMethod(Method method, Object obj, Object[] args) jobject self = nullptr; size_t len = alen; if (!isStatic()) { JP_TRACE("Call instance"); len--; JPValue *selfObj = PyJPValue_getJavaSlot(arg[0]); if (selfObj == nullptr) JP_RAISE(PyExc_RuntimeError, "Null object"); // GCOVR_EXCL_LINE self = selfObj->getJavaObject(); } // Convert arguments jobjectArray ja = frame.NewObjectArray((jsize) len, context->_java_lang_Object->getJavaClass(), nullptr); for (jsize i = 0; i < (jsize) len; ++i) { JPClass *cls = m_ParameterTypes[i + match.m_Skip - match.m_Offset]; if (cls->isPrimitive()) { auto* type = dynamic_cast( cls); PyObject *u = arg[i + match.m_Skip]; JPMatch conv(&frame, u); JPClass *boxed = type->getBoxedClass(frame); boxed->findJavaConversion(conv); jvalue v = conv.convert(); frame.SetObjectArrayElement(ja, i, v.l); } else { frame.SetObjectArrayElement(ja, i, v[i].l); } } // Call the method jobject o; { JPPyCallRelease call; o = frame.callMethod(m_Method.get(), self, ja); } JP_TRACE("ReturnType", retType->getCanonicalName()); // Deal with the return if (retType->isPrimitive()) { JP_TRACE("Return primitive"); JPClass *boxed = (dynamic_cast( retType))->getBoxedClass(frame); JPValue out = retType->getValueFromObject(frame, JPValue(boxed, o)); return retType->convertToPythonObject(frame, out.getValue(), false); } else { JP_TRACE("Return object"); jvalue v; v.l = o; return retType->convertToPythonObject(frame, v, false); } JP_TRACE_OUT; } JPValue JPMethod::invokeConstructor(JPJavaFrame& frame, JPMethodMatch& match, JPPyObjectVector& arg) { JP_TRACE_IN("JPMethod::invokeConstructor"); size_t alen = m_ParameterTypes.size(); vector v(alen + 1); packArgs(frame, match, v, arg); JPPyCallRelease call; return JPValue(m_Class, frame.NewObjectA(m_Class->getJavaClass(), m_MethodID, &v[0])); JP_TRACE_OUT; // GCOVR_EXCL_LINE } string JPMethod::matchReport(JPPyObjectVector& args) { ensureTypeCache(); JPJavaFrame frame = JPJavaFrame::outer(); std::stringstream res; res << m_ReturnType->getCanonicalName() << " ("; bool isFirst = true; for (auto & m_ParameterType : m_ParameterTypes) { if (isFirst && !isStatic()) { isFirst = false; continue; } isFirst = false; res << m_ParameterType->getCanonicalName(); } res << ") ==> "; JPMethodMatch methodMatch(frame, args, false); matches(frame, methodMatch, !isStatic(), args); // GCOVR_EXCL_START switch (methodMatch.m_Type) { case JPMatch::_none: res << "NONE"; break; case JPMatch::_explicit: res << "EXPLICIT"; break; case JPMatch::_implicit: res << "IMPLICIT"; break; case JPMatch::_exact: res << "EXACT"; break; default: res << "UNKNOWN"; break; } // GCOVR_EXCL_STOP res << std::endl; return res.str(); } bool JPMethod::checkMoreSpecificThan(JPMethod* other) const { for (auto m_MoreSpecificOverload : m_MoreSpecificOverloads) { if (other == m_MoreSpecificOverload) return true; } return false; } void JPMethod::ensureTypeCache() { if (m_ReturnType != (JPClass*) (-1)) return; JPContext_global->getTypeManager()->populateMethod(this, m_Method.get()); } jpype-1.6.0/native/common/jp_methoddispatch.cpp000066400000000000000000000163761501674766700216600ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include "jpype.h" #include "jp_method.h" #include "jp_methoddispatch.h" JPMethodDispatch::JPMethodDispatch(JPClass* clazz, const string& name, JPMethodList& overloads, jint modifiers) : m_Name(name) { m_Class = clazz; m_Overloads = overloads; m_Modifiers = modifiers; m_LastCache.m_Hash = -1; } JPMethodDispatch::~JPMethodDispatch() = default; const string& JPMethodDispatch::getName() const { return m_Name; } bool JPMethodDispatch::findOverload(JPJavaFrame& frame, JPMethodMatch &bestMatch, JPPyObjectVector& arg, bool callInstance, bool raise) { JP_TRACE_IN("JPMethodDispatch::findOverload"); JP_TRACE("Checking overload", m_Name); JP_TRACE("Got overloads to check", m_Overloads.size()); JPMethodList ambiguous; // Check cache to see if we already resolved this overload. // First we need to see if the hash matches for the last set of arguments. // Then make sure we don't hit the rare case that the hash was -1 by chance. // Then make sure it isn't variadic list match, as the hash of an opaque list // element can't be resolved without going through the resolution process. if (m_LastCache.m_Hash == bestMatch.m_Hash && m_LastCache.m_Overload != nullptr && !m_LastCache.m_Overload->isVarArgs()) { bestMatch.m_Overload = m_LastCache.m_Overload; bestMatch.m_Overload->matches(frame, bestMatch, callInstance, arg); // Anything better than explicit constitutes a hit on the cache if (bestMatch.m_Type > JPMatch::_explicit) return true; else // bad match so forget the overload. bestMatch.m_Overload = nullptr; } // We need two copies of the match. One to hold the best match we have // found, and one to hold the test of the next overload. JPMethodMatch match = bestMatch; // Check each overload in order (the TypeManager orders them by priority // according to Java overload rules). for (auto & current : m_Overloads) { JP_TRACE("Trying to match", current->toString()); current->matches(frame, match, callInstance, arg); JP_TRACE(" match ended", match.m_Type); if (match.m_Type == JPMatch::_exact) { // We can bypass the process here as there is no better match than exact. bestMatch = match; m_LastCache = (JPMethodCache&) match; // lgtm [cpp/slicing] return true; } if (match.m_Type < JPMatch::_implicit) continue; // If this is the first match then make it the best. if (bestMatch.m_Overload == nullptr) { bestMatch = match; continue; } // If the best does not hide the other, than we have ambiguity. if (!(bestMatch.m_Overload->checkMoreSpecificThan(current))) { // See if we can match based on instance if (callInstance == !current->isStatic()) { // if current matches instance and best does not switch if (callInstance == bestMatch.m_Overload->isStatic()) { bestMatch = match; continue; } } else { // if best matches instance and current does not, no ambiguity if (callInstance == !bestMatch.m_Overload->isStatic()) { continue; } } // If the best match is worse than current, switch if (bestMatch.m_Type < match.m_Type) { bestMatch = match; // Remove any prior ambiguity ambiguous.clear(); continue; } // If the best match is still better than current, keep it if (bestMatch.m_Type > match.m_Type) { continue; } JP_TRACE("Adding to ambiguous list"); // Keep trace of ambiguous overloads for the error report. ambiguous.push_back(current); } } // If we have an ambiguous overload report an error. if (!ambiguous.empty()) { if (!raise) return false; ambiguous.push_back(bestMatch.m_Overload); // We have two possible overloads so we declare an error std::stringstream ss; if (JPModifier::isConstructor(m_Modifiers)) ss << "Ambiguous overloads found for constructor " << m_Class->getCanonicalName() << "("; else ss << "Ambiguous overloads found for " << m_Class->getCanonicalName() << "." << getName() << "("; size_t start = callInstance ? 1 : 0; for (size_t i = start; i < arg.size(); ++i) { if (i != start) ss << ","; ss << Py_TYPE(arg[i])->tp_name; } ss << ")" << " between:" << std::endl; for (auto & ambiguous_mthd : ambiguous) { ss << "\t" << ambiguous_mthd->toString() << std::endl; } JP_RAISE(PyExc_TypeError, ss.str()); JP_TRACE(ss.str()); } // If we can't find a matching overload throw an error. if (!bestMatch.m_Overload) { if (!raise) return false; std::stringstream ss; if (JPModifier::isConstructor(m_Modifiers)) ss << "No matching overloads found for constructor " << m_Class->getCanonicalName() << "("; else { ss << "No matching overloads found for "; if (!callInstance) ss << "*static* "; ss << m_Class->getCanonicalName() << "." << getName() << "("; } size_t start = callInstance ? 1 : 0; for (size_t i = start; i < arg.size(); ++i) { if (i != start) ss << ","; ss << Py_TYPE(arg[i])->tp_name; } ss << ")" << ", options are:" << std::endl; for (auto current : m_Overloads) { ss << "\t" << current->toString(); ss << std::endl; } JP_RAISE(PyExc_TypeError, ss.str()); } // Set up a cache to bypass repeated calls. if (bestMatch.m_Type == JPMatch::_implicit) { m_LastCache = (JPMethodCache&) bestMatch; // lgtm [cpp/slicing] } JP_TRACE("Best match", bestMatch.m_Overload->toString()); return true; JP_TRACE_OUT; } JPPyObject JPMethodDispatch::invoke(JPJavaFrame& frame, JPPyObjectVector& args, bool instance) { JP_TRACE_IN("JPMethodDispatch::invoke"); JPMethodMatch match(frame, args, instance); findOverload(frame, match, args, instance, true); return match.m_Overload->invoke(frame, match, args, instance); JP_TRACE_OUT; } JPValue JPMethodDispatch::invokeConstructor(JPJavaFrame& frame, JPPyObjectVector& args) { JP_TRACE_IN("JPMethodDispatch::invokeConstructor"); JPMethodMatch match(frame, args, false); findOverload(frame, match, args, false, true); return match.m_Overload->invokeConstructor(frame, match, args); JP_TRACE_OUT; } bool JPMethodDispatch::matches(JPJavaFrame& frame, JPPyObjectVector& args, bool instance) { JP_TRACE_IN("JPMethodDispatch::invoke"); JPMethodMatch match(frame, args, instance); return findOverload(frame, match, args, instance, false); JP_TRACE_OUT; // GCOVR_EXCL_LINE } string JPMethodDispatch::matchReport(JPPyObjectVector& args) { std::stringstream res; res << "Match report for method " << m_Name << ", has " << m_Overloads.size() << " overloads." << std::endl; for (auto current : m_Overloads) { res << " " << current->matchReport(args); } return res.str(); } jpype-1.6.0/native/common/jp_monitor.cpp000066400000000000000000000022721501674766700203350ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "jp_monitor.h" JPMonitor::JPMonitor(jobject value) : m_Value(value) { } JPMonitor::~JPMonitor() = default; void JPMonitor::enter() { // This can hold off for a while so we need to release resource // so that we don't dead lock. JPPyCallRelease call; JPJavaFrame frame = JPJavaFrame::outer(); frame.MonitorEnter(m_Value.get()); } void JPMonitor::exit() { JPJavaFrame frame = JPJavaFrame::outer(); frame.MonitorExit(m_Value.get()); } jpype-1.6.0/native/common/jp_numbertype.cpp000066400000000000000000000035761501674766700210500ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_numbertype.h" JPNumberType::JPNumberType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers) : JPClass(frame, clss, name, super, interfaces, modifiers) { } JPNumberType::~JPNumberType() = default; JPMatch::Type JPNumberType::findJavaConversion(JPMatch& match) { // Rules for java.lang.Object JP_TRACE_IN("JPNumberType::canConvertToJava"); if (nullConversion->matches(this, match) || javaNumberAnyConversion->matches(this, match) || boxLongConversion->matches(this, match) || boxDoubleConversion->matches(this, match) || hintsConversion->matches(this, match) ) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPNumberType::getConversionInfo(JPConversionInfo &info) { JP_TRACE_IN("JPNumberType::getConversionInfo"); JPJavaFrame frame = JPJavaFrame::outer(); javaNumberAnyConversion->getInfo(this, info); boxLongConversion->getInfo(this, info); boxDoubleConversion->getInfo(this, info); hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_objecttype.cpp000066400000000000000000000043651501674766700210230ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_objecttype.h" JPObjectType::JPObjectType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers) : JPClass(frame, clss, name, super, interfaces, modifiers) { } JPObjectType::~JPObjectType() = default; JPMatch::Type JPObjectType::findJavaConversion(JPMatch& match) { // Rules for java.lang.Object JP_TRACE_IN("JPObjectType::canConvertToJava"); if (nullConversion->matches(this, match) || javaObjectAnyConversion->matches(this, match) || stringConversion->matches(this, match) || boxBooleanConversion->matches(this, match) || boxLongConversion->matches(this, match) || boxDoubleConversion->matches(this, match) || classConversion->matches(this, match) || proxyConversion->matches(this, match) || hintsConversion->matches(this, match) ) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPObjectType::getConversionInfo(JPConversionInfo &info) { JP_TRACE_IN("JPObjectType::getConversionInfo"); JPJavaFrame frame = JPJavaFrame::outer(); nullConversion->getInfo(this, info); objectConversion->getInfo(this, info); stringConversion->getInfo(this, info); boxBooleanConversion->getInfo(this, info); boxLongConversion->getInfo(this, info); boxDoubleConversion->getInfo(this, info); classConversion->getInfo(this, info); proxyConversion->getInfo(this, info); hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_platform.cpp000066400000000000000000000105231501674766700204700ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "jp_platform.h" JPPlatformAdapter::~JPPlatformAdapter() = default; #ifdef WIN32 #include /** * Windows-specific platform adapter */ class Win32PlatformAdapter : public JPPlatformAdapter { private: HINSTANCE jvmLibrary; std::string formatMessage(DWORD msgCode) { LPVOID lpMsgBuf; DWORD rc = ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, msgCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL ); // Fail to get format if (rc == 0) { std::stringstream ss; ss << "error code " << msgCode; return ss.str(); } std::string res((LPTSTR) lpMsgBuf); LocalFree(lpMsgBuf); return res; } public: virtual void loadLibrary(const char* path) override { JP_TRACE_IN("Win32PlatformAdapter::loadLibrary"); wchar_t *wpath = Py_DecodeLocale(path, NULL); if (wpath == NULL) { JP_RAISE(PyExc_SystemError, "Unable to get JVM path with locale."); } jvmLibrary = LoadLibraryW(wpath); PyMem_RawFree(wpath); if (jvmLibrary == NULL) { JP_RAISE_OS_ERROR_WINDOWS( GetLastError(), path); } JP_TRACE_OUT; } virtual void unloadLibrary() override { // on success return code is nonzero, TODO: handle error? FreeLibrary(jvmLibrary); } virtual void* getSymbol(const char* name) override { void* res = (void*) GetProcAddress(jvmLibrary, name); if (res == NULL) { std::stringstream msg; msg << "Unable to load symbol [" << name << "], error = " << formatMessage(GetLastError()); JP_RAISE(PyExc_RuntimeError, msg.str().c_str()); } return res; } } ; #define PLATFORM_ADAPTER Win32PlatformAdapter #else #if defined(_HPUX) && !defined(_IA64) #include #else #include #endif // HPUX #include // The code in this modules is mostly excluded from coverage as it is only // possible to execute during a fatal error. class LinuxPlatformAdapter : public JPPlatformAdapter { private: void* jvmLibrary; public: void loadLibrary(const char* path) override { JP_TRACE_IN("LinuxPlatformAdapter::loadLibrary"); #if defined(_HPUX) && !defined(_IA64) JP_TRACE("shl_load", path); jvmLibrary = shl_load(path, BIND_DEFERRED | BIND_VERBOSE, 0L); #else JP_TRACE("dlopen", path); jvmLibrary = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); #endif // HPUX // GCOVR_EXCL_START if (jvmLibrary == nullptr) { JP_TRACE("null library"); JP_TRACE("errno", errno); if (errno == ENOEXEC) { JP_TRACE("dignostics", dlerror()); } JP_RAISE_OS_ERROR_UNIX( errno, path); } // GCOVR_EXCL_STOP JP_TRACE_OUT; // GCOVR_EXCL_LINE } void unloadLibrary() override { JP_TRACE_IN("LinuxPlatformAdapter::unloadLibrary"); int r = dlclose(jvmLibrary); // GCOVR_EXCL_START if (r != 0) // error { std::cerr << dlerror() << std::endl; } // GCOVR_EXCL_STOP JP_TRACE_OUT; // GCOVR_EXCL_LINE } void* getSymbol(const char* name) override { JP_TRACE_IN("LinuxPlatformAdapter::getSymbol"); JP_TRACE("Load", name); void* res = dlsym(jvmLibrary, name); JP_TRACE("Res", res); // GCOVR_EXCL_START if (res == nullptr) { JP_TRACE("errno", errno); std::stringstream msg; msg << "Unable to load symbol [" << name << "], error = " << dlerror(); JP_RAISE(PyExc_RuntimeError, msg.str().c_str()); } // GCOVR_EXCL_STOP return res; JP_TRACE_OUT; // GCOVR_EXCL_LINE } } ; #define PLATFORM_ADAPTER LinuxPlatformAdapter #endif namespace { PLATFORM_ADAPTER* adapter; } JPPlatformAdapter* JPPlatformAdapter::getAdapter() { if (adapter == nullptr) adapter = new PLATFORM_ADAPTER(); return adapter; } jpype-1.6.0/native/common/jp_primitivetype.cpp000066400000000000000000000040171501674766700215570ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" JPPrimitiveType::JPPrimitiveType(const string& name) : JPClass(name, 0x411) { } JPPrimitiveType::~JPPrimitiveType() = default; bool JPPrimitiveType::isPrimitive() const { return true; } extern "C" Py_ssize_t PyJPValue_getJavaSlotOffset(PyObject* self); // equivalent of long_subtype_new as it isn't exposed PyObject *JPPrimitiveType::convertLong(PyTypeObject* wrapper, PyLongObject* tmp) { if (wrapper == nullptr) JP_RAISE(PyExc_SystemError, "bad wrapper"); #if PY_VERSION_HEX<0x030c0000 // Determine number of digits to copy Py_ssize_t n = Py_SIZE(tmp); if (n < 0) n = -n; auto *newobj = (PyLongObject *) wrapper->tp_alloc(wrapper, n); if (newobj == nullptr) return nullptr; // Size is in units of digits ((PyVarObject*) newobj)->ob_size = Py_SIZE(tmp); for (Py_ssize_t i = 0; i < n; i++) { newobj->ob_digit[i] = tmp->ob_digit[i]; } #else // 3.12 completely does away with ob_size field and repurposes it // Determine the number of digits to copy int n = (tmp->long_value.lv_tag >> 3); PyLongObject *newobj = (PyLongObject *) wrapper->tp_alloc(wrapper, n); if (newobj == NULL) return NULL; newobj->long_value.lv_tag = tmp->long_value.lv_tag; memcpy(&newobj->long_value.ob_digit, &tmp->long_value.ob_digit, n*sizeof(digit)); #endif return (PyObject*) newobj; } jpype-1.6.0/native/common/jp_proxy.cpp000066400000000000000000000227021501674766700200270ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_proxy.h" #include "jp_classloader.h" #include "jp_reference_queue.h" #include "jp_primitive_accessor.h" #include "jp_boxedtype.h" #include "jp_functional.h" JPPyObject getArgs(jlongArray parameterTypePtrs, jobjectArray args, PyObject* self, int addSelf) { JP_TRACE_IN("JProxy::getArgs"); JPJavaFrame frame = JPJavaFrame::outer(); jsize argLen = frame.GetArrayLength(parameterTypePtrs); jsize extra = addSelf?1:0; JPPyObject pyargs = JPPyObject::call(PyTuple_New(argLen+extra)); JPPrimitiveArrayAccessor accessor(frame, parameterTypePtrs, &JPJavaFrame::GetLongArrayElements, &JPJavaFrame::ReleaseLongArrayElements); if (addSelf) { Py_IncRef(self); PyTuple_SetItem(pyargs.get(), 0, self); } jlong* types = accessor.get(); for (jsize i = 0; i < argLen; i++) { jobject obj = frame.GetObjectArrayElement(args, i); JPClass* type = frame.findClassForObject(obj); if (type == nullptr) type = reinterpret_cast (types[i]); JPValue val = type->getValueFromObject(frame, JPValue(type, obj)); PyTuple_SetItem(pyargs.get(), i+extra, type->convertToPythonObject(frame, val, false).keep()); } return pyargs; JP_TRACE_OUT; } extern "C" JNIEXPORT jobject JNICALL Java_org_jpype_proxy_JPypeProxy_hostInvoke( JNIEnv *env, jclass clazz, jlong contextPtr, jstring name, jlong hostObj, jlong returnTypePtr, jlongArray parameterTypePtrs, jobjectArray args, jobject missing) { JPJavaFrame frame = JPJavaFrame::external(env); // We need the resources to be held for the full duration of the proxy. JPPyCallAcquire callback; try { JP_TRACE_IN("JPype_InvocationHandler_hostInvoke"); JP_TRACE("hostObj", (void*) hostObj); try { // GCOVR_EXCL_START // Sanity check, should never be hit if (hostObj == 0) { env->functions->ThrowNew(env, JPContext_global->m_RuntimeException.get(), "host reference is null"); return nullptr; } // GCOVR_EXCL_STOP string cname = frame.toStringUTF8(name); JP_TRACE("Get callable for", cname); // Get the callable object int addSelf = 0; JPProxy* proxy = (JPProxy*) hostObj; JPPyObject callable(proxy->getCallable(cname, addSelf)); // If method can't be called, throw an exception if (callable.isNull() || callable.get() == Py_None) return missing; // Find the return type auto* returnClass = (JPClass*) returnTypePtr; JP_TRACE("Get return type", returnClass->getCanonicalName()); // convert the arguments into a python list JP_TRACE("Convert arguments"); JPPyObject pyargs = getArgs(parameterTypePtrs, args, proxy->m_Instance->m_Target, addSelf); JP_TRACE("Call Python"); JPPyObject returnValue = JPPyObject::call(PyObject_Call(callable.get(), pyargs.get(), nullptr)); JP_TRACE("Handle return", Py_TYPE(returnValue.get())->tp_name); if (returnClass == JPContext_global->_void) { JP_TRACE("Void return"); return nullptr; } // This is a SystemError where the caller return null without // setting a Python error. if (returnValue.isNull()) { JP_TRACE("Null return"); JP_RAISE(PyExc_TypeError, "Return value is null when it cannot be"); } // We must box here. JPMatch returnMatch(&frame, returnValue.get()); if (returnClass->isPrimitive()) { JP_TRACE("Box return"); if (returnClass->findJavaConversion(returnMatch) == JPMatch::_none) JP_RAISE(PyExc_TypeError, "Return value is not compatible with required type."); jvalue res = returnMatch.convert(); auto *boxed = dynamic_cast( (dynamic_cast( returnClass))->getBoxedClass(frame)); jvalue res2; res2.l = boxed->box(frame, res); return frame.keep(res2.l); } if (returnClass->findJavaConversion(returnMatch) == JPMatch::_none) { JP_TRACE("Cannot convert"); JP_RAISE(PyExc_TypeError, "Return value is not compatible with required type."); } JP_TRACE("Convert return to", returnClass->getCanonicalName()); jvalue res = returnMatch.convert(); return frame.keep(res.l); } catch (JPypeException& ex) { JP_TRACE("JPypeException raised"); ex.toJava(); } catch (...) // GCOVR_EXCL_LINE { JP_TRACE("Other Exception raised"); env->functions->ThrowNew(env, JPContext_global->m_RuntimeException.get(), "unknown error occurred"); } return nullptr; JP_TRACE_OUT; // GCOVR_EXCL_LINE } catch (...) // JP_TRACE_OUT implies a throw but that is not allowed. {} return NULL; } JPProxy::JPProxy(PyJPProxy* inst, JPClassList& intf) : m_Instance(inst), m_InterfaceClasses(intf) { JP_TRACE_IN("JPProxy::JPProxy"); JPJavaFrame frame = JPJavaFrame::outer(); // Convert the interfaces to a Class[] jobjectArray ar = frame.NewObjectArray((int) intf.size(), JPContext_global->_java_lang_Class->getJavaClass(), nullptr); for (unsigned int i = 0; i < intf.size(); i++) { frame.SetObjectArrayElement(ar, i, intf[i]->getJavaClass()); } jvalue v[4]; v[0].l = JPContext_global->getJavaContext(); v[1].j = (jlong) this; v[2].j = (jlong) & JPProxy::releaseProxyPython; v[3].l = ar; // Create the proxy jobject proxy = frame.CallStaticObjectMethodA(JPContext_global->m_ProxyClass.get(), JPContext_global->m_Proxy_NewID, v); m_Proxy = JPObjectRef(proxy); m_Ref = nullptr; JP_TRACE_OUT; } JPProxy::~JPProxy() { try { if (m_Ref != nullptr && JPContext_global->isRunning()) { JPContext_global->getEnv()->DeleteWeakGlobalRef(m_Ref); } } catch (JPypeException &ex) // GCOVR_EXCL_LINE { // Cannot throw } } void JPProxy::releaseProxyPython(void* host) { Py_XDECREF(((JPProxy*) host)->m_Instance); } jvalue JPProxy::getProxy() { JP_TRACE_IN("JPProxy::getProxy"); JPJavaFrame frame = JPJavaFrame::inner(); jobject instance = nullptr; if (m_Ref != nullptr) { instance = frame.NewLocalRef(m_Ref); } if (instance == nullptr) { // Use the proxy to make an instance JP_TRACE("Create handler"); Py_INCREF(m_Instance); instance = frame.CallObjectMethodA(m_Proxy.get(), JPContext_global->m_Proxy_NewInstanceID, nullptr); m_Ref = frame.NewWeakGlobalRef(instance); } jvalue out; out.l = frame.keep(instance); return out; JP_TRACE_OUT; } JPProxyType::JPProxyType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers) : JPClass(frame, clss, name, super, interfaces, modifiers) { jclass proxyClass = frame.FindClass("java/lang/reflect/Proxy"); m_ProxyClass = JPClassRef(frame, proxyClass); m_GetInvocationHandlerID = frame.GetStaticMethodID(proxyClass, "getInvocationHandler", "(Ljava/lang/Object;)Ljava/lang/reflect/InvocationHandler;"); m_InstanceID = frame.GetFieldID(clss, "instance", "J"); } JPProxyType::~JPProxyType() = default; JPPyObject JPProxyType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { JP_TRACE_IN("JPProxyType::convertToPythonObject"); jobject ih = frame.CallStaticObjectMethodA(m_ProxyClass.get(), m_GetInvocationHandlerID, &val); JPProxy *proxy = (JPProxy*) frame.GetLongField(ih, m_InstanceID); PyJPProxy *pproxy = proxy->m_Instance; // Is it a native Python object if (pproxy->m_Convert && pproxy->m_Target != Py_None) return JPPyObject::use(pproxy->m_Target); // Is it a user extended class if (pproxy->m_Dispatch == Py_None) return JPPyObject::use((PyObject*) pproxy); // Return the Proxy itself JP_TRACE("Target", pproxy); return JPPyObject::use((PyObject*) pproxy); JP_TRACE_OUT; // GCOVR_EXCL_LINE } JPProxyDirect::JPProxyDirect(PyJPProxy* inst, JPClassList& intf) : JPProxy(inst, intf) { } JPProxyDirect::~JPProxyDirect() = default; JPPyObject JPProxyDirect::getCallable(const string& cname, int& addSelf) { return JPPyObject::accept(PyObject_GetAttrString((PyObject*) m_Instance, cname.c_str())); } JPProxyIndirect::JPProxyIndirect(PyJPProxy* inst, JPClassList& intf) : JPProxy(inst, intf) { } JPProxyIndirect::~JPProxyIndirect() = default; JPPyObject JPProxyIndirect::getCallable(const string& cname, int& addSelf) { JPPyObject out = JPPyObject::accept(PyObject_GetAttrString(m_Instance->m_Dispatch, cname.c_str())); if (!out.isNull()) { addSelf = (m_Instance->m_Dispatch != m_Instance->m_Target) && (m_Instance->m_Target != Py_None); return out; } return JPPyObject::accept(PyObject_GetAttrString((PyObject*) m_Instance, cname.c_str())); } JPProxyFunctional::JPProxyFunctional(PyJPProxy* inst, JPClassList& intf) : JPProxy(inst, intf) { m_Functional = dynamic_cast( intf[0]); } JPProxyFunctional::~JPProxyFunctional() = default; JPPyObject JPProxyFunctional::getCallable(const string& cname, int& addSelf) { if (cname == m_Functional->getMethod()) return JPPyObject::accept(PyObject_GetAttrString(m_Instance->m_Dispatch, "__call__")); return JPPyObject::accept(PyObject_GetAttrString((PyObject*) m_Instance, cname.c_str())); } jpype-1.6.0/native/common/jp_reference_queue.cpp000066400000000000000000000060501501674766700220060ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include "jpype.h" #include "jp_classloader.h" #include "jp_reference_queue.h" #include "jp_gc.h" #include "pyjp.h" static jobject s_ReferenceQueue = nullptr; static jmethodID s_ReferenceQueueRegisterMethod = nullptr; extern "C" { static void releasePython(void* host) { Py_XDECREF((PyObject*) host); } /* * Class: org_jpype_ref_JPypeReferenceQueue * Method: init * Signature: (Ljava/lang/Object;Ljava/lang/reflect/Method;)V */ JNIEXPORT void JNICALL Java_org_jpype_ref_JPypeReferenceNative_init (JNIEnv *env, jclass clazz, jobject refqueue, jobject registerID) { s_ReferenceQueue = env->NewGlobalRef(refqueue); s_ReferenceQueueRegisterMethod = env->FromReflectedMethod(registerID); } JNIEXPORT void JNICALL Java_org_jpype_ref_JPypeReferenceNative_removeHostReference (JNIEnv *env, jclass, jlong host, jlong cleanup) { // Exceptions are not allowed here try { JPJavaFrame frame = JPJavaFrame::external(env); JPPyCallAcquire callback; if (cleanup != 0) { auto func = (JCleanupHook) cleanup; (*func)((void*) host); } } catch (...) // GCOVR_EXCL_LINE { } } /** Triggered whenever the sentinel is deleted */ JNIEXPORT void JNICALL Java_org_jpype_ref_JPypeReferenceNative_wake (JNIEnv *env, jclass clazz) { // Exceptions are not allowed here try { JPContext* context = JPContext_global; context->m_GC->triggered(); } catch (...) // GCOVR_EXCL_LINE { } } } void JPReferenceQueue::registerRef(JPJavaFrame &frame, jobject obj, PyObject* hostRef) { // There are certain calls such as exception handling in which the // Python object is null. In those cases, we don't need to bind the Java // object lifespan and can just ignore it. if (hostRef == nullptr) return; // MATCH TO DECREF IN releasePython Py_INCREF(hostRef); registerRef(frame, obj, hostRef, &releasePython); } void JPReferenceQueue::registerRef(JPJavaFrame &frame, jobject obj, void* host, JCleanupHook func) { JP_TRACE_IN("JPReferenceQueue::registerRef"); // create the ref ... jvalue args[3]; args[0].l = obj; args[1].j = (jlong) host; args[2].j = (jlong) func; if (s_ReferenceQueue == nullptr) JP_RAISE(PyExc_SystemError, "Memory queue not installed"); JP_TRACE("Register reference"); frame.CallVoidMethodA(s_ReferenceQueue, s_ReferenceQueueRegisterMethod, args); JP_TRACE_OUT; // GCOVR_EXCL_LINE } jpype-1.6.0/native/common/jp_shorttype.cpp000066400000000000000000000220631501674766700207070ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_primitive_accessor.h" #include "jp_shorttype.h" JPShortType::JPShortType() : JPPrimitiveType("short") { } JPShortType::~JPShortType() = default; JPClass* JPShortType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Short; } JPPyObject JPShortType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { JPPyObject tmp = JPPyObject::call(PyLong_FromLong(field(val))); JPPyObject out = JPPyObject::call(convertLong(getHost(), (PyLongObject*) tmp.get())); PyJPValue_assignJavaSlot(frame, out.get(), JPValue(this, val)); return out; } JPValue JPShortType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { jvalue v; jobject jo = obj.getValue().l; auto* jb = dynamic_cast( frame.findClassForObject(jo)); field(v) = (type_t) frame.CallIntMethodA(jo, jb->m_IntValueID, nullptr); return JPValue(this, v); } JPConversionLong shortConversion; JPConversionLongNumber shortNumberConversion; JPConversionLongWiden shortWidenConversion; class JPConversionJShort : public JPConversionJavaValue { public: JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue* value = match.getJavaSlot(); if (value == nullptr) return JPMatch::_none; match.type = JPMatch::_none; // Implied conversion from boxed to primitive (JLS 5.1.8) if (javaValueConversion->matches(cls, match) || unboxConversion->matches(cls, match)) return match.type; // Consider widening JPClass *cls2 = value->getClass(); if (cls2->isPrimitive()) { // https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2 auto *prim = dynamic_cast( cls2); switch (prim->getTypeCode()) { case 'C': case 'B': match.conversion = &shortWidenConversion; return match.type = JPMatch::_implicit; default: break; } } // Unboxing must be to the from the exact boxed type (JLS 5.1.8) return JPMatch::_implicit; //short cut further checks } void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = JPContext_global; PyList_Append(info.exact, (PyObject*) context->_short->getHost()); PyList_Append(info.implicit, (PyObject*) context->_byte->getHost()); PyList_Append(info.implicit, (PyObject*) context->_char->getHost()); unboxConversion->getInfo(cls, info); } } jshortConversion; JPMatch::Type JPShortType::findJavaConversion(JPMatch &match) { JP_TRACE_IN("JPShortType::findJavaConversion"); if (match.object == Py_None) return match.type = JPMatch::_none; if (jshortConversion.matches(this, match) || shortConversion.matches(this, match) || shortNumberConversion.matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; } void JPShortType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); jshortConversion.getInfo(this, info); shortConversion.getInfo(this, info); shortNumberConversion.getInfo(this, info); PyList_Append(info.ret, (PyObject*) JPContext_global->_short->getHost()); } jarray JPShortType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewShortArray(sz); } JPPyObject JPShortType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { jvalue v; field(v) = frame.GetStaticShortField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPShortType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { jvalue v; field(v) = frame.GetShortField(c, fid); return convertToPythonObject(frame, v, false); } JPPyObject JPShortType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; field(v) = frame.CallStaticShortMethodA(claz, mth, val); } return convertToPythonObject(frame, v, false); } JPPyObject JPShortType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { jvalue v; { JPPyCallRelease call; if (clazz == nullptr) field(v) = frame.CallShortMethodA(obj, mth, val); else field(v) = frame.CallNonvirtualShortMethodA(obj, clazz, mth, val); } return convertToPythonObject(frame, v, false); } void JPShortType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java short"); type_t val = field(match.convert()); frame.SetStaticShortField(c, fid, val); } void JPShortType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java short"); type_t val = field(match.convert()); frame.SetShortField(c, fid, val); } void JPShortType::setArrayRange(JPJavaFrame& frame, jarray a, jsize start, jsize length, jsize step, PyObject* sequence) { JP_TRACE_IN("JPShortType::setArrayRange"); JPPrimitiveArrayAccessor accessor(frame, a, &JPJavaFrame::GetShortArrayElements, &JPJavaFrame::ReleaseShortArrayElements); type_t* val = accessor.get(); // First check if assigning sequence supports buffer API if (PyObject_CheckBuffer(sequence)) { JPPyBuffer buffer(sequence, PyBUF_FULL_RO); if (buffer.valid()) { Py_buffer& view = buffer.getView(); if (view.ndim != 1) JP_RAISE(PyExc_TypeError, "buffer dims incorrect"); Py_ssize_t vshape = view.shape[0]; Py_ssize_t vstep = view.strides[0]; if (vshape != length) JP_RAISE(PyExc_ValueError, "mismatched size"); char* memory = (char*) view.buf; if (view.suboffsets && view.suboffsets[0] >= 0) memory = *((char**) memory) + view.suboffsets[0]; jsize index = start; jconverter conv = getConverter(view.format, (int) view.itemsize, "s"); for (Py_ssize_t i = 0; i < length; ++i, index += step) { jvalue r = conv(memory); val[index] = r.s; memory += vstep; } accessor.commit(); return; } else { PyErr_Clear(); } } // Use sequence API JPPySequence seq = JPPySequence::use(sequence); jsize index = start; for (Py_ssize_t i = 0; i < length; ++i, index += step) { PyObject *item = seq[i].get(); if (!PyIndex_Check(item)) { PyErr_Format(PyExc_TypeError, "Unable to implicitly convert '%s' to short", Py_TYPE(item)->tp_name); JP_RAISE_PYTHON(); } jlong v = PyLong_AsLongLong(item); if (v == -1) JP_PY_CHECK(); val[index] = (type_t) assertRange(v); } accessor.commit(); JP_TRACE_OUT; } JPPyObject JPShortType::getArrayItem(JPJavaFrame& frame, jarray a, jsize ndx) { auto array = (array_t) a; type_t val; frame.GetShortArrayRegion(array, ndx, 1, &val); jvalue v; field(v) = val; return convertToPythonObject(frame, v, false); } void JPShortType::setArrayItem(JPJavaFrame& frame, jarray a, jsize ndx, PyObject* obj) { JPMatch match(&frame, obj); if (findJavaConversion(match) < JPMatch::_implicit) JP_RAISE(PyExc_TypeError, "Unable to convert to Java short"); type_t val = field(match.convert()); frame.SetShortArrayRegion((array_t) a, ndx, 1, &val); } void JPShortType::getView(JPArrayView& view) { JPJavaFrame frame = JPJavaFrame::outer(); view.m_Memory = (void*) frame.GetShortArrayElements( (jshortArray) view.m_Array->getJava(), &view.m_IsCopy); view.m_Buffer.format = "h"; view.m_Buffer.itemsize = sizeof (jshort); } void JPShortType::releaseView(JPArrayView& view) { try { JPJavaFrame frame = JPJavaFrame::outer(); frame.ReleaseShortArrayElements((jshortArray) view.m_Array->getJava(), (jshort*) view.m_Memory, view.m_Buffer.readonly ? JNI_ABORT : 0); } catch (JPypeException&) { // This is called as part of the cleanup routine and exceptions // are not permitted } } const char* JPShortType::getBufferFormat() { return "h"; } Py_ssize_t JPShortType::getItemSize() { return sizeof (jshort); } void JPShortType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { auto* b = (jshort*) ((char*) memory + offset); frame.GetShortArrayRegion((jshortArray) a, start, len, b); } static void pack(jshort* d, jvalue v) { *d = v.s; } PyObject *JPShortType::newMultiArray(JPJavaFrame &frame, JPPyBuffer &buffer, int subs, int base, jobject dims) { JP_TRACE_IN("JPShortType::newMultiArray"); return convertMultiArray( frame, this, &pack, "s", buffer, subs, base, dims); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_stringtype.cpp000066400000000000000000000055071501674766700210620ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_stringtype.h" JPStringType::JPStringType(JPJavaFrame& frame, jclass clss, const string& name, JPClass* super, JPClassList& interfaces, jint modifiers) : JPClass(frame, clss, name, super, interfaces, modifiers) { } JPStringType::~JPStringType() = default; JPPyObject JPStringType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { JP_TRACE_IN("JPStringType::asHostObject"); JPContext *context = frame.getContext(); if (!cast) { // This loses type if (val.l == nullptr) { return JPPyObject::getNone(); } if (context->getConvertStrings()) { string str = frame.toStringUTF8((jstring) (val.l)); return JPPyObject::call(PyUnicode_FromStringAndSize(str.c_str(), str.length())); } } return JPClass::convertToPythonObject(frame, val, cast); JP_TRACE_OUT; // GCOV_EXCL_LINE } JPMatch::Type JPStringType::findJavaConversion(JPMatch& match) { JP_TRACE_IN("JPStringType::findJavaConversion"); if (nullConversion->matches(this, match) || objectConversion->matches(this, match) || stringConversion->matches(this, match) || hintsConversion->matches(this, match) ) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; // GCOV_EXCL_LINE } void JPStringType::getConversionInfo(JPConversionInfo &info) { JPJavaFrame frame = JPJavaFrame::outer(); JPContext* context = frame.getContext(); objectConversion->getInfo(this, info); stringConversion->getInfo(this, info); hintsConversion->getInfo(this, info); if (context->getConvertStrings()) PyList_Append(info.ret, (PyObject*) & PyUnicode_Type); // GCOVR_EXCL_LINE else PyList_Append(info.ret, (PyObject*) getHost()); } JPValue JPStringType::newInstance(JPJavaFrame& frame, JPPyObjectVector& args) { JP_TRACE_IN("JPStringType::newInstance"); if (args.size() == 1 && JPPyString::check(args[0])) { // JNI has a short cut for constructing java.lang.String JP_TRACE("Direct"); string str = JPPyString::asStringUTF8(args[0]); return JPValue(this, frame.fromStringUTF8(str)); } return JPClass::newInstance(frame, args); JP_TRACE_OUT; // GCOV_EXCL_LINE } jpype-1.6.0/native/common/jp_tracer.cpp000066400000000000000000000114011501674766700201200ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include "jpype.h" #include "jp_tracer.h" // GCOVR_EXCL_START #if defined(_MSC_VER) && _MSC_VER<1700 // Welcome to the year 2008! namespace std { class mutex { } ; template class lock_guard { public: lock_guard(const T& mutex_type) { } } ; } #else #include #endif static int jpype_traceLevel = 0; static JPypeTracer* jpype_tracer_last = nullptr; std::mutex trace_lock; #define JPYPE_TRACING_OUTPUT std::cerr static int INDENT_WIDTH = 2; static const char *INDENT = " "; static void jpype_indent(int space) { space *= INDENT_WIDTH; while (space > 80) { JPYPE_TRACING_OUTPUT << INDENT; space -= 80; } JPYPE_TRACING_OUTPUT << &INDENT[80 - space]; } //This code is not thread safe, thus tracing a multithreaded code is likely // to result in crashes. JPypeTracer::JPypeTracer(const char* name, void* reference) : m_Name(name) { m_Error = false; m_Last = jpype_tracer_last; jpype_tracer_last = this; traceIn(name, reference); } JPypeTracer::~JPypeTracer() { traceOut(m_Name.c_str(), m_Error); jpype_tracer_last = m_Last; } void JPypeTracer::traceIn(const char* msg, void* ref) { if (_PyJPModule_trace == 0) return; if (jpype_traceLevel < 0) jpype_traceLevel = 0; std::lock_guard guard(trace_lock); jpype_indent(jpype_traceLevel); JPYPE_TRACING_OUTPUT << "> " << msg ; if (ref != nullptr) JPYPE_TRACING_OUTPUT << " id=\"" << ref << "\""; JPYPE_TRACING_OUTPUT << std::endl; JPYPE_TRACING_OUTPUT.flush(); jpype_traceLevel++; } void JPypeTracer::traceOut(const char* msg, bool error) { if (_PyJPModule_trace == 0) return; std::lock_guard guard(trace_lock); jpype_traceLevel--; jpype_indent(jpype_traceLevel); if (error) { JPYPE_TRACING_OUTPUT << "EXCEPTION! " << msg << std::endl; } else { JPYPE_TRACING_OUTPUT << "< " << msg << std::endl; } JPYPE_TRACING_OUTPUT.flush(); } void JPypeTracer::traceJavaObject(const char* msg, const void* ref) { if ((_PyJPModule_trace & 4) == 0) return; if (ref == (void*) nullptr) { JPypeTracer::trace1("JNI", msg); return; } if (ref == (void*) - 1) { JPypeTracer::trace1("+ JNI", msg); jpype_traceLevel++; return; } if (ref == (void*) - 2) { jpype_traceLevel--; JPypeTracer::trace1("- JNI", msg); return; } std::stringstream str; str << msg << " " << (void*) ref ; JPypeTracer::trace1("JNI", str.str().c_str()); } void JPypeTracer::tracePythonObject(const char* msg, PyObject* ref) { if ((_PyJPModule_trace & 2) == 0) return; if (ref != nullptr) { std::stringstream str; str << msg << " " << (void*) ref << " "<< #ifndef Py_GIL_DISABLED Py_REFCNT(ref) #else -1 #endif << " " << Py_TYPE(ref)->tp_name; JPypeTracer::trace1("PY", str.str().c_str()); } else { std::stringstream str; str << msg << " " << (void*) ref; JPypeTracer::trace1("PY", str.str().c_str()); } } void JPypeTracer::trace1(const char* source, const char* msg) { if (_PyJPModule_trace == 0) return; std::lock_guard guard(trace_lock); string name = "unknown"; if (jpype_tracer_last != nullptr) name = jpype_tracer_last->m_Name; jpype_indent(jpype_traceLevel); if (source != nullptr) JPYPE_TRACING_OUTPUT << source << ": "; if (source == nullptr || (_PyJPModule_trace & 16) != 0) JPYPE_TRACING_OUTPUT << name << ": "; JPYPE_TRACING_OUTPUT << msg << std::endl; JPYPE_TRACING_OUTPUT.flush(); } void JPypeTracer::trace2(const char* msg1, const char* msg2) { if (_PyJPModule_trace == 0) return; std::lock_guard guard(trace_lock); string name = "unknown"; if (jpype_tracer_last != nullptr) name = jpype_tracer_last->m_Name; jpype_indent(jpype_traceLevel); JPYPE_TRACING_OUTPUT << name << ": " << msg1 << " " << msg2 << std::endl; JPYPE_TRACING_OUTPUT.flush(); } void JPypeTracer::traceLocks(const string& msg, void* ref) { std::lock_guard guard(trace_lock); JPYPE_TRACING_OUTPUT << msg << ": " << ref << std::endl; JPYPE_TRACING_OUTPUT.flush(); } // GCOVR_EXCL_STOP jpype-1.6.0/native/common/jp_typefactory.cpp000066400000000000000000000323761501674766700212270ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_primitive_accessor.h" #include "jp_classloader.h" #include "jp_boxedtype.h" #include "jp_field.h" #include "jp_objecttype.h" #include "jp_numbertype.h" #include "jp_classtype.h" #include "jp_method.h" #include "jp_methoddispatch.h" #include "jp_buffertype.h" #include "jp_typemanager.h" #include "jp_arrayclass.h" #include "jp_stringtype.h" #include "jp_voidtype.h" #include "jp_booleantype.h" #include "jp_bytetype.h" #include "jp_chartype.h" #include "jp_shorttype.h" #include "jp_inttype.h" #include "jp_longtype.h" #include "jp_floattype.h" #include "jp_doubletype.h" #include "jp_functional.h" #include "jp_proxy.h" void JPTypeFactory_rethrow(JPJavaFrame& frame) { try { throw; } catch (JPypeException& ex) { ex.toJava(); } catch (...) // GCOVR_EXCL_LINE { // GCOVR_EXCL_START frame.ThrowNew(frame.getContext()->m_RuntimeException.get(), "unknown error occurred"); // GCOVR_EXCL_STOP } } template void convert(JPJavaFrame& frame, jlongArray array, vector& out) { JPPrimitiveArrayAccessor accessor(frame, array, &JPJavaFrame::GetLongArrayElements, &JPJavaFrame::ReleaseLongArrayElements); jlong* values = accessor.get(); jsize sz = frame.GetArrayLength(array); out.resize(sz); for (int i = 0; i < sz; ++i) { out[i] = (T) values[i]; } } #ifdef JP_TRACING_ENABLE #define JP_JAVA_TRY(...) \ JPypeTracer _trace(__VA_ARGS__); \ try { #define JP_JAVA_CATCH(...) \ } \ catch(...) { \ _trace.gotError(JP_STACKINFO()); \ JPTypeFactory_rethrow(frame); } return __VA_ARGS__ #else #define JP_JAVA_TRY(...) try { #define JP_JAVA_CATCH(...) } catch(...) { JPTypeFactory_rethrow(frame); } return __VA_ARGS__ #endif extern "C" { JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_newWrapper( JNIEnv *env, jobject self, jlong contextPtr, jlong jcls) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_newWrapper"); JPPyCallAcquire callback; auto* cls = (JPClass*) jcls; PyJPClass_hook(frame, cls); JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_destroy( JNIEnv *env, jobject self, jlong contextPtr, jlongArray resources, jint sz) { JPJavaFrame frame = JPJavaFrame::external(env); auto* context = frame.getContext(); JP_JAVA_TRY("JPTypeFactory_destroy"); JPPrimitiveArrayAccessor accessor(frame, resources, &JPJavaFrame::GetLongArrayElements, &JPJavaFrame::ReleaseLongArrayElements); jlong* values = accessor.get(); for (int i = 0; i < sz; ++i) { context->m_Resources.push_back((JPResource*) values[i]); } return; JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineMethodDispatch( JNIEnv *env, jobject self, jlong contextPtr, jlong clsPtr, jstring name, jlongArray overloadPtrs, jint modifiers) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_defineMethodDispatch"); auto* cls = (JPClass*) clsPtr; JPMethodList overloadList; convert(frame, overloadPtrs, overloadList); string cname = frame.toStringUTF8(name); JP_TRACE(cname); auto* dispatch = new JPMethodDispatch(cls, cname, overloadList, modifiers); return (jlong) dispatch; JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineArrayClass( JNIEnv *env, jobject self, jlong contextPtr, jclass cls, jstring name, jlong superClass, jlong componentClass, jint modifiers) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_defineArrayClass"); string cname = frame.toStringUTF8(name); JP_TRACE(cname); auto* result = new JPArrayClass(frame, cls, cname, (JPClass*) superClass, (JPClass*) componentClass, modifiers); return (jlong) result; JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineObjectClass( JNIEnv *env, jobject self, jlong contextPtr, jclass cls, jstring name, jlong superClass, jlongArray interfacePtrs, jint modifiers) { // All resources are created here are owned by Java and deleted by Java shutdown routine JPJavaFrame frame = JPJavaFrame::external(env); auto* context = frame.getContext(); JP_JAVA_TRY("JPTypeFactory_defineObjectClass"); string className = frame.toStringUTF8(name); JP_TRACE(className); JPClassList interfaces; if (interfacePtrs != nullptr) convert(frame, interfacePtrs, interfaces); JPClass* result = nullptr; if (!JPModifier::isSpecial(modifiers)) { // Create a normal class return (jlong) new JPClass(frame, cls, className, (JPClass*) superClass, interfaces, modifiers); } if (JPModifier::isFunctional(modifiers)) return (jlong) new JPFunctional(frame, cls, className, (JPClass*) superClass, interfaces, modifiers); if (JPModifier::isBuffer(modifiers)) return (jlong) new JPBufferType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers); // Certain classes require special implementations if (className == "java.lang.Object") return (jlong) (context->_java_lang_Object = new JPObjectType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); if (className == "java.lang.Class") return (jlong) (context->_java_lang_Class = new JPClassType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); if (className == "java.lang.CharSequence") return (jlong) (new JPStringType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); if (className == "java.lang.String") return (jlong) (context->_java_lang_String = new JPStringType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); if (className == "java.lang.Throwable") return (jlong) (context->_java_lang_Throwable = new JPClassType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); if (className == "java.lang.Number") return (jlong) new JPNumberType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers); // Register the box types if (className == "java.lang.Void") { context->_void = new JPVoidType(); return (jlong) (context->_java_lang_Void = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_void)); } if (className == "java.lang.Boolean") { context->_boolean = new JPBooleanType(); return (jlong) (context->_java_lang_Boolean = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_boolean)); } if (className == "java.lang.Byte") { context->_byte = new JPByteType(); return (jlong) (context->_java_lang_Byte = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_byte)); } if (className == "java.lang.Character") { context->_char = new JPCharType(); return (jlong) (context->_java_lang_Character = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_char)); } if (className == "java.lang.Short") { context->_short = new JPShortType(); return (jlong) (context->_java_lang_Short = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_short)); } if (className == "java.lang.Integer") { context->_int = new JPIntType(); return (jlong) (context->_java_lang_Integer = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_int)); } if (className == "java.lang.Long") { context->_long = new JPLongType(); return (jlong) (context->_java_lang_Long = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_long)); } if (className == "java.lang.Float") { context->_float = new JPFloatType(); return (jlong) (context->_java_lang_Float = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_float)); } if (className == "java.lang.Double") { context->_double = new JPDoubleType(); return (jlong) (context->_java_lang_Double = new JPBoxedType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers, context->_double)); } if (className == "org.jpype.proxy.JPypeProxy") return (jlong) new JPProxyType(frame, cls, className, (JPClass*) superClass, interfaces, modifiers); // Register reflection types for later use if (className == "java.lang.reflect.Method") return (jlong) (context->_java_lang_reflect_Method = new JPClass(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); if (className == "java.lang.reflect.Field") return (jlong) (context->_java_lang_reflect_Field = new JPClass(frame, cls, className, (JPClass*) superClass, interfaces, modifiers)); std::stringstream ss; ss << "Special class not defined for " << className; JP_RAISE(PyExc_RuntimeError, ss.str()); return (jlong) result; JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_definePrimitive( JNIEnv *env, jobject self, jlong contextPtr, jstring name, jclass cls, jlong boxedPtr, jint modifiers) { // These resources are created by the boxed types JPJavaFrame frame = JPJavaFrame::external(env); auto* context = frame.getContext(); JP_JAVA_TRY("JPTypeFactory_definePrimitive"); string cname = frame.toStringUTF8(name); JP_TRACE(cname); if (cname == "void") { context->_void->setClass(frame, cls); return (jlong) (context->_void); } if (cname == "byte") { context->_byte->setClass(frame, cls); return (jlong) (context->_byte); } if (cname == "boolean") { context->_boolean->setClass(frame, cls); return (jlong) (context->_boolean); } if (cname == "char") { context->_char->setClass(frame, cls); return (jlong) (context->_char); } if (cname == "short") { context->_short->setClass(frame, cls); return (jlong) (context->_short); } if (cname == "int") { context->_int->setClass(frame, cls); return (jlong) (context->_int); } if (cname == "long") { context->_long->setClass(frame, cls); return (jlong) (context->_long); } if (cname == "float") { context->_float->setClass(frame, cls); return (jlong) (context->_float); } if (cname == "double") { context->_double->setClass(frame, cls); return (jlong) (context->_double); } return 0; JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_assignMembers( JNIEnv *env, jobject self, jlong contextPtr, jlong clsPtr, jlong ctorMethod, jlongArray methodPtrs, jlongArray fieldPtrs) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_assignMembers"); auto* cls = (JPClass*) clsPtr; JPMethodDispatchList methodList; convert(frame, methodPtrs, methodList); JPFieldList fieldList; convert(frame, fieldPtrs, fieldList); cls->assignMembers( (JPMethodDispatch*) ctorMethod, methodList, fieldList); return; JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineField( JNIEnv *env, jobject self, jlong contextPtr, jlong cls, jstring name, jobject field, jlong fieldType, jint modifiers) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_defineField"); string cname = frame.toStringUTF8(name); JP_TRACE("class", cls); JP_TRACE(cname); jfieldID fid = frame.FromReflectedField(field); return (jlong) (new JPField( frame, (JPClass*) cls, cname, field, fid, (JPClass*) fieldType, modifiers)); JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineMethod( JNIEnv *env, jobject self, jlong contextPtr, jlong cls, jstring name, jobject method, jlongArray overloadList, jint modifiers) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_defineMethod"); jmethodID mid = frame.FromReflectedMethod(method); JPMethodList cover; convert(frame, overloadList, cover); string cname = frame.toStringUTF8(name); JP_TRACE(cname); return (jlong) (new JPMethod( frame, (JPClass*) cls, cname, method, mid, cover, modifiers)); JP_JAVA_CATCH(0); } JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_populateMethod( JNIEnv *env, jobject self, jlong contextPtr, jlong method, jlong returnType, jlongArray argumentTypes ) { JPJavaFrame frame = JPJavaFrame::external(env); JP_JAVA_TRY("JPTypeFactory_populateMethod"); JPClassList cargs; convert(frame, argumentTypes, cargs); auto *methodPtr = (JPMethod*) method; methodPtr->setParameters((JPClass*) returnType, std::move(cargs)); JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } } // extern "C" jpype-1.6.0/native/common/jp_typemanager.cpp000066400000000000000000000071531501674766700211650ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "jp_classloader.h" JPTypeManager::JPTypeManager(JPJavaFrame& frame) { JP_TRACE_IN("JPTypeManager::init"); jclass cls = frame.getContext()->getClassLoader()->findClass(frame, "org.jpype.manager.TypeManager"); m_FindClass = frame.GetMethodID(cls, "findClass", "(Ljava/lang/Class;)J"); m_FindClassByName = frame.GetMethodID(cls, "findClassByName", "(Ljava/lang/String;)J"); m_FindClassForObject = frame.GetMethodID(cls, "findClassForObject", "(Ljava/lang/Object;)J"); m_PopulateMethod = frame.GetMethodID(cls, "populateMethod", "(JLjava/lang/reflect/Executable;)V"); m_PopulateMembers = frame.GetMethodID(cls, "populateMembers", "(Ljava/lang/Class;)V"); m_InterfaceParameterCount = frame.GetMethodID(cls, "interfaceParameterCount", "(Ljava/lang/Class;)I"); // The object instance will be loaded later JP_TRACE_OUT; } JPClass* JPTypeManager::findClass(jclass obj) { JP_TRACE_IN("JPTypeManager::findClass"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val; val.l = obj; return (JPClass*) (frame.CallLongMethodA(m_JavaTypeManager.get(), m_FindClass, &val)); JP_TRACE_OUT; } JPClass* JPTypeManager::findClassByName(const string& name) { JP_TRACE_IN("JPTypeManager::findClassByName"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val; val.l = (jobject) frame.fromStringUTF8(name); auto* out = (JPClass*) (frame.CallLongMethodA(m_JavaTypeManager.get(), m_FindClassByName, &val)); if (out == nullptr) { std::stringstream err; err << "Class " << name << " is not found"; JP_RAISE(PyExc_TypeError, err.str()); } return out; JP_TRACE_OUT; } JPClass* JPTypeManager::findClassForObject(jobject obj) { JP_TRACE_IN("JPTypeManager::findClassForObject"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val; val.l = obj; auto *cls = (JPClass*) (frame.CallLongMethodA(m_JavaTypeManager.get(), m_FindClassForObject, &val)); frame.check(); JP_TRACE("ClassName", cls == NULL ? "null" : cls->getCanonicalName()); return cls; JP_TRACE_OUT; } void JPTypeManager::populateMethod(void* method, jobject obj) { JP_TRACE_IN("JPTypeManager::populateMethod"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val[2]; val[0].j = (jlong) method; val[1].l = obj; JP_TRACE("Method", method); frame.CallVoidMethodA(m_JavaTypeManager.get(), m_PopulateMethod, val); JP_TRACE_OUT; } void JPTypeManager::populateMembers(JPClass* cls) { JP_TRACE_IN("JPTypeManager::populateMembers"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val[1]; val[0].l = (jobject) cls->getJavaClass(); frame.CallVoidMethodA(m_JavaTypeManager.get(), m_PopulateMembers, val); JP_TRACE_OUT; } int JPTypeManager::interfaceParameterCount(JPClass *cls) { JP_TRACE_IN("JPTypeManager::interfaceParameterCount"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val[1]; val[0].l = (jobject) cls->getJavaClass(); return frame.CallIntMethodA(m_JavaTypeManager.get(), m_InterfaceParameterCount, val); JP_TRACE_OUT; } jpype-1.6.0/native/common/jp_value.cpp000066400000000000000000000023621501674766700177620ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include jobject JPValue::getJavaObject() const { // This is all sanity check // GCOVR_EXCL_START if (m_Class == nullptr) JP_RAISE(PyExc_RuntimeError, "Null class"); if (!m_Class->isPrimitive()) // GCOVR_EXCL_STOP return m_Value.l; // This method is only used internally, thus it requires a logical code // error to trigger. We will use type error in case there is some // way a user can trigger it. JP_RAISE(PyExc_TypeError, "cannot access Java primitive value as Java object"); // GCOVR_EXCL_LINE } jpype-1.6.0/native/common/jp_voidtype.cpp000066400000000000000000000076001501674766700205110ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "jp_voidtype.h" JPVoidType::JPVoidType() : JPPrimitiveType("void") { } JPVoidType::~JPVoidType() = default; JPClass* JPVoidType::getBoxedClass(JPJavaFrame& frame) const { return frame.getContext()->_java_lang_Void; } JPPyObject JPVoidType::invokeStatic(JPJavaFrame& frame, jclass claz, jmethodID mth, jvalue* val) { { JPPyCallRelease call; frame.CallStaticVoidMethodA(claz, mth, val); } return JPPyObject::getNone(); } JPPyObject JPVoidType::invoke(JPJavaFrame& frame, jobject obj, jclass clazz, jmethodID mth, jvalue* val) { { JPPyCallRelease call; if (clazz == nullptr) frame.CallVoidMethodA(obj, mth, val); else frame.CallNonvirtualVoidMethodA(obj, clazz, mth, val); } return JPPyObject::getNone(); } // GCOVR_EXCL_START JPValue JPVoidType::getValueFromObject(JPJavaFrame& frame, const JPValue& obj) { // This is needed if we call a caller sensitive method // and we get a return which is expected to be a void object JP_TRACE_IN("JPVoidType::getValueFromObject"); return JPValue(this, (jobject) nullptr); JP_TRACE_OUT; } JPPyObject JPVoidType::getStaticField(JPJavaFrame& frame, jclass c, jfieldID fid) { JP_RAISE(PyExc_SystemError, "void cannot be the type of a static field."); } JPPyObject JPVoidType::getField(JPJavaFrame& frame, jobject c, jfieldID fid) { JP_RAISE(PyExc_SystemError, "void cannot be the type of a field."); } JPPyObject JPVoidType::convertToPythonObject(JPJavaFrame& frame, jvalue val, bool cast) { return JPPyObject::getNone(); } JPMatch::Type JPVoidType::findJavaConversion(JPMatch &match) { return match.type = JPMatch::_none; } void JPVoidType::setStaticField(JPJavaFrame& frame, jclass c, jfieldID fid, PyObject*) { JP_RAISE(PyExc_SystemError, "void cannot be the type of a static field."); } void JPVoidType::setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject*) { JP_RAISE(PyExc_SystemError, "void cannot be the type of a field."); } void JPVoidType::setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject* sequence) { JP_RAISE(PyExc_SystemError, "void cannot be the type of an array."); } JPPyObject JPVoidType::getArrayItem(JPJavaFrame& frame, jarray, jsize) { JP_RAISE(PyExc_SystemError, "void cannot be the type of an array."); } void JPVoidType::setArrayItem(JPJavaFrame& frame, jarray, jsize, PyObject*) { JP_RAISE(PyExc_SystemError, "void cannot be the type of an array."); } jarray JPVoidType::newArrayOf(JPJavaFrame& frame, jsize) { JP_RAISE(PyExc_SystemError, "void cannot be the type of an array."); } void JPVoidType::getView(JPArrayView& view) { } void JPVoidType::releaseView(JPArrayView& view) { } const char* JPVoidType::getBufferFormat() { return nullptr; } Py_ssize_t JPVoidType::getItemSize() { return 0; } void JPVoidType::copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) { } char JPVoidType::getTypeCode() { return 'V'; } jlong JPVoidType::getAsLong(jvalue v) { return 0; } jdouble JPVoidType::getAsDouble(jvalue v) { return 0; } PyObject *JPVoidType::newMultiArray(JPJavaFrame &frame, JPPyBuffer& view, int subs, int base, jobject dims) { return nullptr; } // GCOVR_EXCL_STOP jpype-1.6.0/native/jni_include/000077500000000000000000000000001501674766700164415ustar00rootroot00000000000000jpype-1.6.0/native/jni_include/jni.h000066400000000000000000001503221501674766700173750ustar00rootroot00000000000000/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * JNI specification, as defined by Sun: * http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html * * Everything here is expected to be VM-neutral. */ #ifndef JNI_H_ #define JNI_H_ #include #ifdef _MSC_VER // broken m$ compiler does not have c99 std header #if _MSC_VER >= 1600 #include #else typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; #endif #else #include #endif #include /* Primitive types that match up with Java equivalents. */ typedef uint8_t jboolean; /* unsigned 8 bits */ typedef int8_t jbyte; /* signed 8 bits */ typedef uint16_t jchar; /* unsigned 16 bits */ typedef int16_t jshort; /* signed 16 bits */ #if defined(_WIN32) typedef long jint; #else typedef int32_t jint; /* signed 32 bits */ #endif // define jlong #ifdef _LP64 /* 64-bit build */ typedef int64_t jlong; /* signed 64 bits */ // typedef long jlong; #else typedef long long jlong; #endif typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ /* "cardinal indices and sizes" */ typedef jint jsize; #ifndef __has_attribute #define __has_attribute(x) 0 #endif #if defined(_WIN32) #define JNIEXPORT __declspec(dllexport) #define JNIIMPORT __declspec(dllimport) #define JNICALL __stdcall #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) #define JNIEXPORT __attribute__((visibility("default"))) #define JNIIMPORT __attribute__((visibility("default"))) #define JNICALL #else #define JNICALL #define JNIEXPORT #define JNIIMPORT #endif #ifdef __cplusplus /* * Reference types, in C++ */ class _jobject {}; class _jclass : public _jobject {}; class _jstring : public _jobject {}; class _jarray : public _jobject {}; class _jobjectArray : public _jarray {}; class _jbooleanArray : public _jarray {}; class _jbyteArray : public _jarray {}; class _jcharArray : public _jarray {}; class _jshortArray : public _jarray {}; class _jintArray : public _jarray {}; class _jlongArray : public _jarray {}; class _jfloatArray : public _jarray {}; class _jdoubleArray : public _jarray {}; class _jthrowable : public _jobject {}; typedef _jobject* jobject; typedef _jclass* jclass; typedef _jstring* jstring; typedef _jarray* jarray; typedef _jobjectArray* jobjectArray; typedef _jbooleanArray* jbooleanArray; typedef _jbyteArray* jbyteArray; typedef _jcharArray* jcharArray; typedef _jshortArray* jshortArray; typedef _jintArray* jintArray; typedef _jlongArray* jlongArray; typedef _jfloatArray* jfloatArray; typedef _jdoubleArray* jdoubleArray; typedef _jthrowable* jthrowable; typedef _jobject* jweak; #else /* not __cplusplus */ /* * Reference types, in C. */ typedef void* jobject; typedef jobject jclass; typedef jobject jstring; typedef jobject jarray; typedef jarray jobjectArray; typedef jarray jbooleanArray; typedef jarray jbyteArray; typedef jarray jcharArray; typedef jarray jshortArray; typedef jarray jintArray; typedef jarray jlongArray; typedef jarray jfloatArray; typedef jarray jdoubleArray; typedef jobject jthrowable; typedef jobject jweak; #endif /* not __cplusplus */ struct _jfieldID; /* opaque structure */ typedef struct _jfieldID* jfieldID; /* field IDs */ struct _jmethodID; /* opaque structure */ typedef struct _jmethodID* jmethodID; /* method IDs */ struct JNIInvokeInterface; typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue; typedef enum jobjectRefType { JNIInvalidRefType = 0, JNILocalRefType = 1, JNIGlobalRefType = 2, JNIWeakGlobalRefType = 3 } jobjectRefType; typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod; struct _JNIEnv; struct _JavaVM; typedef const struct JNINativeInterface* C_JNIEnv; #if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif /* * Table of interface function pointers. */ struct JNINativeInterface { void* reserved0; void* reserved1; void* reserved2; void* reserved3; jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*); jmethodID (*FromReflectedMethod)(JNIEnv*, jobject); jfieldID (*FromReflectedField)(JNIEnv*, jobject); /* spec doesn't show jboolean parameter */ jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean); jclass (*GetSuperclass)(JNIEnv*, jclass); jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass); /* spec doesn't show jboolean parameter */ jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean); jint (*Throw)(JNIEnv*, jthrowable); jint (*ThrowNew)(JNIEnv *, jclass, const char *); jthrowable (*ExceptionOccurred)(JNIEnv*); void (*ExceptionDescribe)(JNIEnv*); void (*ExceptionClear)(JNIEnv*); void (*FatalError)(JNIEnv*, const char*); jint (*PushLocalFrame)(JNIEnv*, jint); jobject (*PopLocalFrame)(JNIEnv*, jobject); jobject (*NewGlobalRef)(JNIEnv*, jobject); void (*DeleteGlobalRef)(JNIEnv*, jobject); void (*DeleteLocalRef)(JNIEnv*, jobject); jboolean (*IsSameObject)(JNIEnv*, jobject, jobject); jobject (*NewLocalRef)(JNIEnv*, jobject); jint (*EnsureLocalCapacity)(JNIEnv*, jint); jobject (*AllocObject)(JNIEnv*, jclass); jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...); jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list); jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, jvalue*); jclass (*GetObjectClass)(JNIEnv*, jobject); jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass); jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list); jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...); jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list); jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...); jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list); jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...); jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list); jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...); jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list); jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list); jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...); jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list); jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...); jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list); jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...); jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list); jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list); void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*); jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID); jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID); jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID); jchar (*GetCharField)(JNIEnv*, jobject, jfieldID); jshort (*GetShortField)(JNIEnv*, jobject, jfieldID); jint (*GetIntField)(JNIEnv*, jobject, jfieldID); jlong (*GetLongField)(JNIEnv*, jobject, jfieldID); jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID); jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID); void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject); void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean); void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte); void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar); void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort); void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint); void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong); void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat); void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble); jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...); jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list); jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...); jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID, va_list); jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...); jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list); jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...); jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list); jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...); jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list); jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...); jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list); jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...); jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list); jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...); jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list); jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...); jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list); jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list); void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*, const char*); jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID); jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID); jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID); jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID); jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID); jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID); jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID); jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID); jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID); void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject); void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean); void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte); void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar); void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort); void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint); void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong); void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat); void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble); jstring (*NewString)(JNIEnv*, const jchar*, jsize); jsize (*GetStringLength)(JNIEnv*, jstring); const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*); void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*); jstring (*NewStringUTF)(JNIEnv*, const char*); jsize (*GetStringUTFLength)(JNIEnv*, jstring); /* JNI spec says this returns const jbyte*, but that's inconsistent */ const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*); void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*); jsize (*GetArrayLength)(JNIEnv*, jarray); jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject); jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize); void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject); jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize); jbyteArray (*NewByteArray)(JNIEnv*, jsize); jcharArray (*NewCharArray)(JNIEnv*, jsize); jshortArray (*NewShortArray)(JNIEnv*, jsize); jintArray (*NewIntArray)(JNIEnv*, jsize); jlongArray (*NewLongArray)(JNIEnv*, jsize); jfloatArray (*NewFloatArray)(JNIEnv*, jsize); jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize); jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*); jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*); jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*); jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*); jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*); jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*); jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*); void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*, jint); void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint); void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray, jchar*, jint); void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray, jshort*, jint); void (*ReleaseIntArrayElements)(JNIEnv*, jintArray, jint*, jint); void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray, jlong*, jint); void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray, jfloat*, jint); void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray, jdouble*, jint); void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*); void (*GetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, jbyte*); void (*GetCharArrayRegion)(JNIEnv*, jcharArray, jsize, jsize, jchar*); void (*GetShortArrayRegion)(JNIEnv*, jshortArray, jsize, jsize, jshort*); void (*GetIntArrayRegion)(JNIEnv*, jintArray, jsize, jsize, jint*); void (*GetLongArrayRegion)(JNIEnv*, jlongArray, jsize, jsize, jlong*); void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray, jsize, jsize, jfloat*); void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*); /* spec shows these without const; some jni.h do, some don't */ void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray, jsize, jsize, const jboolean*); void (*SetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*); void (*SetCharArrayRegion)(JNIEnv*, jcharArray, jsize, jsize, const jchar*); void (*SetShortArrayRegion)(JNIEnv*, jshortArray, jsize, jsize, const jshort*); void (*SetIntArrayRegion)(JNIEnv*, jintArray, jsize, jsize, const jint*); void (*SetLongArrayRegion)(JNIEnv*, jlongArray, jsize, jsize, const jlong*); void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray, jsize, jsize, const jfloat*); void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray, jsize, jsize, const jdouble*); jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint); jint (*UnregisterNatives)(JNIEnv*, jclass); jint (*MonitorEnter)(JNIEnv*, jobject); jint (*MonitorExit)(JNIEnv*, jobject); jint (*GetJavaVM)(JNIEnv*, JavaVM**); void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*); void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*); void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*); void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint); const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*); void (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*); jweak (*NewWeakGlobalRef)(JNIEnv*, jobject); void (*DeleteWeakGlobalRef)(JNIEnv*, jweak); jboolean (*ExceptionCheck)(JNIEnv*); jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong); void* (*GetDirectBufferAddress)(JNIEnv*, jobject); jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject); /* added in JNI 1.6 */ jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject); }; /* * C++ object wrapper. * * This is usually overlaid on a C struct whose first element is a * JNINativeInterface*. We rely somewhat on compiler behavior. */ struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); } jclass FindClass(const char* name) { return functions->FindClass(this, name); } jmethodID FromReflectedMethod(jobject method) { return functions->FromReflectedMethod(this, method); } jfieldID FromReflectedField(jobject field) { return functions->FromReflectedField(this, field); } jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { return functions->ToReflectedMethod(this, cls, methodID, isStatic); } jclass GetSuperclass(jclass clazz) { return functions->GetSuperclass(this, clazz); } jboolean IsAssignableFrom(jclass clazz1, jclass clazz2) { return functions->IsAssignableFrom(this, clazz1, clazz2); } jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { return functions->ToReflectedField(this, cls, fieldID, isStatic); } jint Throw(jthrowable obj) { return functions->Throw(this, obj); } jint ThrowNew(jclass clazz, const char* message) { return functions->ThrowNew(this, clazz, message); } jthrowable ExceptionOccurred() { return functions->ExceptionOccurred(this); } void ExceptionDescribe() { functions->ExceptionDescribe(this); } void ExceptionClear() { functions->ExceptionClear(this); } void FatalError(const char* msg) { functions->FatalError(this, msg); } jint PushLocalFrame(jint capacity) { return functions->PushLocalFrame(this, capacity); } jobject PopLocalFrame(jobject result) { return functions->PopLocalFrame(this, result); } jobject NewGlobalRef(jobject obj) { return functions->NewGlobalRef(this, obj); } void DeleteGlobalRef(jobject globalRef) { functions->DeleteGlobalRef(this, globalRef); } void DeleteLocalRef(jobject localRef) { functions->DeleteLocalRef(this, localRef); } jboolean IsSameObject(jobject ref1, jobject ref2) { return functions->IsSameObject(this, ref1, ref2); } jobject NewLocalRef(jobject ref) { return functions->NewLocalRef(this, ref); } jint EnsureLocalCapacity(jint capacity) { return functions->EnsureLocalCapacity(this, capacity); } jobject AllocObject(jclass clazz) { return functions->AllocObject(this, clazz); } jobject NewObject(jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); jobject result = functions->NewObjectV(this, clazz, methodID, args); va_end(args); return result; } jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args) { return functions->NewObjectV(this, clazz, methodID, args); } jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args) { return functions->NewObjectA(this, clazz, methodID, args); } jclass GetObjectClass(jobject obj) { return functions->GetObjectClass(this, obj); } jboolean IsInstanceOf(jobject obj, jclass clazz) { return functions->IsInstanceOf(this, obj, clazz); } jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetMethodID(this, clazz, name, sig); } #define CALL_TYPE_METHOD(_jtype, _jname) \ _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...) \ { \ _jtype result; \ va_list args; \ va_start(args, methodID); \ result = functions->Call##_jname##MethodV(this, obj, methodID, \ args); \ va_end(args); \ return result; \ } #define CALL_TYPE_METHODV(_jtype, _jname) \ _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID, \ va_list args) \ { return functions->Call##_jname##MethodV(this, obj, methodID, args); } #define CALL_TYPE_METHODA(_jtype, _jname) \ _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \ jvalue* args) \ { return functions->Call##_jname##MethodA(this, obj, methodID, args); } #define CALL_TYPE(_jtype, _jname) \ CALL_TYPE_METHOD(_jtype, _jname) \ CALL_TYPE_METHODV(_jtype, _jname) \ CALL_TYPE_METHODA(_jtype, _jname) CALL_TYPE(jobject, Object) CALL_TYPE(jboolean, Boolean) CALL_TYPE(jbyte, Byte) CALL_TYPE(jchar, Char) CALL_TYPE(jshort, Short) CALL_TYPE(jint, Int) CALL_TYPE(jlong, Long) CALL_TYPE(jfloat, Float) CALL_TYPE(jdouble, Double) void CallVoidMethod(jobject obj, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallVoidMethodV(this, obj, methodID, args); va_end(args); } void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args) { functions->CallVoidMethodV(this, obj, methodID, args); } void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args) { functions->CallVoidMethodA(this, obj, methodID, args); } #define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \ _jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz, \ jmethodID methodID, ...) \ { \ _jtype result; \ va_list args; \ va_start(args, methodID); \ result = functions->CallNonvirtual##_jname##MethodV(this, obj, \ clazz, methodID, args); \ va_end(args); \ return result; \ } #define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \ _jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz, \ jmethodID methodID, va_list args) \ { return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz, \ methodID, args); } #define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) \ _jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz, \ jmethodID methodID, jvalue* args) \ { return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz, \ methodID, args); } #define CALL_NONVIRT_TYPE(_jtype, _jname) \ CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \ CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \ CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) CALL_NONVIRT_TYPE(jobject, Object) CALL_NONVIRT_TYPE(jboolean, Boolean) CALL_NONVIRT_TYPE(jbyte, Byte) CALL_NONVIRT_TYPE(jchar, Char) CALL_NONVIRT_TYPE(jshort, Short) CALL_NONVIRT_TYPE(jint, Int) CALL_NONVIRT_TYPE(jlong, Long) CALL_NONVIRT_TYPE(jfloat, Float) CALL_NONVIRT_TYPE(jdouble, Double) void CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); va_end(args); } void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); } void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue* args) { functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); } jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetFieldID(this, clazz, name, sig); } jobject GetObjectField(jobject obj, jfieldID fieldID) { return functions->GetObjectField(this, obj, fieldID); } jboolean GetBooleanField(jobject obj, jfieldID fieldID) { return functions->GetBooleanField(this, obj, fieldID); } jbyte GetByteField(jobject obj, jfieldID fieldID) { return functions->GetByteField(this, obj, fieldID); } jchar GetCharField(jobject obj, jfieldID fieldID) { return functions->GetCharField(this, obj, fieldID); } jshort GetShortField(jobject obj, jfieldID fieldID) { return functions->GetShortField(this, obj, fieldID); } jint GetIntField(jobject obj, jfieldID fieldID) { return functions->GetIntField(this, obj, fieldID); } jlong GetLongField(jobject obj, jfieldID fieldID) { return functions->GetLongField(this, obj, fieldID); } jfloat GetFloatField(jobject obj, jfieldID fieldID) { return functions->GetFloatField(this, obj, fieldID); } jdouble GetDoubleField(jobject obj, jfieldID fieldID) { return functions->GetDoubleField(this, obj, fieldID); } void SetObjectField(jobject obj, jfieldID fieldID, jobject value) { functions->SetObjectField(this, obj, fieldID, value); } void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value) { functions->SetBooleanField(this, obj, fieldID, value); } void SetByteField(jobject obj, jfieldID fieldID, jbyte value) { functions->SetByteField(this, obj, fieldID, value); } void SetCharField(jobject obj, jfieldID fieldID, jchar value) { functions->SetCharField(this, obj, fieldID, value); } void SetShortField(jobject obj, jfieldID fieldID, jshort value) { functions->SetShortField(this, obj, fieldID, value); } void SetIntField(jobject obj, jfieldID fieldID, jint value) { functions->SetIntField(this, obj, fieldID, value); } void SetLongField(jobject obj, jfieldID fieldID, jlong value) { functions->SetLongField(this, obj, fieldID, value); } void SetFloatField(jobject obj, jfieldID fieldID, jfloat value) { functions->SetFloatField(this, obj, fieldID, value); } void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value) { functions->SetDoubleField(this, obj, fieldID, value); } jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetStaticMethodID(this, clazz, name, sig); } #define CALL_STATIC_TYPE_METHOD(_jtype, _jname) \ _jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID, \ ...) \ { \ _jtype result; \ va_list args; \ va_start(args, methodID); \ result = functions->CallStatic##_jname##MethodV(this, clazz, \ methodID, args); \ va_end(args); \ return result; \ } #define CALL_STATIC_TYPE_METHODV(_jtype, _jname) \ _jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID, \ va_list args) \ { return functions->CallStatic##_jname##MethodV(this, clazz, methodID, \ args); } #define CALL_STATIC_TYPE_METHODA(_jtype, _jname) \ _jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID, \ jvalue* args) \ { return functions->CallStatic##_jname##MethodA(this, clazz, methodID, \ args); } #define CALL_STATIC_TYPE(_jtype, _jname) \ CALL_STATIC_TYPE_METHOD(_jtype, _jname) \ CALL_STATIC_TYPE_METHODV(_jtype, _jname) \ CALL_STATIC_TYPE_METHODA(_jtype, _jname) CALL_STATIC_TYPE(jobject, Object) CALL_STATIC_TYPE(jboolean, Boolean) CALL_STATIC_TYPE(jbyte, Byte) CALL_STATIC_TYPE(jchar, Char) CALL_STATIC_TYPE(jshort, Short) CALL_STATIC_TYPE(jint, Int) CALL_STATIC_TYPE(jlong, Long) CALL_STATIC_TYPE(jfloat, Float) CALL_STATIC_TYPE(jdouble, Double) void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallStaticVoidMethodV(this, clazz, methodID, args); va_end(args); } void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args) { functions->CallStaticVoidMethodV(this, clazz, methodID, args); } void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args) { functions->CallStaticVoidMethodA(this, clazz, methodID, args); } jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetStaticFieldID(this, clazz, name, sig); } jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { return functions->GetStaticObjectField(this, clazz, fieldID); } jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { return functions->GetStaticBooleanField(this, clazz, fieldID); } jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { return functions->GetStaticByteField(this, clazz, fieldID); } jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { return functions->GetStaticCharField(this, clazz, fieldID); } jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { return functions->GetStaticShortField(this, clazz, fieldID); } jint GetStaticIntField(jclass clazz, jfieldID fieldID) { return functions->GetStaticIntField(this, clazz, fieldID); } jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { return functions->GetStaticLongField(this, clazz, fieldID); } jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { return functions->GetStaticFloatField(this, clazz, fieldID); } jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { return functions->GetStaticDoubleField(this, clazz, fieldID); } void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) { functions->SetStaticObjectField(this, clazz, fieldID, value); } void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) { functions->SetStaticBooleanField(this, clazz, fieldID, value); } void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) { functions->SetStaticByteField(this, clazz, fieldID, value); } void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) { functions->SetStaticCharField(this, clazz, fieldID, value); } void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) { functions->SetStaticShortField(this, clazz, fieldID, value); } void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) { functions->SetStaticIntField(this, clazz, fieldID, value); } void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) { functions->SetStaticLongField(this, clazz, fieldID, value); } void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) { functions->SetStaticFloatField(this, clazz, fieldID, value); } void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) { functions->SetStaticDoubleField(this, clazz, fieldID, value); } jstring NewString(const jchar* unicodeChars, jsize len) { return functions->NewString(this, unicodeChars, len); } jsize GetStringLength(jstring string) { return functions->GetStringLength(this, string); } const jchar* GetStringChars(jstring string, jboolean* isCopy) { return functions->GetStringChars(this, string, isCopy); } void ReleaseStringChars(jstring string, const jchar* chars) { functions->ReleaseStringChars(this, string, chars); } jstring NewStringUTF(const char* bytes) { return functions->NewStringUTF(this, bytes); } jsize GetStringUTFLength(jstring string) { return functions->GetStringUTFLength(this, string); } const char* GetStringUTFChars(jstring string, jboolean* isCopy) { return functions->GetStringUTFChars(this, string, isCopy); } void ReleaseStringUTFChars(jstring string, const char* utf) { functions->ReleaseStringUTFChars(this, string, utf); } jsize GetArrayLength(jarray array) { return functions->GetArrayLength(this, array); } jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement) { return functions->NewObjectArray(this, length, elementClass, initialElement); } jobject GetObjectArrayElement(jobjectArray array, jsize index) { return functions->GetObjectArrayElement(this, array, index); } void SetObjectArrayElement(jobjectArray array, jsize index, jobject value) { functions->SetObjectArrayElement(this, array, index, value); } jbooleanArray NewBooleanArray(jsize length) { return functions->NewBooleanArray(this, length); } jbyteArray NewByteArray(jsize length) { return functions->NewByteArray(this, length); } jcharArray NewCharArray(jsize length) { return functions->NewCharArray(this, length); } jshortArray NewShortArray(jsize length) { return functions->NewShortArray(this, length); } jintArray NewIntArray(jsize length) { return functions->NewIntArray(this, length); } jlongArray NewLongArray(jsize length) { return functions->NewLongArray(this, length); } jfloatArray NewFloatArray(jsize length) { return functions->NewFloatArray(this, length); } jdoubleArray NewDoubleArray(jsize length) { return functions->NewDoubleArray(this, length); } jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy) { return functions->GetBooleanArrayElements(this, array, isCopy); } jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy) { return functions->GetByteArrayElements(this, array, isCopy); } jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy) { return functions->GetCharArrayElements(this, array, isCopy); } jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy) { return functions->GetShortArrayElements(this, array, isCopy); } jint* GetIntArrayElements(jintArray array, jboolean* isCopy) { return functions->GetIntArrayElements(this, array, isCopy); } jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy) { return functions->GetLongArrayElements(this, array, isCopy); } jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy) { return functions->GetFloatArrayElements(this, array, isCopy); } jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy) { return functions->GetDoubleArrayElements(this, array, isCopy); } void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode) { functions->ReleaseBooleanArrayElements(this, array, elems, mode); } void ReleaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode) { functions->ReleaseByteArrayElements(this, array, elems, mode); } void ReleaseCharArrayElements(jcharArray array, jchar* elems, jint mode) { functions->ReleaseCharArrayElements(this, array, elems, mode); } void ReleaseShortArrayElements(jshortArray array, jshort* elems, jint mode) { functions->ReleaseShortArrayElements(this, array, elems, mode); } void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode) { functions->ReleaseIntArrayElements(this, array, elems, mode); } void ReleaseLongArrayElements(jlongArray array, jlong* elems, jint mode) { functions->ReleaseLongArrayElements(this, array, elems, mode); } void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode) { functions->ReleaseFloatArrayElements(this, array, elems, mode); } void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode) { functions->ReleaseDoubleArrayElements(this, array, elems, mode); } void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) { functions->GetBooleanArrayRegion(this, array, start, len, buf); } void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf) { functions->GetByteArrayRegion(this, array, start, len, buf); } void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* buf) { functions->GetCharArrayRegion(this, array, start, len, buf); } void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf) { functions->GetShortArrayRegion(this, array, start, len, buf); } void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf) { functions->GetIntArrayRegion(this, array, start, len, buf); } void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf) { functions->GetLongArrayRegion(this, array, start, len, buf); } void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf) { functions->GetFloatArrayRegion(this, array, start, len, buf); } void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf) { functions->GetDoubleArrayRegion(this, array, start, len, buf); } void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean* buf) { functions->SetBooleanArrayRegion(this, array, start, len, buf); } void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte* buf) { functions->SetByteArrayRegion(this, array, start, len, buf); } void SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar* buf) { functions->SetCharArrayRegion(this, array, start, len, buf); } void SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort* buf) { functions->SetShortArrayRegion(this, array, start, len, buf); } void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf) { functions->SetIntArrayRegion(this, array, start, len, buf); } void SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong* buf) { functions->SetLongArrayRegion(this, array, start, len, buf); } void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat* buf) { functions->SetFloatArrayRegion(this, array, start, len, buf); } void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble* buf) { functions->SetDoubleArrayRegion(this, array, start, len, buf); } jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods) { return functions->RegisterNatives(this, clazz, methods, nMethods); } jint UnregisterNatives(jclass clazz) { return functions->UnregisterNatives(this, clazz); } jint MonitorEnter(jobject obj) { return functions->MonitorEnter(this, obj); } jint MonitorExit(jobject obj) { return functions->MonitorExit(this, obj); } jint GetJavaVM(JavaVM** vm) { return functions->GetJavaVM(this, vm); } void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf) { functions->GetStringRegion(this, str, start, len, buf); } void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf) { return functions->GetStringUTFRegion(this, str, start, len, buf); } void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy) { return functions->GetPrimitiveArrayCritical(this, array, isCopy); } void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode) { functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); } const jchar* GetStringCritical(jstring string, jboolean* isCopy) { return functions->GetStringCritical(this, string, isCopy); } void ReleaseStringCritical(jstring string, const jchar* carray) { functions->ReleaseStringCritical(this, string, carray); } jweak NewWeakGlobalRef(jobject obj) { return functions->NewWeakGlobalRef(this, obj); } void DeleteWeakGlobalRef(jweak obj) { functions->DeleteWeakGlobalRef(this, obj); } jboolean ExceptionCheck() { return functions->ExceptionCheck(this); } jobject NewDirectByteBuffer(void* address, jlong capacity) { return functions->NewDirectByteBuffer(this, address, capacity); } void* GetDirectBufferAddress(jobject buf) { return functions->GetDirectBufferAddress(this, buf); } jlong GetDirectBufferCapacity(jobject buf) { return functions->GetDirectBufferCapacity(this, buf); } /* added in JNI 1.6 */ jobjectRefType GetObjectRefType(jobject obj) { return functions->GetObjectRefType(this, obj); } #endif /*__cplusplus*/ }; /* * JNI invocation interface. */ struct JNIInvokeInterface { void* reserved0; void* reserved1; void* reserved2; jint (*DestroyJavaVM)(JavaVM*); jint (*AttachCurrentThread)(JavaVM*, void**, void*); jint (*DetachCurrentThread)(JavaVM*); jint (*GetEnv)(JavaVM*, void**, jint); jint (*AttachCurrentThreadAsDaemon)(JavaVM*, void**, void*); }; /* * C++ version. */ struct _JavaVM { const struct JNIInvokeInterface* functions; #if defined(__cplusplus) jint DestroyJavaVM() { return functions->DestroyJavaVM(this); } jint AttachCurrentThread(void** p_env, void* thr_args) { return functions->AttachCurrentThread(this, p_env, thr_args); } jint DetachCurrentThread() { return functions->DetachCurrentThread(this); } jint GetEnv(void** env, jint version) { return functions->GetEnv(this, env, version); } jint AttachCurrentThreadAsDaemon(void** p_env, void* thr_args) { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); } #endif /*__cplusplus*/ }; struct JavaVMAttachArgs { jint version; /* must be >= JNI_VERSION_1_2 */ const char* name; /* NULL or name of thread as modified UTF-8 str */ jobject group; /* global ref of a ThreadGroup object, or NULL */ }; typedef struct JavaVMAttachArgs JavaVMAttachArgs; /* * JNI 1.2+ initialization. (As of 1.6, the pre-1.2 structures are no * longer supported.) */ typedef struct JavaVMOption { const char* optionString; void* extraInfo; } JavaVMOption; typedef struct JavaVMInitArgs { jint version; /* use JNI_VERSION_1_2 or later */ jint nOptions; JavaVMOption* options; jboolean ignoreUnrecognized; } JavaVMInitArgs; #ifdef __cplusplus extern "C" { #endif /* * VM initialization functions. * * Note these are the only symbols exported for JNI by the VM. */ jint JNI_GetDefaultJavaVMInitArgs(void*); jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*); jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*); // commented out by marscher //#define JNIIMPORT //#define JNIEXPORT __attribute__ ((visibility ("default"))) //#define JNICALL /* * Prototypes for functions exported by loadable shared libs. These are * called by JNI, not provided by JNI. */ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved); JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved); #ifdef __cplusplus } #endif /* * Manifest constants. */ #define JNI_FALSE 0 #define JNI_TRUE 1 #define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 #define JNI_VERSION_1_7 0x00010007 #define JNI_VERSION_1_8 0x00010008 #define JNI_VERSION_9 0x00090000 #define JNI_VERSION_10 0x000a0000 #define JNI_VERSION_19 0x00130000 #define JNI_VERSION_20 0x00140000 #define JNI_VERSION_21 0x00150000 #define JNI_OK (0) /* no error */ #define JNI_ERR (-1) /* generic error */ #define JNI_EDETACHED (-2) /* thread detached from the VM */ #define JNI_EVERSION (-3) /* JNI version error */ #define JNI_COMMIT 1 /* copy content, do not free buffer */ #define JNI_ABORT 2 /* free buffer w/o copying back */ #endif /* JNI_H_ */ jpype-1.6.0/native/jpype_module/000077500000000000000000000000001501674766700166525ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/licenseheader.txt000066400000000000000000000015251501674766700222110ustar00rootroot00000000000000<#if licenseFirst??> ${licenseFirst} ${licensePrefix} Licensed under the Apache License, Version 2.0 (the "License"); you may not ${licensePrefix} use this file except in compliance with the License. You may obtain a copy of ${licensePrefix} the License at ${licensePrefix} ${licensePrefix} http://www.apache.org/licenses/LICENSE-2.0 ${licensePrefix} ${licensePrefix} Unless required by applicable law or agreed to in writing, software ${licensePrefix} distributed under the License is distributed on an "AS IS" BASIS, WITHOUT ${licensePrefix} WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the ${licensePrefix} License for the specific language governing permissions and limitations under ${licensePrefix} the License. ${licensePrefix} ${licensePrefix} See NOTICE file for details. <#if licenseLast??> ${licenseLast} jpype-1.6.0/native/jpype_module/nb-configuration.xml000066400000000000000000000140201501674766700226350ustar00rootroot00000000000000 2 2 4 4 false 4 2 4 2 LEAVE_ALONE NEW_LINE none NEW_LINE 2 true 8 LEAVE_ALONE NEW_LINE 80 LEAVE_ALONE LEAVE_ALONE NEW_LINE true none 4 4 8 80 true project jpype-1.6.0/native/jpype_module/nbactions.xml000066400000000000000000000046641501674766700213660ustar00rootroot00000000000000 run jar process-classes org.codehaus.mojo:exec-maven-plugin:3.1.0:exec ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} org.jpype.bridge.Bridge java ../.. debug jar process-classes org.codehaus.mojo:exec-maven-plugin:3.1.0:exec -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} ${packageClassName} java true ../.. profile jar process-classes org.codehaus.mojo:exec-maven-plugin:3.1.0:exec ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} ${packageClassName} java ../.. jpype-1.6.0/native/jpype_module/pom.xml000066400000000000000000000041771501674766700202000ustar00rootroot00000000000000 4.0.0 org.jpype jpype 1.6.0 jar org.testng testng 6.8.1 test net.java.dev.jna jna 5.17.0 jar UTF-8 21 org.apache.maven.plugins maven-compiler-plugin 3.10.1 compile-single-class compile compile exclude/org/jpype/Reflector0.java ${project.build.directory}/classes/META-INF/versions/0 jpype_module jpype-1.6.0/native/jpype_module/src/000077500000000000000000000000001501674766700174415ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/000077500000000000000000000000001501674766700203655ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/000077500000000000000000000000001501674766700213065ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/exclude/000077500000000000000000000000001501674766700227375ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/exclude/org/000077500000000000000000000000001501674766700235265ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/exclude/org/jpype/000077500000000000000000000000001501674766700246555ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/exclude/org/jpype/Reflector0.java000066400000000000000000000016501501674766700275270ustar00rootroot00000000000000package org.jpype; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Reflector0 implements JPypeReflector { public Reflector0() {} /** * Call a method using reflection. * * This method creates a stackframe so that caller sensitive methods will * execute properly. * * @param method is the method to call. * @param obj is the object to operate on, it will be null if the method is * static. * @param args the arguments to method. * @return the object that results form the invocation. * @throws java.lang.Throwable throws whatever type the called method * produces. */ public Object callMethod(Method method, Object obj, Object[] args) throws Throwable { try { return method.invoke(obj, args); } catch (InvocationTargetException ex) { // ex.printStackTrace(); throw ex.getCause(); } } } jpype-1.6.0/native/jpype_module/src/main/java/manifest.txt000066400000000000000000000001631501674766700236550ustar00rootroot00000000000000Manifest-Version: 1.0 Specification-Title: org.jpype Implementation-Title: org.jpype Implementation-Version: 1.5.1 jpype-1.6.0/native/jpype_module/src/main/java/module-info.java000066400000000000000000000020141501674766700243640ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ module jpype { requires java.xml; requires java.sql; requires java.management; exports org.jpype; exports org.jpype.html; exports org.jpype.javadoc; exports org.jpype.manager; exports org.jpype.pickle; exports org.jpype.pkg; exports org.jpype.proxy; exports org.jpype.ref; } jpype-1.6.0/native/jpype_module/src/main/java/org/000077500000000000000000000000001501674766700220755ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/000077500000000000000000000000001501674766700232245ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/JPypeClassLoader.java000066400000000000000000000213431501674766700272360ustar00rootroot00000000000000package org.jpype; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.net.URLDecoder; /** * Class loader for JPype. * * This is augmented to manage directory resources, allow for late loading, and * handling of resources on non-ASCII paths. */ public class JPypeClassLoader extends URLClassLoader { HashMap> map = new HashMap<>(); int code = 0; public JPypeClassLoader(ClassLoader parent) { super(initial(), parent); } /** * Used to keep the cache up to date. * * @return */ public int getCode() { return code; } /** * Special routine for handling non-ascii paths. * * If we are loaded as the system ClassLoader, then we will use * "jpype.class.path" rather than "java.class.path" during the load process. * We will move it into the expected place after so no one is the wiser. * * @return */ private static URL[] initial() { // Check to see if we have a late loaded path String cp = System.getProperty("jpype.class.path"); if (cp == null) return new URL[0]; try { cp = URLDecoder.decode(cp, "UTF-8"); } catch (UnsupportedEncodingException ex) { // ignored } ArrayList path = new ArrayList<>(); int last = 0; int next = 0; while (next != -1) { // Find the parts next = cp.indexOf(File.pathSeparator, last); String element = (next == -1) ? cp.substring(last) : cp.substring(last, next); if (!element.isEmpty()) { try { URL url = Paths.get(element).toUri().toURL(); if (url != null) path.add(url); } catch (MalformedURLException ex) { System.err.println("Malformed url in classpath skipped " + element); } } last = next + 1; } // Replace the path System.clearProperty("jpype.class.path"); System.setProperty("java.class.path", cp); return path.toArray(new URL[0]); } // This is required to add a Java agent even if it is already in the path @SuppressWarnings("unused") private void appendToClassPathForInstrumentation(String path) throws Throwable { addURL(Paths.get(path).toAbsolutePath().toUri().toURL()); } /** * Add a set of jars to the classpath. * * @param root * @param glob * @throws IOException */ public void addPaths(Path root, String glob) throws IOException { final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(glob); Files.walkFileTree(root, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { if (pathMatcher.matches(root.relativize(path))) { URL url = path.toUri().toURL(); addURL(url); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } }); } /** * Add a path to the loader after the JVM is started. * * @param path * @throws FileNotFoundException */ public void addPath(Path path) throws FileNotFoundException { try { if (!Files.exists(path)) throw new FileNotFoundException(path.toString()); this.addURL(path.toUri().toURL()); } catch (MalformedURLException ex) { // This should never happen throw new RuntimeException(ex); } } /** * Loads a class from the class loader. * * @param name is the name of the class with java class notation (using dots). * @return the class * @throws ClassNotFoundException was not found by the class loader. * @throws ClassFormatError if the class byte code was invalid. */ @Override public Class findClass(String name) throws ClassNotFoundException, ClassFormatError { String aname = name.replace('.', '/') + ".class"; URL url = this.getResource(aname); if (url == null) throw new ClassNotFoundException(name); try { URLConnection connection = url.openConnection(); try (InputStream is = connection.getInputStream()) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int bytes; byte[] d = new byte[1024]; while ((bytes = is.read(d, 0, d.length)) != -1) { buffer.write(d, 0, bytes); } buffer.flush(); byte[] data = buffer.toByteArray(); return defineClass(null, data, 0, data.length); } } catch (IOException ex) { } throw new ClassNotFoundException(name); } @Override public URL findResource(String name) { // Check local first URL url = super.findResource(name); if (url != null) return url; // Both with and without / should generate the same result if (name.endsWith("/")) name = name.substring(0, name.length() - 1); if (map.containsKey(name)) return map.get(name).get(0); // We have some resource which must be sourced to a particular class loader if (name.startsWith("org/jpype/")) return getResource("META-INF/versions/0/" + name); return null; } @Override public Enumeration findResources(String name) throws IOException { ArrayList out = new ArrayList<>(); out.addAll(Collections.list(super.findResources(name))); // Both with and without / should generate the same result if (name.endsWith("/")) name = name.substring(0, name.length() - 1); if (map.containsKey(name)) out.addAll(map.get(name)); return Collections.enumeration(out); } /** * Add a resource to the search. * * Many jar files lack directory support which is needed for the packaging * import. * * @param name * @param url */ public void addResource(String name, URL url) { if (!this.map.containsKey(name)) this.map.put(name, new ArrayList<>()); this.map.get(name).add(url); } @Override public void addURL(URL url) { // Mark our cache as dirty code = code * 98745623 + url.hashCode(); // add to the search tree super.addURL(url); // See if it is a path Path path; try { path = Paths.get(url.toURI()); } catch (URISyntaxException ex) { return; } // Scan for missing resources scanJar(path); } /** * Recreate missing directory entries for Jars that lack indexing. * * Some jar files are missing the directory entries that prevents use from * properly importing their contents. This procedure scans a jar file when * loaded to build missing directories. * * @param path */ void scanJar(Path path) { if (!Files.exists(path)) return; if (Files.isDirectory(path)) return; try (JarFile jf = new JarFile(path.toFile())) { Enumeration entries = jf.entries(); URI abs = path.toAbsolutePath().toUri(); Set urls = new java.util.HashSet(); while (entries.hasMoreElements()) { JarEntry next = entries.nextElement(); String name = next.getName(); // Skip over META-INF if (name.startsWith("META-INF/")) continue; if (next.isDirectory()) { // If we find a directory entry then the jar has directories already return; } // Split on each separator in the name int i = 0; while (true) { i = name.indexOf("/", i); if (i == -1) break; String name2 = name.substring(0, i); i++; // Already have an entry no problem if (urls.contains(name2)) continue; // Add a new entry for the missing directory String jar = "jar:" + abs + "!/" + name2 + "/"; urls.add(name2); this.addResource(name2, new URL(jar)); } } } catch (IOException ex) { // Anything goes wrong skip it } } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/JPypeContext.java000066400000000000000000000414261501674766700264720ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype; import java.io.File; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteOrder; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.jpype.manager.TypeFactory; import org.jpype.manager.TypeFactoryNative; import org.jpype.manager.TypeManager; import org.jpype.pkg.JPypePackage; import org.jpype.pkg.JPypePackageManager; import org.jpype.ref.JPypeReferenceQueue; /** * Context for JPype. *

* This is the part of JPype that holds all resources. After the classloader is * created this class is given the address of the context object in JPype. Any * resources in JPype Java layer can be contacted using the context. *

* Boot order is - create the C++ portion of the context. - start the jvm - load * the bootloader - install the jar into the bootloader - install all native * methods using the bootloader - create the Java portion of the context. - use * the Java context to access the resources (ReferenceQueue, TypeFactory, * TypeManager) *

* Once started, python calls use the context to get a frame and attach their * threads. Methods called from Java will get the env and use it to get their * context from which they can create a frame. *

* The C++ context will hold all the previous global variables thus allowing the * C++ portion to be cleaned up properly when the JVM is shutdown or * disconnected. *

* As the JPypeContext can't be tested directly from Java code, it will need to * be kept light. *

* Our goal is to remove as much direct contact methods as possible from the C++ * layer. Previous globals in JPTypeManager move to the context as do the * contents of JPJni. * * * * @author nelson85 */ public class JPypeContext { public final String VERSION = "1.6.0"; private static final JPypeContext INSTANCE = new JPypeContext(); // This is the C++ portion of the context. private long context; private TypeFactory typeFactory; private TypeManager typeManager; private JPypeClassLoader classLoader; private final AtomicInteger shutdownFlag = new AtomicInteger(); private final List shutdownHooks = new ArrayList<>(); private final List postHooks = new ArrayList<>(); public static boolean freeResources = true; public JPypeReflector reflector = null; static public JPypeContext getInstance() { return INSTANCE; } /** * Start the JPype system. * * @param context is the C++ portion of the context. * @param loader is the classloader holding JPype resources. * @return the created context. */ private static JPypeContext createContext(long context, ClassLoader loader, String nativeLib, boolean interrupt) throws Throwable { try { if (nativeLib != null) { System.load(nativeLib); } INSTANCE.context = context; INSTANCE.classLoader = (JPypeClassLoader) loader; INSTANCE.typeFactory = new TypeFactoryNative(); INSTANCE.typeManager = new TypeManager(context, INSTANCE.typeFactory); INSTANCE.initialize(interrupt); try { INSTANCE.reflector = (JPypeReflector) Class.forName("org.jpype.Reflector0", true, loader) .getConstructor() .newInstance(); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { throw new RuntimeException("Unable to create reflector " + ex.getMessage(), ex); } scanExistingJars(); return INSTANCE; } catch (Throwable ex) { ex.printStackTrace(System.err); throw ex; } } private JPypeContext() { } private void initialize(boolean interrupt) { // Okay everything is setup so lets give it a go. this.typeManager.init(); JPypeReferenceQueue.getInstance().start(); if (!interrupt) JPypeSignal.installHandlers(); // Install a shutdown hook to clean up Python resources. Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { INSTANCE.shutdown(); } })); } /** * Shutdown and remove all Python resources. * * This hook is only called after the last user thread has died. Thus the only * remaining connections are proxies that were attached to the JVM shutdown * hook, the reference queue, and the typemanager. * * This routine will try to take out the last connections in an orderly * fashion. Inherently this is a very dangerous time as portions of Java have * already been deactivated. */ @SuppressWarnings( { "CallToThreadYield", "SleepWhileInLoop" }) private void shutdown() { try { // Try to yield in case there is a race condition. The user // may have installed a shutdown hook, but we cannot verify // the order that shutdown hook threads are executed. Thus we will // try to intentionally lose the race. // // This will only occur if something registered a shutdown hook through // a Java API. Those registered though the JPype API will be joined // manually. for (int i = 0; i < 5; i++) { try { Thread.sleep(1); Thread.yield(); } catch (InterruptedException ex) { } } // Execute any used defined shutdown hooks registered with JPype. if (!this.shutdownHooks.isEmpty()) { for (Thread thread : this.shutdownHooks) { thread.start(); } for (Thread thread : this.shutdownHooks) { try { thread.join(); } catch (InterruptedException ex) { } } } // Disable all future calls to proxies this.shutdownFlag.incrementAndGet(); // Past this point any further execution of a Python proxy would // be fatal. Thread t1 = Thread.currentThread(); Map threads = Thread.getAllStackTraces(); for (Thread t : threads.keySet()) { if (t1 == t || t.isDaemon()) continue; t.interrupt(); } // Inform Python no more calls are permitted onShutdown(this.context); Thread.yield(); } catch (Throwable th) { } if (freeResources) { // Release all Python references try { JPypeReferenceQueue.getInstance().stop(); } catch (Throwable th) { } // Release any C++ resources try { this.typeManager.shutdown(); } catch (Throwable th) { } } // Execute post hooks for (Runnable run : this.postHooks) { run.run(); } try { classLoader.close(); } catch (IOException ex) { // ignored } } private static native void onShutdown(long ctxt); public void addShutdownHook(Thread th) { this.shutdownHooks.add(th); } public boolean removeShutdownHook(Thread th) { if (this.shutdownHooks.contains(th)) { this.shutdownHooks.remove(th); return true; } else return Runtime.getRuntime().removeShutdownHook(th); } /** * Get the C++ portion. * * @return */ public long getContext() { return context; } public ClassLoader getClassLoader() { if (this.classLoader == null) this.classLoader = new JPypeClassLoader(ClassLoader.getSystemClassLoader()); return this.classLoader; } public TypeFactory getTypeFactory() { return this.typeFactory; } public TypeManager getTypeManager() { return this.typeManager; } /** * Add a hook to run after Python interface is shutdown. * * This must never have a Python method attached. * * @param run */ public void _addPost(Runnable run) { this.postHooks.add(run); } /** * Helper function for collect rectangular, */ private static boolean collect(List l, Object o, int q, int[] shape, int d) { if (Array.getLength(o) != shape[q]) return false; if (q + 1 == d) { l.add(o); return true; } for (int i = 0; i < shape[q]; ++i) { if (!collect(l, Array.get(o, i), q + 1, shape, d)) return false; } return true; } /** * Collect up a rectangular primitive array for a Python memory view. * * If it is a rectangular primitive array then the result will be an object * array containing. - the primitive type - an int array with the shape of the * array - each of the primitive arrays that will need be visited in order. * * This is the safest way to provide a view as we are verifying and collected * thus even if something mutates the shape of the array after we have * visited, we have a locked copy. * * @param o is the object to be tested. * @return null if the object is not a rectangular primitive array. */ public Object[] collectRectangular(Object o) { if (o == null || !o.getClass().isArray()) return null; int[] shape = new int[5]; int d = 0; ArrayList out = new ArrayList<>(); Object o1 = o; Class c1 = o1.getClass(); for (int i = 0; i < 5; ++i) { int l = Array.getLength(o1); if (l == 0) return null; shape[d++] = l; o1 = Array.get(o1, 0); if (o1 == null) return null; c1 = c1.getComponentType(); if (!c1.isArray()) break; } if (!c1.isPrimitive()) return null; out.add(c1); shape = Arrays.copyOfRange(shape, 0, d); out.add(shape); int total = 1; for (int i = 0; i < d - 1; i++) total *= shape[i]; out.ensureCapacity(total + 2); if (d == 5) return null; if (!collect(out, o, 0, shape, d)) return null; return out.toArray(); } private Object unpack(int size, Object parts) { Object e0 = Array.get(parts, 0); Class c = e0.getClass(); int segments = Array.getLength(parts) / size; Object a2 = Array.newInstance(c, size); Object a1 = Array.newInstance(a2.getClass(), segments); int k = 0; for (int i = 0; i < segments; i++) { for (int j = 0; j < size; j++, k++) { Object o = Array.get(parts, k); Array.set(a2, j, o); } Array.set(a1, i, a2); if (i < segments - 1) a2 = Array.newInstance(c, size); } return a1; } private Object assemble(int[] dims, Object parts) { int n = dims.length; if (n == 1) return Array.get(parts, 0); if (n == 2) return Array.get(unpack(dims[0], parts), 0); for (int i = 0; i < n - 2; ++i) { parts = unpack(dims[n - i - 2], parts); } return parts; } public boolean isShutdown() { return shutdownFlag.get() > 0; } /** * Clear the current interrupt. * * @param x is true if an exception should be thrown. * @throws InterruptedException */ public static void clearInterrupt(boolean x) throws InterruptedException { try { Thread th = Thread.currentThread(); // Only relevant if this is the main thread for signal handling if (th != JPypeSignal.main) return; // Unconditionally clear the interrupt flag if we are called from // C++. This happens when a field get() or method call() is // invoked. if (!x) JPypeSignal.acknowledgePy(); // Check if this thread is interrupted if (th.isInterrupted()) { // Clear the flag in C++ JPypeSignal.acknowledgePy(); // Clear the flag in Java Thread.sleep(1); } } catch (InterruptedException ex) { if (x) throw ex; } } public long getExcClass(Throwable th) { if (th instanceof PyExceptionProxy) return ((PyExceptionProxy) th).cls; return 0; } public long getExcValue(Throwable th) { if (th instanceof PyExceptionProxy) return ((PyExceptionProxy) th).value; return 0; } private Exception createException(long l0, long l1) { return new PyExceptionProxy(l0, l1); } private boolean order(Buffer b) { if (b instanceof java.nio.ByteBuffer) return ((java.nio.ByteBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; if (b instanceof java.nio.ShortBuffer) return ((java.nio.ShortBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; if (b instanceof java.nio.CharBuffer) return ((java.nio.CharBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; if (b instanceof java.nio.IntBuffer) return ((java.nio.IntBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; if (b instanceof java.nio.LongBuffer) return ((java.nio.LongBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; if (b instanceof java.nio.FloatBuffer) return ((java.nio.FloatBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; if (b instanceof java.nio.DoubleBuffer) return ((java.nio.DoubleBuffer) b).order() == ByteOrder.LITTLE_ENDIAN; return true; } public boolean isPackage(String s) { s = JPypeKeywords.safepkg(s); return JPypePackageManager.isPackage(s); } public JPypePackage getPackage(String s) { s = JPypeKeywords.safepkg(s); if (!JPypePackageManager.isPackage(s)) return null; return new JPypePackage(s); } /** * Utility to probe functional interfaces. * * @param cls * @return */ public static String getFunctional(Class cls) { Method m = JPypeUtilities.getFunctionalInterfaceMethod(cls); return m != null ? m.getName() : null; } /** * Utility function for extracting the unique portion of a stack trace. * * This is a bit different that the Java method which works from the back. We * will be using fake stacktraces from Python at some point so finding the * first common is a better approach. * * @param th is the throwable. * @param enclosing is the throwsble that holds this or null if top level. * @return the unique frames as an object array with 4 objects per frame. */ public Object[] getStackTrace(Throwable th, Throwable enclosing) { StackTraceElement[] trace = th.getStackTrace(); if (trace == null || enclosing == null) return toFrames(trace); StackTraceElement[] te = enclosing.getStackTrace(); if (te == null) return toFrames(trace); for (int i = 0; i < trace.length; ++i) { if (trace[i].equals(te[0])) { return toFrames(Arrays.copyOfRange(trace, 0, i)); } } return toFrames(trace); } private Object[] toFrames(StackTraceElement[] stackTrace) { if (stackTrace == null) return null; Object[] out = new Object[4 * stackTrace.length]; int i = 0; for (StackTraceElement fr : stackTrace) { out[i++] = fr.getClassName(); out[i++] = fr.getMethodName(); out[i++] = fr.getFileName(); out[i++] = fr.getLineNumber(); } return out; } public void newWrapper(long l) { // We can only go through this point single file. synchronized (this.typeFactory) { this.typeFactory.newWrapper(context, l); } } private static void scanExistingJars() { // Scan existing jars for missing directory entries String[] paths = System.getProperty("java.class.path").split(File.pathSeparator); for (String path : paths) { INSTANCE.classLoader.scanJar(Paths.get(path)); } } private static long getTotalMemory() { return Runtime.getRuntime().totalMemory(); } private static long getFreeMemory() { return Runtime.getRuntime().freeMemory(); } private static long getMaxMemory() { return Runtime.getRuntime().maxMemory(); } private static long getUsedMemory() { return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); } private static long getHeapMemory() { java.lang.management.MemoryMXBean memoryBean = java.lang.management.ManagementFactory.getMemoryMXBean(); return memoryBean.getHeapMemoryUsage().getUsed(); } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/JPypeKeywords.java000066400000000000000000000031471501674766700266530ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * * @author nelson85 */ public class JPypeKeywords { final public static Set keywords = new HashSet<>(); public static void setKeywords(String[] s) { keywords.addAll(Arrays.asList(s)); } public static String wrap(String name) { if (keywords.contains(name)) return name + "_"; return name; } public static String unwrap(String name) { if (!name.endsWith("_")) return name; String name2 = name.substring(0, name.length() - 1); if (keywords.contains(name2)) return name2;; return name; } static String safepkg(String s) { if (!s.contains("_")) return s; String[] parts = s.split("\\."); for (int i = 0; i < parts.length; ++i) parts[i] = unwrap(parts[i]); return String.join(".", parts); } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/JPypeReflector.java000066400000000000000000000012711501674766700267650ustar00rootroot00000000000000package org.jpype; import java.lang.reflect.Method; /** * * @author nelson85 */ public interface JPypeReflector { /** * Call a method using reflection. * * This method creates a stackframe so that caller sensitive methods will * execute properly. * * @param method is the method to call. * @param obj is the object to operate on, it will be null if the method is * static. * @param args the arguments to method. * @return the object that results form the invocation. * @throws java.lang.Throwable throws whatever type the called method * produces. */ public Object callMethod(Method method, Object obj, Object[] args) throws Throwable; } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/JPypeSignal.java000066400000000000000000000045321501674766700262600ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Java wants to make this action nearly impossible. * * Thus the have warnings against it that cannot be disabled. So we will skin * this cat another way. */ public class JPypeSignal { static Thread main; static Object getSignalHandler(Class signalHandlerClazz, int signal) throws ClassNotFoundException { return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { signalHandlerClazz }, (proxy, method, args) -> { main.interrupt(); interruptPy(signal); return null; }); } static void installHandlers() { try { Class Signal = Class.forName("sun.misc.Signal"); Class SignalHandler = Class.forName("sun.misc.SignalHandler"); main = Thread.currentThread(); Method method = Signal.getMethod("handle", Signal, SignalHandler); Object intr = Signal.getDeclaredConstructor(String.class).newInstance("INT"); method.invoke(null, intr, getSignalHandler(SignalHandler, 2)); Object intrTerm = Signal.getDeclaredConstructor(String.class).newInstance("TERM"); method.invoke(null, intrTerm, getSignalHandler(SignalHandler, 15)); } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException | InstantiationException | ClassNotFoundException | NoSuchMethodException | SecurityException ex) { // If we don't get the signal handler run without it. (ANDROID) } } native static void interruptPy(int signal); native static void acknowledgePy(); } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/JPypeUtilities.java000066400000000000000000000057101501674766700270150ustar00rootroot00000000000000package org.jpype; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandleProxies; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.function.Predicate; public class JPypeUtilities { // a functional interface can only re-declare a public non-final method from Object // this should end up being an array of equals, hashCode and toString private static final Method[] OBJECT_METHODS = Arrays.stream(Object.class.getMethods()) .filter(m -> !Modifier.isFinal(m.getModifiers())) .toArray(Method[]::new); private static final Predicate isSealed; static { Predicate result = null; try { Method m = Class.class.getMethod("isSealed"); MethodHandle handle = MethodHandles.publicLookup().unreflect(m); result = MethodHandleProxies.asInterfaceInstance(Predicate.class, handle); } catch (IllegalAccessException e) { // it's a public method so this should never occur throw new IllegalAccessError(e.getMessage()); } catch (NoSuchMethodException e) { // if isSealed doesn't exist then neither do sealed classes result = c -> false; } isSealed = result; } public static Path getJarPath(Class c) { try { return Paths.get(c.getProtectionDomain().getCodeSource().getLocation() .toURI()).getParent(); } catch (URISyntaxException ex) { return null; } } public static Method getFunctionalInterfaceMethod(Class cls) { if (!cls.isInterface() || cls.isAnnotation() || isSealed.test(cls)) return null; Method result = null; for (Method m : cls.getMethods()) { if (Modifier.isAbstract(m.getModifiers())) { if (isObjectMethodOverride(m)) continue; if (result != null && !equals(m, result)) return null; if (result == null || cls.equals(m.getDeclaringClass())) result = m; } } return result; } private static boolean isObjectMethodOverride(Method m) { for (Method objectMethod : OBJECT_METHODS) { if (equals(m, objectMethod)) return true; } return false; } private static boolean equals(Method a, Method b) { // this should be the fastest possible short circuit if (a.getParameterCount() != b.getParameterCount()) return false; if (!a.getName().equals(b.getName())) return false; // if the return types are different it wouldn't compile // parameters must be exactly the same and may not be an extended class if (!Arrays.equals(a.getParameterTypes(), b.getParameterTypes())) return false; // if declared exceptions were different it wouldn't compile // if it did compile then it is an override return true; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/PyExceptionProxy.java000066400000000000000000000003351501674766700274010ustar00rootroot00000000000000package org.jpype; /** * * @author nelson85 */ public class PyExceptionProxy extends RuntimeException { long cls; long value; public PyExceptionProxy(long l0, long l1) { cls = l0; value = l1; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/000077500000000000000000000000001501674766700241705ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/AttrGrammar.java000066400000000000000000000127061501674766700272620ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.html; import org.jpype.html.Parser.Entity; import org.jpype.html.Parser.Rule; import org.w3c.dom.Attr; public class AttrGrammar implements Parser.Grammar { static final AttrGrammar INSTANCE = new AttrGrammar(); private AttrGrammar() { } @Override public void start(Parser p) { p.state = State.FREE; ((AttrParser) p).attrs.clear(); } @Override public Object end(Parser p) { return ((AttrParser) p).attrs; } // enum Token implements Parser.Token { TEXT, QUOTE("\""), SQUOTE("'"), EQ("="), WHITESPACE(" "); byte value; String text; Token() { } Token(String s) { text = s; if (s.length() == 1) value = (byte) s.charAt(0); } @Override final public boolean matches(byte b) { if (value == ' ') return Character.isWhitespace(b); if (value == 0) return true; return b == value; } @Override public boolean runs() { return this == Token.TEXT; } @Override public String toString() { if (text != null) return text; return "TEXT"; } } final static Token[] freeTokens = tokens( Token.QUOTE, Token.SQUOTE, Token.EQ, Token.WHITESPACE, Token.TEXT); final static Token[] qtTokens = tokens( Token.QUOTE, Token.TEXT); final static Token[] sqtTokens = tokens( Token.SQUOTE, Token.TEXT); final static Rule ignoreWS = new IgnoreWSRule(); final static Rule quoteRule = new QuoteRule(); final static Rule endRule = new EndQuoteRule(); final static Rule attrRule = new AttrRule(); final static Rule boolRule = new BooleanRule(); final static Rule[] freeRules = rules(attrRule, boolRule, ignoreWS, quoteRule); final static Rule[] qtRules = rules(endRule); static Token[] tokens(Token... t) { return t; } static Rule[] rules(Rule... t) { return t; } // // enum State implements Parser.State { FREE(freeTokens, freeRules), IN_QUOTE(qtTokens, qtRules), IN_SQUOTE(sqtTokens, qtRules); Token[] tokens; Rule[] rules; State(Token[] tokens, Rule[] rules) { this.tokens = tokens; this.rules = rules; } @Override public Token[] getTokens() { return this.tokens; } @Override public Rule[] getRules() { return this.rules; } } // // static class IgnoreWSRule implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token != Token.WHITESPACE) return false; parser.stack.removeLast(); return true; } } static class QuoteRule implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token == Token.QUOTE) { parser.state = State.IN_QUOTE; parser.stack.removeLast(); return true; } if (entity.token == Token.SQUOTE) { parser.state = State.IN_SQUOTE; parser.stack.removeLast(); return true; } return false; } } static class EndQuoteRule implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (State.IN_QUOTE == parser.state && entity.token == Token.QUOTE) { parser.state = State.FREE; parser.stack.removeLast(); return true; } if (State.IN_SQUOTE == parser.state && entity.token == Token.SQUOTE) { parser.state = State.FREE; parser.stack.removeLast(); return true; } return false; } } static class AttrRule extends Parser.MatchRule { AttrRule() { super(Token.TEXT, Token.EQ, Token.TEXT); } @Override public void execute(Parser parser) { Entity e2 = (Entity) parser.stack.removeLast(); Entity e1 = (Entity) parser.stack.removeLast(); Entity e0 = (Entity) parser.stack.removeLast(); AttrParser aparser = (AttrParser) parser; Attr attr = aparser.doc.createAttribute((String) e0.value); attr.setNodeValue((String) e2.value); aparser.attrs.add(attr); } } static class BooleanRule extends Parser.MatchRule { BooleanRule() { super(Token.TEXT, Token.TEXT); } @Override public void execute(Parser parser) { Entity e2 = (Entity) parser.stack.removeLast(); Entity e1 = (Entity) parser.stack.removeLast(); AttrParser aparser = (AttrParser) parser; Attr attr = aparser.doc.createAttribute((String) e1.value); attr.setNodeValue((String) e1.value); aparser.attrs.add(attr); parser.stack.add(e2); } } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/AttrParser.java000066400000000000000000000020761501674766700271270ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.html; import java.util.ArrayList; import java.util.List; import org.w3c.dom.Attr; import org.w3c.dom.Document; public class AttrParser extends Parser> { final Document doc; final List attrs = new ArrayList<>(); public AttrParser(Document doc) { super(AttrGrammar.INSTANCE); this.doc = doc; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/Html.java000066400000000000000000000127261501674766700257470ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.html; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.jpype.JPypeContext; import org.w3c.dom.Attr; import org.w3c.dom.Document; public class Html { public final static HashSet VOID_ELEMENTS = new HashSet<>(); public final static HashSet OPTIONAL_ELEMENTS = new HashSet<>(); public final static HashSet OPTIONAL_CLOSE = new HashSet<>(); static { VOID_ELEMENTS.addAll(Arrays.asList( "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr")); OPTIONAL_ELEMENTS.addAll(Arrays.asList("html", "head", "body", "p", "dt", "dd", "li", "option", "thead", "th", "tbody", "tr", "td", "tfoot", "colgroup")); OPTIONAL_CLOSE.addAll(Arrays.asList("li:li", "dt:dd", "p:address", "p:article", "p:aside", "p:blockquote", "p:details", "p:div", "p:dl", "p:fieldset", "p:figcaption", "p:figure", "p:footer", "p:form", "p:h1", "p:h2", "p:h3", "p:h4", "p:h5", "p:h6", "p:header", "p:hgroup", "p:hr", "p:main", "p:menu", "p:nav", "p:ol", "p:p", "p:pre", "p:section", "p:table", "p:ul", "dd:dt", "dd:dd", "dt:dt", "dt:dd", "rt:rt", "rt:rp", "rp:rt", "rp:rp", "optgroup:optgroup", "option:option", "option:optiongroup", "thread:tbody", "thread:tfoot", "tbody:tfoot", "tbody:tbody", "tr:tr", "td:td", "td:th", "th:td", "p:li")); } public static Parser newParser() { return new HtmlParser(); } public static List parseAttributes(Document doc, String str) { AttrParser p = new AttrParser(doc); p.parse(str); return p.attrs; } // public static Map ENTITIES = new HashMap<>(); static { ClassLoader cl = ClassLoader.getSystemClassLoader(); try (InputStream is = cl.getResourceAsStream("org/jpype/html/entities.txt"); InputStreamReader isr = new InputStreamReader(is); BufferedReader rd = new BufferedReader(isr)) { while (true) { String line = rd.readLine(); if (line == null) break; if (line.startsWith("#")) continue; String[] parts = line.split("\\s+"); ENTITIES.put(parts[0], Integer.parseInt(parts[1])); } } catch (IOException ex) { throw new RuntimeException(ex); } } public static String decode(String s) { if (!s.contains("&")) return s; int dead = 0; byte[] b = s.getBytes(StandardCharsets.UTF_8); for (int i = 0; i < b.length; ++i) { if (b[i] != '&') continue; int i1 = i; int i2 = i + 1; if (i2 == b.length) break; if (b[i2] == '#') { // Try to be robust when there is no ; for (i = i2 + 1; i < b.length; ++i) { if (!Character.isDigit(b[i])) break; } } else { for (i = i2; i < b.length; ++i) { if (b[i] == ';') break; } } int i3 = i; int c = 0; if (b[i2] == '#') { i2++; try { c = Integer.parseInt(new String(b, i2, i3 - i2, StandardCharsets.UTF_8)); } catch (NumberFormatException ex) { } } else { String e = new String(b, i2, i3 - i2, StandardCharsets.UTF_8); Integer c2 = ENTITIES.get(e); if (c2 == null) throw new RuntimeException("Bad entity " + e); c = c2; } // Substitute if (c < 128) { b[i1++] = (byte) c; } else if (c < 0x0800) { b[i1++] = (byte) (0xc0 + ((c >> 6) & 0x1f)); if (i1 < b.length) // lgtm [java/constant-comparison] b[i1++] = (byte) (0x80 + (c & 0x3f)); // lgtm [java/index-out-of-bounds] } else { b[i1++] = (byte) (0xe0 + ((c >> 12) & 0x0f)); if (i1 < b.length) // lgtm [java/constant-comparison] b[i1++] = (byte) (0x80 + ((c >> 6) & 0x3f)); // lgtm [java/index-out-of-bounds] if (i1 < b.length) b[i1++] = (byte) (0x80 + (c & 0x3f)); } if (i3 < b.length && b[i3] == ';') i3++; dead += i3 - i1; for (; i1 < i3; ++i1) b[i1] = 0; i = i3; } int j = 0; byte[] b2 = new byte[b.length - dead]; for (int i = 0; i < b.length; ++i) { if (b[i] != 0) b2[j++] = b[i]; } return new String(b2, StandardCharsets.UTF_8); } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/HtmlGrammar.java000066400000000000000000000365571501674766700272660ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.html; import java.util.LinkedList; import org.jpype.html.Parser.Entity; import org.jpype.html.Parser.Rule; public class HtmlGrammar implements Parser.Grammar { final static HtmlGrammar INSTANCE = new HtmlGrammar(); private HtmlGrammar() { } @Override public void start(Parser p) { p.state = State.FREE; ((HtmlParser) p).handler.startDocument(); } @Override public Object end(Parser p) { ((HtmlParser) p).handler.endDocument(); return ((HtmlParser) p).handler.getResult(); } // BeginElement does < parser) { if (parser.stack.isEmpty()) return; Entity last = parser.stack.removeLast(); StringBuilder s = new StringBuilder(); for (Entity e : parser.stack) { s.append(e.toString()); } ((HtmlParser) parser).handler.text(s.toString()); parser.stack.clear(); parser.stack.add(last); } // enum State implements Parser.State { FREE(freeTokens, freeRules), ELEMENT(elementTokens, elementRules), DIRECTIVE(directiveTokens, directiveRules), CDATA(cdataTokens, cdataRules), COMMENT(commentTokens, commentRules); Token[] tokens; Rule[] rules; State(Token[] tokens, Rule[] rules) { this.tokens = tokens; this.rules = rules; } @Override public Token[] getTokens() { return this.tokens; } @Override public Rule[] getRules() { return this.rules; } } // // enum Token implements Parser.Token { TEXT, BANG("!"), DASH("-"), LT("<"), GT(">"), SLASH("/"), AMP("&"), SEMI(";"), LSB("["), RSB("]"), CLOSE(" // final static Rule escaped = new Escaped(); final static Rule slash = new Cleanup(); final static Rule mergeText = new MergeText(); final static Rule beginElement = new BeginElement(); final static Rule startElement = new StartElement(); final static Rule completeElement = new CompleteElement(); final static Rule endElement = new EndElement(); final static Rule quote = new StartQuote(); final static Token[] freeTokens = tokens( Token.LT, Token.AMP, Token.SEMI, Token.TEXT); final static Token[] elementTokens = tokens( Token.BANG, Token.AMP, Token.LT, Token.SEMI, Token.SLASH, Token.GT, Token.QUOTE, Token.SQUOTE, Token.TEXT); final static Token[] directiveTokens = tokens( Token.DASH, Token.LSB, Token.RSB, Token.LT, Token.GT, Token.QUOTE, Token.SQUOTE, Token.TEXT); final static Token[] cdataTokens = tokens( Token.LSB, Token.RSB, Token.GT, Token.TEXT); final static Token[] commentTokens = tokens( Token.DASH, Token.GT, Token.TEXT); final static Token[] quoteTokens = tokens( Token.QUOTE, Token.TEXT); final static Token[] squoteTokens = tokens( Token.SQUOTE, Token.TEXT); final static Rule[] freeRules = rules( beginElement, escaped, mergeText); final static Rule[] elementRules = rules( mergeText, quote, startElement, endElement, completeElement, escaped, slash); final static Rule[] directiveRules = rules(quote, new EndDirective(), mergeText); final static Rule[] cdataRules = rules( new EndCData()); final static Rule[] commentRules = rules( new EndComment(), new StartComment()); private static Rule[] rules(Rule... t) { return t; } private static Token[] tokens(Token... t) { return t; } // // private static class Cleanup implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token == Token.SLASH) { parser.lookahead = this::next; return false; } if (entity.token == Token.GT && parser.stack.size() > 4) { // This it to help debug a rare problem for (Entity e : parser.stack) { System.out.print(e.token); System.out.print("("); System.out.print(e.value); System.out.print(") "); } System.out.println(); throw new RuntimeException("Need cleanup"); } return false; } private boolean next(Parser parser, Entity entity) { if (entity.token != Token.GT) { parser.stack.removeLast(); parser.stack.removeLast(); // parser.stack.getLast().token = Token.TEXT; entity.value = "/" + entity.value; parser.stack.add(entity); } return false; } } private static class Escaped extends Parser.MatchRule { public Escaped() { super(Token.AMP, Token.TEXT, Token.SEMI); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; Entity e2 = stack.removeLast(); Entity e1 = stack.removeLast(); // Currently we do not verify the text contents Entity e0 = stack.getLast(); promote(e0).append(e1.toString()).append(e2.toString()); } } static class MergeText extends Parser.MatchRule { public MergeText() { super(Token.TEXT, Token.TEXT); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; Entity t2 = stack.removeLast(); Entity t1 = stack.getLast(); promote(t1).append(t2.toString()); } } // // static class BeginElement implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token != Token.LT) return false; getGrammar(parser).flushText(parser); parser.state = State.ELEMENT; parser.lookahead = this::next; return true; } public boolean next(Parser parser, Parser.Entity entity) { if (entity.token == Token.SLASH) { parser.stack.removeLast(); parser.stack.getLast().token = Token.CLOSE; return true; } if (entity.token == Token.BANG) { parser.stack.removeLast(); Entity last = parser.stack.getLast(); last.token = Token.DECL_DIRECTIVE; parser.lookahead = new Directive(); parser.state = State.DIRECTIVE; return true; } return false; } } static class StartElement extends Parser.MatchRule { StartElement() { super(Token.LT, Token.TEXT, Token.GT); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; stack.removeLast(); Entity e1 = stack.removeLast(); stack.removeLast(); String content = e1.value.toString(); getGrammar(parser).flushText(parser); String[] parts = content.split("\\s+", 2); if (parts.length == 1) getHandler(parser).startElement(content, null); else getHandler(parser).startElement(parts[0], parts[1]); parser.state = State.FREE; } } static class CompleteElement extends Parser.MatchRule { CompleteElement() { super(Token.LT, Token.TEXT, Token.SLASH, Token.GT); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; stack.removeLast(); // > stack.removeLast(); // / Entity e1 = stack.removeLast(); stack.removeLast(); // < String content = e1.value.toString(); stack.clear(); int i = content.indexOf(" "); if (i == -1) { getHandler(parser).startElement(content, null); getHandler(parser).endElement(content); } else { String name = content.substring(0, i); String attr = content.substring(i).trim(); getHandler(parser).startElement(name, attr); getHandler(parser).endElement(name); } parser.state = State.FREE; } } static class EndElement extends Parser.MatchRule { EndElement() { super(Token.CLOSE, Token.TEXT, Token.GT); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; Entity e2 = stack.removeLast(); Entity e1 = stack.removeLast(); Entity e0 = stack.removeLast(); String content = e1.value.toString(); getGrammar(parser).flushText(parser); getHandler(parser).endElement(content); parser.state = State.FREE; } } static class EndDirective extends Parser.MatchRule { EndDirective() { super(Token.DECL_DIRECTIVE, Token.TEXT, Token.GT); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; Entity e2 = stack.removeLast(); Entity e1 = stack.removeLast(); Entity e0 = stack.removeLast(); String content = e1.value.toString(); getGrammar(parser).flushText(parser); getHandler(parser).directive(content); parser.state = State.FREE; } } // // // This is a look ahead rule static class Directive implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token == Token.LSB) { parser.lookahead = new CData(); return true; } if (entity.token == Token.DASH) { parser.lookahead = new Comment(); return true; } return false; } } static class CData implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token != Token.TEXT) parser.error("Expected CDATA"); parser.lookahead = this::next; return true; } public boolean next(Parser parser, Parser.Entity entity) { if (entity.token != Token.LSB) parser.error("Expected ["); parser.stack.clear(); parser.state = State.CDATA; return true; } } static class EndCData extends Parser.MatchRule { public EndCData() { super(Token.RSB, Token.RSB, Token.GT); } @Override public void execute(Parser parser) { LinkedList stack = parser.stack; stack.removeLast(); // > stack.removeLast(); // ] stack.removeLast(); // ] Entity first = stack.removeFirst(); StringBuilder sb = promote(first); for (Entity e : stack) { sb.append(e.toString()); } stack.clear(); ((HtmlParser) parser).handler.cdata(sb.toString()); parser.state = State.FREE; } } static class Comment implements Rule { @Override public boolean apply(Parser parser, Entity entity) { if (entity.token != Token.DASH) parser.error("Expected -"); parser.lookahead = this::next; parser.stack.clear(); parser.state = State.COMMENT; return true; } public boolean next(Parser parser, Parser.Entity entity) { if (entity.token == Token.DASH) parser.error("Bad comment(-)"); if (entity.token == Token.GT) parser.error("Bad comment(>)"); return false; } } static class StartComment extends Parser.MatchRule { public StartComment() { super(Token.LT, Token.BANG, Token.DASH, Token.DASH); } @Override public void execute(Parser parser) { parser.lookahead = this::next; } public boolean next(Parser parser, Parser.Entity entity) { if (entity.token == Token.GT) return false; parser.error("Comment contains "); } private void writeCData(CDATASection cData) throws IOException { writer.write(""); } private void writeText(Text text) throws IOException { writer.write(text.getData()); } @Override public void close() throws IOException { writer.close(); } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/Parser.java000066400000000000000000000145721501674766700263000ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.html; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.util.LinkedList; import java.util.ListIterator; /** * Generic document parser. * * @param */ public class Parser { final Grammar grammar; public State state = null; public Token last = null; public Rule lookahead = null; public LinkedList stack = new LinkedList<>(); Parser(Grammar grammar) { this.grammar = grammar; } public T parse(InputStream is) { ByteBuffer incoming = ByteBuffer.allocate(1024); ByteBuffer outgoing = ByteBuffer.allocate(1024); ReadableByteChannel channel = Channels.newChannel(is); stack.clear(); grammar.start(this); try { while (true) { incoming.position(0); int rc = channel.read(incoming); if (rc < 0) break; int p = incoming.position(); incoming.rewind(); process(incoming, outgoing, rc); } flushTokens(outgoing); } catch (IOException ex) { throw new RuntimeException(ex); } return (T) grammar.end(this); } public T parse(String str) { byte[] b = str.getBytes(); ByteBuffer incoming = ByteBuffer.wrap(b); ByteBuffer outgoing = ByteBuffer.allocate(1024); stack.clear(); grammar.start(this); process(incoming, outgoing, b.length); flushTokens(outgoing); return (T) grammar.end(this); } private void process(ByteBuffer incoming, ByteBuffer outgoing, int rc) { while (incoming.position() < rc) { byte b = incoming.get(); Token match = null; for (Token t : state.getTokens()) { if (t.matches(b)) { match = t; break; } } if (match == null) this.error("Unable to parse " + (char) b); else if (match.runs()) { if (last != match) flushTokens(outgoing); if (!outgoing.hasRemaining()) flushTokens(outgoing); outgoing.put(b); } else { if (outgoing.position() > 0) flushTokens(outgoing); processToken(match, null); } last = match; } } /** * Send all the queue up text to a token. */ private void flushTokens(ByteBuffer outgoing) { if (outgoing.position() == 0) return; processToken(last, new String(outgoing.array(), 0, outgoing.position())); outgoing.rewind(); } /** * Process a token. * * This will add it to the stack and then match the stack with the nearest * rule. * * @param token * @param value */ protected void processToken(Token token, String value) { if (token == null) return; Entity entity = add(token, value); // Take the next lookahead Rule rule1 = this.lookahead; this.lookahead = null; if (rule1 != null) { // System.out.println(" FORWARD " + rule1); if (rule1.apply(this, entity)) { return; } } // If not handled then proceed to rules. boolean done = false; while (!done && !stack.isEmpty()) { done = true; for (Rule rule : state.getRules()) { // System.out.println(" RULE " + rule); if (rule.apply(this, stack.getLast())) { done = false; break; } } } } /** * Add a token to the token stack * * @param token * @param object * @return */ public Entity add(Token token, String object) { Entity entity = new Entity(token, object); this.stack.add(entity); return entity; } public void error(String bad_token) { throw new RuntimeException("bad_token"); } public interface State { Token[] getTokens(); Rule[] getRules(); } public interface Token { int ordinal(); public boolean matches(byte b); public boolean runs(); } public interface Rule { boolean apply(Parser parser, Entity entity); } public interface Grammar { /** * Should set the initial state. * * @param p */ public void start(Parser p); /** * Should check the state of the stack, fail if bad, or return the final * object if good. * * @param p * @return */ public Object end(Parser p); } /** * Token or text. */ public static class Entity { public Token token; public Object value; private Entity(Token token) { this.token = token; } private Entity(Token token, String value) { this.token = token; this.value = value; } @Override public String toString() { if (value == null) return token.toString(); return value.toString(); } } // /** * Generic matcher for multiple tokens on the stack. */ abstract static class MatchRule implements Rule { Token[] pattern; MatchRule(Token... tokens) { this.pattern = tokens; } @Override public boolean apply(Parser parser, Entity entity) { LinkedList stack = parser.stack; int n = stack.size(); if (n < pattern.length) return false; ListIterator iter = stack.listIterator(stack.size()); for (int i = 0; i < pattern.length; ++i) { if (!iter.hasPrevious()) return false; Entity next = iter.previous(); if (next.token != pattern[pattern.length - i - 1]) { return false; } } execute(parser); return true; } abstract public void execute(Parser parser); } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/html/entities.txt000066400000000000000000000070401501674766700265560ustar00rootroot00000000000000# Portions Β© International Organization for Standardization 1986: # Permission to copy in any form is granted for use with # conforming SGML systems and applications as defined in # ISO 8879, provided this notice is included in all copies. nbsp 160 iexcl 161 cent 162 pound 163 curren 164 yen 165 brvbar 166 sect 167 uml 168 copy 169 ordf 170 laquo 171 not 172 shy 173 reg 174 macr 175 deg 176 plusmn 177 sup2 178 sup3 179 acute 180 micro 181 para 182 middot 183 cedil 184 sup1 185 ordm 186 raquo 187 frac14 188 frac12 189 frac34 190 iquest 191 Agrave 192 Aacute 193 Acirc 194 Atilde 195 Auml 196 Aring 197 AElig 198 Ccedil 199 Egrave 200 Eacute 201 Ecirc 202 Euml 203 Igrave 204 Iacute 205 Icirc 206 Iuml 207 ETH 208 Ntilde 209 Ograve 210 Oacute 211 Ocirc 212 Otilde 213 Ouml 214 times 215 Oslash 216 Ugrave 217 Uacute 218 Ucirc 219 Uuml 220 Yacute 221 THORN 222 szlig 223 agrave 224 aacute 225 acirc 226 atilde 227 auml 228 aring 229 aelig 230 ccedil 231 egrave 232 eacute 233 ecirc 234 euml 235 igrave 236 iacute 237 icirc 238 iuml 239 eth 240 ntilde 241 ograve 242 oacute 243 ocirc 244 otilde 245 ouml 246 divide 247 oslash 248 ugrave 249 uacute 250 ucirc 251 uuml 252 yacute 253 thorn 254 yuml 255 fnof 402 Alpha 913 Beta 914 Gamma 915 Delta 916 Epsilon 917 Zeta 918 Eta 919 Theta 920 Iota 921 Kappa 922 Lambda 923 Mu 924 Nu 925 Xi 926 Omicron 927 Pi 928 Rho 929 Sigma 931 Tau 932 Upsilon 933 Phi 934 Chi 935 Psi 936 Omega 937 alpha 945 beta 946 gamma 947 delta 948 epsilon 949 zeta 950 eta 951 theta 952 iota 953 kappa 954 lambda 955 mu 956 nu 957 xi 958 omicron 959 pi 960 rho 961 sigmaf 962 sigma 963 tau 964 upsilon 965 phi 966 chi 967 psi 968 omega 969 thetasym 977 upsih 978 piv 982 bull 8226 hellip 8230 prime 8242 Prime 8243 oline 8254 frasl 8260 weierp 8472 image 8465 real 8476 trade 8482 alefsym 8501 larr 8592 uarr 8593 rarr 8594 darr 8595 harr 8596 crarr 8629 lArr 8656 uArr 8657 rArr 8658 dArr 8659 hArr 8660 forall 8704 part 8706 exist 8707 empty 8709 nabla 8711 isin 8712 notin 8713 ni 8715 prod 8719 sum 8721 minus 8722 lowast 8727 radic 8730 prop 8733 infin 8734 ang 8736 and 8743 or 8744 cap 8745 cup 8746 int 8747 there4 8756 sim 8764 cong 8773 asymp 8776 ne 8800 equiv 8801 le 8804 ge 8805 sub 8834 sup 8835 nsub 8836 sube 8838 supe 8839 oplus 8853 otimes 8855 perp 8869 sdot 8901 lceil 8968 rceil 8969 lfloor 8970 rfloor 8971 lang 9001 rang 9002 loz 9674 spades 9824 clubs 9827 hearts 9829 diams 9830 quot 34 amp 38 lt 60 gt 62 OElig 338 oelig 339 Scaron 352 scaron 353 Yuml 376 circ 710 tilde 732 ensp 8194 emsp 8195 thinsp 8201 zwnj 8204 zwj 8205 lrm 8206 rlm 8207 ndash 8211 mdash 8212 lsquo 8216 rsquo 8217 sbquo 8218 ldquo 8220 rdquo 8221 bdquo 8222 dagger 8224 Dagger 8225 permil 8240 lsaquo 8249 rsaquo 8250 euro 8364jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/000077500000000000000000000000001501674766700246335ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/DomUtilities.java000066400000000000000000000146231501674766700301170ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.javadoc; /** * The usual set of method required to work on DOM. * * DOM leaves a lot of basic stuff incomplete. */ import java.util.function.BiConsumer; import java.util.function.Consumer; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; public class DomUtilities { /** * Traverse all children in depth first order applying an operation. * * This is hardened against some level of DOM changes. * * @param node * @param operator * @param type */ public static void traverseDFS(Node node, Consumer operator, short type) { Node child = node.getFirstChild(); while (child != null) { // Get a referent to what we are processing next in case the tree changes. Node next = child.getNextSibling(); // Apply transforms to children first if (child.getNodeType() == Node.ELEMENT_NODE) traverseDFS(child, operator, type); // Then process the outer element if (child.getNodeType() == type) operator.accept(child); // Proceed child = next; } } /** * Traverse the children of a node applying an operation. * * This is hardened against some level of DOM changes. * * @param node * @param operator * @param type */ public static void traverseChildren(Node node, Consumer operator, short type) { Node child = node.getFirstChild(); while (child != null) { // Get the next node to process in case this one is changed or removed. Node next = child.getNextSibling(); if (child.getNodeType() == type) operator.accept(child); // Proceed. child = next; } } /** * Traverse all children in depth first order applying an operation. * * This is hardened against some level of DOM changes. * * @param node * @param operator * @param type */ public static void traverseDFS(Node node, BiConsumer operator, short type, T data) { Node child = node.getFirstChild(); while (child != null) { // Get a referent to what we are processing next in case the tree changes. Node next = child.getNextSibling(); // Apply transforms to children first if (child.getNodeType() == Node.ELEMENT_NODE) traverseDFS(child, operator, type, data); // Then process the outer element if (child.getNodeType() == type) operator.accept(child, data); // Proceed child = next; } } /** * Traverse the children of a node applying an operation. * * This is hardened against some level of DOM changes. * * @param node * @param operator * @param type */ public static void traverseChildren(Node node, BiConsumer operator, short type, T data) { Node child = node.getFirstChild(); while (child != null) { // Get the next node to process in case this one is changed or removed. Node next = child.getNextSibling(); if (child.getNodeType() == type) operator.accept(child, data); // Proceed. child = next; } } /** * Remove all attributes from a node. * * @param node */ public static void clearAttributes(Node node) { while (node.getAttributes().getLength() > 0) { Node att = node.getAttributes().item(0); node.getAttributes().removeNamedItem(att.getNodeName()); } } /** * Remove all children from a node. * * @param node */ public static void clearChildren(Node node) { while (node.hasChildNodes()) node.removeChild(node.getFirstChild()); } /** * Determine if a block contains a new line. * * @param n * @return */ public static boolean containsNL(Node n) { Node child = n.getFirstChild(); while (child != null) { if (child.getNodeType() == Node.TEXT_NODE) { if (child.getNodeValue().contains("\n")) return true; } child = child.getNextSibling(); } return false; } /** * Combine all text with neighbors in immediate children. * * @param node */ public static void combineText(Node node) { // merge text nodes Node child = node.getFirstChild(); while (child != null) { Node next = child.getNextSibling(); if (child.getNodeType() != Node.TEXT_NODE) { child = next; continue; } if (next != null && next.getNodeType() == Node.TEXT_NODE) { child.setTextContent(child.getNodeValue() + next.getNodeValue()); child.getParentNode().removeChild(next); continue; } child = next; } } /** * Merge the contents of a node with its parent. * * @param parent * @param node */ public static void mergeNode(Node parent, Node node) { while (node.hasChildNodes()) { parent.insertBefore(node.getFirstChild(), node); } parent.removeChild(node); } static void transferContents(Node dest, Node source) { while (source.hasChildNodes()) { dest.appendChild(source.getFirstChild()); } } /** * Traverse a node and replaces all extra whitespace with one space. * * This should be applied to any element where white space is not relevant. * * @param node */ public static void removeWhitespace(Node node) { // merge text nodes NodeList children = node.getChildNodes(); for (int i = 0; i < children.getLength(); ++i) { Node child = children.item(i); if (child.getNodeType() != Node.TEXT_NODE) continue; Text t = (Text) child; String c = t.getNodeValue(); if (c != null) { c = c.replaceAll("\\s+", " "); t.setNodeValue(c); } } } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/Javadoc.java000066400000000000000000000023561501674766700270530ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.javadoc; import java.util.HashMap; import java.util.List; import java.util.Map; import org.w3c.dom.Node; public class Javadoc { public String description; public String ctors; public Map methods = new HashMap<>(); public Map fields = new HashMap<>(); // These will be removed once debugging is complete public Node descriptionNode; public List ctorsNode; public List methodNodes; public List innerNode; public List fieldNodes; } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/JavadocException.java000066400000000000000000000017101501674766700307230ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.javadoc; import org.w3c.dom.Node; public class JavadocException extends RuntimeException { public Node node; public JavadocException(Node node, Throwable th) { super(th); this.node = node; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/JavadocExtractor.java000066400000000000000000000221361501674766700307450ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.javadoc; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.jpype.html.Html; import org.jpype.html.Parser; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class JavadocExtractor { static final JavadocTransformer transformer = new JavadocTransformer(); static public boolean transform = true; static public boolean render = true; static public boolean failures = false; /** * Search the classpath for documentation. * * @param cls * @return */ public static Javadoc getDocumentation(Class cls) { try { try (InputStream is = getDocumentationAsStream(cls)) { if (is != null) { Parser parser = Html.newParser(); return extractDocument(cls, parser.parse(is)); } } } catch (Exception ex) { System.err.println("Failed to extract javadoc for " + cls + ", caught " + ex); if (failures) throw new RuntimeException(ex); } return null; } public static InputStream getDocumentationAsStream(Class cls) { InputStream is = null; String name = cls.getName().replace('.', '/') + ".html"; ClassLoader cl = ClassLoader.getSystemClassLoader(); // Search the regular class path. is = cl.getResourceAsStream(name); if (is != null) return is; // Search for api documents String name1 = "docs/api/" + name; is = cl.getResourceAsStream(name1); if (is != null) return is; // If we are dealing with Java 9+, the doc tree is different try { Method meth = Class.class.getMethod("getModule"); String module = meth.invoke(cls).toString().substring(7); String name2 = "docs/api/" + module + "/" + name; is = cl.getResourceAsStream(name2); if (is != null) return is; } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { // do nothing if we are not JDK 9+ } return null; } /** * Extract the documentation from the dom. * * @param cls is the class being processed. * @param doc is the DOM holding the javadoc. * @return */ public static Javadoc extractDocument(Class cls, Document doc) { JavadocRenderer renderer = new JavadocRenderer(); try { Javadoc documentation = new Javadoc(); XPath xPath = XPathFactory.newInstance().newXPath(); // Javadoc 8-13 Node n = (Node) xPath.compile("//div[@class='description']/ul/li").evaluate(doc, XPathConstants.NODE); if (n == null) { // Javadoc 14+ n = (Node) xPath.compile("//section[@class='description']").evaluate(doc, XPathConstants.NODE); } if (n == null) { // Javadoc 17+ n = (Node) xPath.compile("//section[@class='class-description']").evaluate(doc, XPathConstants.NODE); } Node description = toFragment(n); if (description != null) { documentation.descriptionNode = description; if (transform) transformer.transformDescription(cls, description); if (render) documentation.description = renderer.render(description); } Node ctorRoot = ((Node) xPath.compile("//li/a[@name='constructor.detail' or @id='constructor.detail']") // Javadoc before 17 .evaluate(doc, XPathConstants.NODE)); if (ctorRoot == null) { // Javadoc 17+ ctorRoot = ((Node) xPath.compile("//section[@class='constructor-details']/ul") .evaluate(doc, XPathConstants.NODE)); } if (ctorRoot != null) { List set = convertNodes((NodeList) xPath.compile("./li/section") // Javadoc 17+ .evaluate(ctorRoot, XPathConstants.NODESET)); if (set.isEmpty()) { // Javadoc before 17 set = convertNodes((NodeList) xPath.compile("./ul/li") .evaluate(ctorRoot.getParentNode(), XPathConstants.NODESET)); } documentation.ctorsNode = set; StringBuilder sb = new StringBuilder(); for (Node ctor : set) { if (transform) transformer.transformMember(cls, ctor); if (render) sb.append(renderer.render(ctor)); } documentation.ctors = sb.toString(); } Node methodRoot = ((Node) xPath.compile("//li/a[@name='method.detail' or @id='method.detail']") // Javadoc before 17 .evaluate(doc, XPathConstants.NODE)); if (methodRoot == null) { // Javadoc 17+ methodRoot = ((Node) xPath.compile("//section[@class='method-details']/ul") .evaluate(doc, XPathConstants.NODE)); } if (methodRoot != null) { List set = convertNodes((NodeList) xPath.compile("./li/section") // Javadoc 17+ .evaluate(methodRoot, XPathConstants.NODESET)); if (set.isEmpty()) { // Javadoc before 17 set = convertNodes((NodeList) xPath.compile("./ul/li") .evaluate(methodRoot.getParentNode(), XPathConstants.NODESET)); } documentation.methodNodes = set; for (Node method : set) { if (transform) transformer.transformMember(cls, method); if (render) { String str = renderer.render(method); String name = renderer.memberName; if (documentation.methods.containsKey(name)) { String old = documentation.methods.get(name); str = old + str; } documentation.methods.put(name, str); } } } // Node inner = (Node) xPath.compile("//li/a[@name='nested_class_summary']").evaluate(doc, XPathConstants.NODE); // if (inner != nullList) // { // NodeList set = (NodeList) xPath.compile("./ul/li").evaluate(inner.getParentNode(), XPathConstants.NODESET); // documentation.innerNode = convertNodes(set); // } Node fieldRoot = ((Node) xPath.compile("//li/a[@name='field.detail' or @id='field.detail']") // Javadoc before 17 .evaluate(doc, XPathConstants.NODE)); if (fieldRoot == null) { // Javadoc 17+ fieldRoot = ((Node) xPath.compile("//section[@class='field-details']/ul") .evaluate(doc, XPathConstants.NODE)); } if (fieldRoot != null) { List set = convertNodes((NodeList) xPath.compile("./li/section") // Javadoc 17+ .evaluate(fieldRoot, XPathConstants.NODESET)); if (set.isEmpty()) { // Javadoc before 17 set = convertNodes((NodeList) xPath.compile("./ul/li") .evaluate(fieldRoot.getParentNode(), XPathConstants.NODESET)); } documentation.fieldNodes = set; for (Node field : set) { if (transform) transformer.transformMember(cls, field); if (render) { String str = renderer.render(field); String name = renderer.memberName; documentation.fields.put(name, str); } } } return documentation; } catch (IOException | XPathExpressionException ex) { throw new RuntimeException(ex); // return null; } } private static List convertNodes(NodeList nl) throws IOException { List out = new ArrayList<>(); for (int i = 0; i < nl.getLength(); ++i) { out.add(toFragment(nl.item(i))); } return out; } /** * Convert a portion of the document into a fragment. * * @param node * @return */ public static Node toFragment(Node node) { Document doc = node.getOwnerDocument(); DocumentFragment out = doc.createDocumentFragment(); while (node.hasChildNodes()) { out.appendChild(node.getFirstChild()); } if (out.getFirstChild() != null && out.getFirstChild().getNodeType() == Node.TEXT_NODE) out.removeChild(out.getFirstChild()); if (out.getLastChild() != null && out.getLastChild().getNodeType() == Node.TEXT_NODE) out.removeChild(out.getLastChild()); return out; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/JavadocRenderer.java000066400000000000000000000253141501674766700305410ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.javadoc; import java.nio.charset.StandardCharsets; import org.w3c.dom.Element; import org.w3c.dom.Node; import java.util.Map; import java.util.HashMap; /** * Render a node as ReStructured Text. * * @author nelson85 */ public class JavadocRenderer { static final int WIDTH = 120; public StringBuilder assembly; public int indentLevel = 0; String memberName; public String render(Node node) { try { indentLevel = 0; assembly = new StringBuilder(); DomUtilities.traverseChildren(node, this::renderSections, Node.ELEMENT_NODE); return assembly.toString(); } catch (Exception ex) { throw new JavadocException(node, ex); } } /** * Render the dom into restructured text. * * @param node */ void renderSections(Node node) { Element e = (Element) node; String name = e.getTagName(); if (name.equals("title")) { this.memberName = node.getTextContent(); return; } if (name.equals("signature")) { assembly.append(node.getTextContent()) .append("\n\n"); indentLevel += 4; return; } if (name.equals("description")) { renderText(node, true, true); return; } if (name.equals("details")) { DomUtilities.traverseChildren(node, this::renderDetails, Node.ELEMENT_NODE); assembly.append("\n"); return; } } final static Map SECTIONS = new HashMap<>(); static { // Decide what sections to render SECTIONS.put("returns", "Returns:"); SECTIONS.put("see", "See also:"); SECTIONS.put("since", "Since:"); SECTIONS.put("jls", "See Java\u2122 specification:"); SECTIONS.put("overrides", "Overrides:"); SECTIONS.put("specified", "Specified by:"); SECTIONS.put("version", null); SECTIONS.put("typeparams", null); SECTIONS.put("author", null); SECTIONS.put("see", "Also see:"); SECTIONS.put("api_note", "API Note:"); SECTIONS.put("requirements", "Implementation Requirements:"); SECTIONS.put("impl_note", "Implementation Note:"); } void renderDetails(Node node) { String name = node.getNodeName(); if (name.equals("parameters")) { assembly.append('\n') .append(indentation(this.indentLevel)) .append("Parameters:\n"); indentLevel += 4; DomUtilities.traverseChildren(node, this::renderParameter, Node.ELEMENT_NODE); indentLevel -= 4; } else if (name.equals("throws")) { assembly.append('\n') .append(indentation(this.indentLevel)) .append("Raises:\n"); indentLevel += 4; DomUtilities.traverseChildren(node, this::renderThrow, Node.ELEMENT_NODE); indentLevel -= 4; } else if (SECTIONS.containsKey(name)) { String title = SECTIONS.get(name); if (title == null) return; assembly.append('\n') .append(indentation(this.indentLevel)) .append(title).append('\n'); indentLevel += 4; renderText(node, true, true); indentLevel -= 4; } else { System.err.println("Need renderer for section " + name); } } void renderParameter(Node node) { Element elem = (Element) node; assembly.append(indentation(this.indentLevel)) // .append(" ") .append(elem.getAttribute("name")) .append(" (") .append(elem.getAttribute("type")) .append("): "); indentLevel += 4; renderText(node, false, true); indentLevel -= 4; } void renderThrow(Node node) { Element elem = (Element) node; assembly.append(indentation(this.indentLevel)) //.append(" ") .append(elem.getAttribute("name")) .append(": "); indentLevel += 4; renderText(node, false, true); indentLevel -= 4; } /** * Render a paragraph or paragraph like element. * */ void renderText(Node node, boolean startIndent, boolean trailingNL) { DomUtilities.combineText(node); DomUtilities.removeWhitespace(node); Node child = node.getFirstChild(); for (; child != null; child = child.getNextSibling()) { if (child.getNodeType() == Node.TEXT_NODE) { String value = child.getNodeValue(); if (value == null) continue; value = value.trim(); if (value.isEmpty()) continue; formatWidth(assembly, value, WIDTH, indentLevel, startIndent); if (trailingNL) assembly.append("\n"); continue; } if (child.getNodeType() != Node.ELEMENT_NODE) continue; Element element = (Element) child; String name = element.getTagName(); if (name.equals("p")) { assembly.append("\n"); renderText(element, true, true); } else if (name.equals("div")) { renderText(element, true, true); } else if (name.equals("center")) { renderText(element, true, true); } else if (name.equals("br")) { assembly.append("\n\n"); } else if (name.equals("ul")) { renderUnordered(element); } else if (name.equals("ol")) { renderOrdered(element); } else if (name.equals("img")) { // punt } else if (name.equals("table")) { // punt } else if (name.equals("hr")) { // punt } else if (name.equals("dl")) { renderDefinitions(element); } else if (name.equals("codeblock")) { renderCodeBlock(element); } else if (name.equals("blockquote")) { renderBlockQuote(element); } else if (name.equals("h1")) { renderHeader(element); } else if (name.equals("h2")) { renderHeader(element); } else if (name.equals("h3")) { renderHeader(element); } else if (name.equals("h4")) { renderHeader(element); } else if (name.equals("h5")) { renderHeader(element); } else { throw new RuntimeException("Need render for " + name); } } } void renderHeader(Node node) { assembly.append("\n"); renderText(node, true, true); assembly.append(new String(new byte[node.getTextContent().length()]).replace('\0', '-')) .append("\n\n"); } void renderBlockQuote(Node node) { indentLevel += 4; renderText(node, true, true); indentLevel -= 4; } /** * Render an unordered list. * * @param node */ void renderOrdered(Node node) { indentLevel += 4; assembly.append("\n"); Node child = node.getFirstChild(); int num = 1; for (; child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; if (child.getNodeName().equals("li")) { assembly.append(indentation(indentLevel - 2)) .append(String.format("%d. ", num++)); renderText(child, false, true); } else throw new RuntimeException("Bad node " + child.getNodeName() + " in UL"); } indentLevel -= 4; assembly.append("\n"); } /** * Render an unordered list. * * @param node */ void renderUnordered(Node node) { indentLevel += 4; assembly.append("\n"); Node child = node.getFirstChild(); for (; child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; if (child.getNodeName().equals("li")) { assembly.append(indentation(indentLevel - 4)) .append(" - "); renderText(child, false, true); } else throw new RuntimeException("Bad node " + child.getNodeName() + " in UL"); } indentLevel -= 4; assembly.append("\n"); } /** * Render a definition list. * * @param node */ void renderDefinitions(Node node) { Node child = node.getFirstChild(); for (; child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; String name = child.getNodeName(); if (name.equals("dt")) { assembly.append("\n"); renderText(child, true, true); } else if (name.equals("dd")) { assembly.append(indentation(indentLevel)); indentLevel += 4; assembly.append(" "); renderText(child, false, true); indentLevel -= 4; } else throw new RuntimeException("Bad node " + name + " in DL"); } assembly.append("\n"); } void renderCodeBlock(Node node) { String indent = indentation(indentLevel); assembly.append("\n") .append(indent) .append(".. code-block: java\n"); String text = node.getTextContent(); if (text.charAt(0) != '\n') text = "\n" + text; text = text.replaceAll("\n", "\n" + indent); assembly.append(indent).append(text).append("\n"); } // static final String SPACING = new String(new byte[40]).replace('\0', ' '); static String indentation(int level) { if (level > 40) return new String(); return SPACING.substring(0, level); } static void formatWidth(StringBuilder sb, String s, int width, int indent, boolean flag) { String sindent = indentation(indent); s = s.replaceAll("\\s+", " ").trim(); if (s.length() < width) { if (flag) sb.append(sindent); sb.append(s); return; } byte[] b = s.getBytes(StandardCharsets.UTF_8); int start = 0; int prev = 0; int l = b.length; int next = 0; while (next < l) { for (next = prev + 1; next < l; ++next) if (b[next] == ' ') break; if (next - start > width) { b[prev] = '\n'; if (flag) sb.append(sindent); flag = true; sb.append(new String(b, start, prev - start + 1)); start = prev + 1; } prev = next; } sb.append(sindent); sb.append(new String(b, start, l - start)); } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/javadoc/JavadocTransformer.java000066400000000000000000000336731501674766700313040ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.javadoc; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jpype.html.Html; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; /** * Transform the document into a form suitable for ReStructured Text. * * The goal of this is to convert all inline markup into rst and leave markup by * section, paragraph to be used by the renderer. * * @author nelson85 */ public class JavadocTransformer { final static Pattern ARGS_PATTERN = Pattern.compile(".*\\((.*)\\).*"); public Node transformDescription(Class cls, Node node) { try { Workspace ws = new Workspace(cls); DomUtilities.traverseDFS(node, this::fixEntities, Node.TEXT_NODE); DomUtilities.traverseChildren(node, this::handleDescription, Node.ELEMENT_NODE, ws); DomUtilities.traverseDFS(node, this::pass1, Node.ELEMENT_NODE, ws); return node; } catch (Exception ex) { throw new JavadocException(node, ex); } } /** * Convert a Javadoc member description into markup for ReStructure Text * rendering. * * This will mutilate the node. * * @param node */ public Node transformMember(Class cls, Node node) { try { Workspace ws = new Workspace(cls); DomUtilities.traverseDFS(node, this::fixEntities, Node.TEXT_NODE); DomUtilities.traverseChildren(node, this::handleMembers, Node.ELEMENT_NODE, ws); DomUtilities.traverseDFS(node, this::pass1, Node.ELEMENT_NODE, ws); return node; } catch (Exception ex) { throw new JavadocException(node, ex); } } // void handleDescription(Node node, Workspace data) { Element e = (Element) node; String name = e.getTagName(); Document doc = e.getOwnerDocument(); Node parent = node.getParentNode(); if (name.equals("dl") && !data.hr) { parent.removeChild(node); } else if (name.equals("br")) { parent.removeChild(node); } else if (name.equals("hr")) { data.hr = true; parent.removeChild(node); } else if (name.equals("pre") || // Javadoc pre-17 (name.equals("div") && e.getAttribute("class").equals("type-signature"))) // Javadoc 17+ { DomUtilities.removeWhitespace(node); doc.renameNode(node, null, "signature"); } else if (name.equals("div")) { doc.renameNode(node, null, "description"); DomUtilities.clearAttributes(node); } else if (name.equals("dl")) { doc.renameNode(node, null, "details"); DomUtilities.traverseChildren(node, this::handleDetails, Node.ELEMENT_NODE, data); } else { throw new RuntimeException("Unknown item at top level " + name); } } // // void handleMembers(Node node, Workspace ws) { Element e = (Element) node; String name = e.getTagName(); Document doc = e.getOwnerDocument(); if (name.equals("h4") || name.equals("h3")) // h4 for Javadoc pre-17, h3 for Javadoc 17+ { doc.renameNode(node, null, "title"); } else if (name.equals("pre") || // Javadoc pre-17 (name.equals("div") && (e.getAttribute("class").equals("member-signature")))) // Javadoc 17+ { doc.renameNode(node, null, "signature"); DomUtilities.traverseDFS(node, this::pass1, Node.ELEMENT_NODE, ws); // We need to get the types from here for the parameters DomUtilities.removeWhitespace(node); String content = node.getTextContent(); Matcher m = ARGS_PATTERN.matcher(content); if (m.matches()) { LinkedList types = new LinkedList<>(); for (String s : m.group(1).split(", ")) { String[] parts = s.split("\u00a0", 2); types.add(parts[0]); } ws.types = types; } } else if (name.equals("div")) { doc.renameNode(node, null, "description"); DomUtilities.clearAttributes(node); } else if (name.equals("dl")) { doc.renameNode(node, null, "details"); DomUtilities.traverseChildren(node, this::handleDetails, Node.ELEMENT_NODE, ws); } else { throw new RuntimeException("Unknown item at top level " + name); } } public final static Map DETAIL_SECTIONS; static { DETAIL_SECTIONS = new HashMap<>(); Map ds = DETAIL_SECTIONS; ds.put("Author:", "author"); ds.put("Since:", "since"); ds.put("Parameters:", "parameters"); ds.put("Returns:", "returns"); ds.put("Overrides:", "overrides"); ds.put("See Also:", "see"); ds.put("API Note:", "api_note"); ds.put("Version:", "version"); ds.put("Type Parameters:", "typeparams"); ds.put("Specified by:", "specified"); ds.put("Throws:", "throws"); ds.put("Implementation Requirements:", "requirements"); ds.put("Implementation Note:", "impl_note"); } void handleDetails(Node node, Workspace ws) { Element e = (Element) node; String name = e.getTagName(); Document doc = e.getOwnerDocument(); Node parent = e.getParentNode(); if (name.equals("dt")) { String key = node.getTextContent().trim(); if (DETAIL_SECTIONS.containsKey(key)) { doc.renameNode(node, null, DETAIL_SECTIONS.get(key)); } else if (key.startsWith("See ")) { doc.renameNode(node, null, "jls"); } else { System.err.println("Bad detail key '" + key + "'"); } ws.key = node.getNodeName(); ws.section = node; DomUtilities.clearChildren(ws.section); } if (name.equals("dd")) { if (ws.key.equals("parameters")) { Node first = node.getFirstChild(); // First is varname Node second = first.getNextSibling(); // Second is " - desc" Element elem = doc.createElement("parameter"); elem.setAttribute("name", first.getTextContent()); elem.setAttribute("type", ws.types.removeFirst()); String value = second.getNodeValue(); second.setNodeValue(value.substring(3)); // Remove " - " node.removeChild(first); DomUtilities.transferContents(elem, node); ws.section.appendChild(elem); parent.removeChild(node); } else if (ws.key.equals("throws")) { Node first = node.getFirstChild(); // First is exc Node second = first.getNextSibling(); // Second is " - desc" Element elem = doc.createElement("exception"); DomUtilities.traverseDFS(first, this::pass1, Node.ELEMENT_NODE, ws); elem.setAttribute("name", first.getTextContent()); if (second != null) { String value = second.getNodeValue(); second.setNodeValue(value.substring(3)); // Remove " - " } node.removeChild(first); DomUtilities.transferContents(elem, node); ws.section.appendChild(elem); parent.removeChild(node); } else { // Normalize the node and transfer it to the section DomUtilities.transferContents(ws.section, node); DomUtilities.traverseDFS(ws.section, this::pass1, Node.ELEMENT_NODE, ws); DomUtilities.removeWhitespace(ws.section); parent.removeChild(node); return; } } } static class Workspace { private final Class cls; boolean hr = false; String key; Node section; private LinkedList types; Workspace(Class cls) { this.cls = cls; } } // // /** * Convert any html entities found the text. * * @param node */ void fixEntities(Node node) { Text n = (Text) node; String s = Html.decode(n.getTextContent()); n.setTextContent(s); } // This corresponds to any simple inline markup transformation. final static String[] PASS1 = new String[] { "cite", "\"%s\"", "code", ":code:`%s`", "pre", ":code:`%s`", "tt", "``%s``", "i", "*%s*", "em", "*%s*", "strong", "**%s**", "b", "**%s**", "sup", " :sup:`%s` ", "sub", " :sub:`%s` ", "small", ":sub:`%s`", "span", "%s", "nop", "%s", "font", "%s", "var", "*%s*", }; /** * Get a bunch of simple substitutions. * * @param node */ void pass1(Node node, Workspace ws) { Element e = (Element) node; String name = e.getTagName(); Document doc = e.getOwnerDocument(); Node parent = e.getParentNode(); if (parent == null) return; // Pre is something used to mark code. if (name.equals("pre")) { if (DomUtilities.containsNL(e)) { doc.renameNode(node, null, "codeblock"); name = "codeblock"; } else { doc.renameNode(node, null, "code"); name = "code"; } } //
 is a common javadoc idiom.
    if (name.equals("code") && (parent.getNodeName().equals("pre")
            || parent.getNodeName().equals("blockquote")))
    {
      doc.renameNode(parent, null, "codeblock");
      DomUtilities.mergeNode(parent, node);
      return;
    }

    if (name.equals("codeblock") && parent.getNodeName().equals("pre"))
    {
      if (DomUtilities.containsNL(node))
      {
        doc.renameNode(parent, null, "codeblock");
      } else
        doc.renameNode(parent, null, "code");
      DomUtilities.mergeNode(parent, node);
      return;
    }

    //  is used to reference members and classes.
    if (name.equals("code") && parent.getNodeName().equals("a"))
    {
      Element eparent = (Element) parent;
      String href = this.toReference(ws, eparent.getAttribute("href"));
      DomUtilities.clearChildren(parent);
      parent.appendChild(doc.createTextNode(href));
      return;
    }

    //  is also used.
    if (name.equals("a") && parent.getNodeName().equals("code"))
    {
      String href = this.toReference(ws, e.getAttribute("href"));
      DomUtilities.clearChildren(parent);
      doc.renameNode(parent, null, "nop");
      parent.appendChild(doc.createTextNode(href));
      return;
    }

    //  by itself is usually external references.
    if (name.equals("a"))
    {
      String href = e.getAttribute("href");
      if (href.startsWith("http:") || href.startsWith("shttp:"))
      {
        String content = node.getTextContent();
        content = String.format("`%s <%s>`", content, href);
        parent.replaceChild(doc.createTextNode(content), node);
        return;
      }
      href = this.toReference(ws, href);
      if (href == null)
        parent.replaceChild(doc.createTextNode(node.getTextContent()), node);
      else
        parent.replaceChild(doc.createTextNode(href), node);
      return;
    }

    // Apply inline transformations.
    for (int i = 0; i < PASS1.length; i += 2)
    {
      if (name.equals(PASS1[i]))
      {
        String s2 = e.getTextContent();
        if (s2 == null)
          e.getParentNode().removeChild(e);
        else
          e.getParentNode().replaceChild(doc.createTextNode(String.format(PASS1[i + 1], s2.trim())), e);
        return;
      }
    }

  }

  /**
   * Convert a reference into method or class.
   *
   * This currently only deals with local links. External links are elsewhere.
   *
   * @param ws
   * @param href
   * @return
   */
  public String toReference(Workspace ws, String href)
  {
    try
    {
      Path p = Paths.get(ws.cls.getName().replace('.', '/'));
      if (href.startsWith("#"))
      {
        // technically it may be a field, but we can't tell currently.
        Path q = p.resolve(href.substring(1).trim())
                .normalize();
        if (q.startsWith(".."))
          q = q.subpath(2, q.getNameCount());
        String r = q.toString()
                .replace('/', '.')
                .replace('\\', '.')
                .replaceAll("\\(.*\\)", "")
                .replaceAll("-.*", "");
        return String.format(":meth:`~%s`", r);
      } else if (href.contains("#"))
      {
        // technically it may be a field, but we can't tell currently.
        Path q = p.getParent()
                .resolve(href.trim())
                .normalize();
        if (q.startsWith(".."))
          q = q.subpath(2, q.getNameCount());
        String r = q.toString()
                .replace('/', '.')
                .replace('\\', '.')
                .replaceAll("\\(.*\\)", "")
                .replaceAll("-.*", "")
                .replaceAll(".html#", ".");
        return String.format(":meth:`~%s`", r);
      } else
      {
        Path q = p.getParent()
                .resolve(href.trim())
                .normalize();
        if (q.startsWith(".."))
          q = q.subpath(2, q.getNameCount());
        String r = q.toString()
                .replace('/', '.')
                .replace('\\', '.')
                .replaceAll("-.*", "")
                .replaceAll(".html", "");
        return String.format(":class:`~%s`", r);
      }
    } catch (Exception ex)
    {
      // There is a lot of ways this can go wrong.  If all else fails
      // return null so that we can just remove the hyperlink.
      return null;
    }
  }

//
}
jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/000077500000000000000000000000001501674766700246365ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/ClassDescriptor.java000066400000000000000000000040371501674766700306110ustar00rootroot00000000000000/* ****************************************************************************
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  See NOTICE file for details.
**************************************************************************** */
package org.jpype.manager;

import java.lang.reflect.Executable;
import java.lang.reflect.Method;

/**
 * A list of resources associated with this class.
 * 

* These can be accessed within JPype using the org.jpype.manager.TypeManager. *

*/ public class ClassDescriptor { public Class cls; /** * JPClass pointer for this class. */ public long classPtr; /** * JPMethodDispatch for the constructor. */ public long constructorDispatch; public long[] constructors; /** * Resources needed by the class */ public long[] methodDispatch; public Executable[] methodIndex; public long[] methods; public int methodCounter = 0; public long[] fields; public long anonymous; public int functional_interface_parameter_count; ClassDescriptor(Class cls, long classPtr, Method method) { this.cls = cls; this.classPtr = classPtr; if (this.classPtr == 0) throw new NullPointerException("Class pointer is null for " + cls); if (method != null) functional_interface_parameter_count = method.getParameterCount(); else functional_interface_parameter_count = -1; } long getMethod(Method requestedMethod) { for (int i = 0; i < methods.length; ++i) if (this.methodIndex[i].equals(requestedMethod)) return this.methods[i]; return 0; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/MethodResolution.java000066400000000000000000000205171501674766700310120ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Executable; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; /** * Sort out which methods hide other methods. *

* When resolving method overloads there may be times in which more than one * overload applies. JPype requires that the methods appear in order from most * to least specific. And each method overload requires a list of methods that * are more general. If two or more methods match and one is not more specific * than the other. * * @author nelson85 */ public class MethodResolution { long ptr = 0; boolean covered = false; Executable executable; List children = new ArrayList<>(); MethodResolution(Executable method) { this.executable = method; } private boolean isCovered() { for (MethodResolution ov : this.children) { if (!ov.covered) return false; } covered = true; return true; } /** * Order methods from least to most specific. * * @param * @param methods * @return */ public static List sortMethods(List methods) { // Create a method resolution for each method LinkedList unsorted = new LinkedList<>(); for (T m1 : methods) { unsorted.add(new MethodResolution(m1)); } for (MethodResolution m1 : unsorted) { for (MethodResolution m2 : unsorted) { if (m1 == m2) continue; if (isMoreSpecificThan(m1.executable, m2.executable) && !isMoreSpecificThan(m2.executable, m1.executable)) { m1.children.add(m2); } } } // Execute a graph sort problem so that the most specific are always on the front LinkedList out = new LinkedList<>(); while (!unsorted.isEmpty()) { // Remove the first unsorted element MethodResolution front = unsorted.pop(); // Check to see if all dependencies are already ordered boolean good = front.isCovered(); // If all dependencies are included if (good) { front.covered = true; out.add(front); } else { unsorted.add(front); } } return out; } // Table for primitive rules static Class[] of(Class... l) { return l; } static HashMap CONVERSION = new HashMap<>(); { CONVERSION.put(Byte.TYPE, of(Byte.TYPE, Byte.class, Short.TYPE, Short.class, Integer.TYPE, Integer.class, Long.TYPE, Long.class, Float.TYPE, Float.class, Double.TYPE, Double.class)); CONVERSION.put(Character.TYPE, of(Character.TYPE, Character.class, Short.TYPE, Short.class, Integer.TYPE, Integer.class, Long.TYPE, Long.class, Float.TYPE, Float.class, Double.TYPE, Double.class)); CONVERSION.put(Short.TYPE, of(Short.TYPE, Short.class, Integer.TYPE, Integer.class, Long.TYPE, Long.class, Float.TYPE, Float.class, Double.TYPE, Double.class)); CONVERSION.put(Integer.TYPE, of(Integer.TYPE, Integer.class, Long.TYPE, Long.class, Float.TYPE, Float.class, Double.TYPE, Double.class)); CONVERSION.put(Long.TYPE, of(Long.TYPE, Long.class, Float.TYPE, Float.class, Double.TYPE, Double.class)); CONVERSION.put(Float.TYPE, of(Float.TYPE, Float.class, Double.TYPE, Double.class)); CONVERSION.put(Double.TYPE, of(Double.TYPE, Double.class)); CONVERSION.put(Boolean.TYPE, of(Boolean.TYPE, Boolean.class)); } static boolean isAssignableTo(Class c1, Class c2) { if (!c1.isPrimitive()) return c2.isAssignableFrom(c1); Class[] cl = CONVERSION.get(c1); if (cl == null) return false; for (Class c3 : cl) if (c2.equals(c3)) return true; return false; } /** * Determine is a executable is more specific than another. *

* This is public so that we can debug from within jpype. * * @param method1 * @param method2 * @return */ public static boolean isMoreSpecificThan(Executable method1, Executable method2) { List> param1 = new ArrayList<>(Arrays.asList(method1.getParameterTypes())); List> param2 = new ArrayList<>(Arrays.asList(method2.getParameterTypes())); if (!Modifier.isStatic(method1.getModifiers())) param1.add(0, method1.getDeclaringClass()); if (!Modifier.isStatic(method2.getModifiers())) param2.add(0, method2.getDeclaringClass()); // Special handling is needed for varargs as it may chop or expand. // we have 4 cases for a varargs methods // foo(Arg0, Arg1...) as // foo(Arg0) // foo(Arg0, Arg1) // foo(Arg0, Arg1[]) // foo(Arg0, Arg1, Arg1+) if (method1.isVarArgs() && method2.isVarArgs()) { // Punt on this as there are too many different cases return isMoreSpecificThan(param1, param2); } if (method1.isVarArgs()) { int n1 = param1.size(); int n2 = param2.size(); // Last element is an array Class cls = param1.get(n1 - 1); Class cls2 = cls.getComponentType(); // Less arguments, chop the list if (n1 - 1 == n2) return isMoreSpecificThan(param1.subList(0, n2), param2); // Same arguments if (n1 == n2) { List> q = new ArrayList<>(param1); q.set(n1 - 1, cls2); // Check both ways boolean isMoreSpecific = isMoreSpecificThan(param1, param2) || isMoreSpecificThan(q, param2); // If the varargs array is of the single-variable's type (or they are primitive-equivalent), // the single-variable signature should win specificity Class svCls = param2.get(n2 - 1); return isMoreSpecific && !(isAssignableTo(cls2, svCls) && isAssignableTo(svCls, cls2)); } // More arguments if (n1 < n2) { // Grow the list List> q = new ArrayList<>(param1); q.set(n1 - 1, cls2); for (int i = n1; i < n2; ++i) q.add(cls2); return isMoreSpecificThan(q, param2); } } if (method2.isVarArgs()) { int n1 = param1.size(); int n2 = param2.size(); // Last element is an array Class cls = param2.get(n2 - 1); Class cls2 = cls.getComponentType(); // Less arguments, chop the list if (n2 - 1 == n1) return isMoreSpecificThan(param1, param2.subList(0, n2)); // Same arguments if (n1 == n2) { List> q = new ArrayList<>(param2); q.set(n2 - 1, cls2); // Compare both ways return isMoreSpecificThan(param1, param2) || isMoreSpecificThan(param1, q); } // More arguments if (n2 < n1) { // Grow the list List> q = new ArrayList<>(param2); q.set(n2 - 1, cls2); for (int i = n2; i < n1; ++i) q.add(cls2); return isMoreSpecificThan(param1, q); } } return isMoreSpecificThan(param1, param2); } public static boolean isMoreSpecificThan(List> param1, List> param2) { // FIXME need to consider resolving mixing of static and non-static // Methods here. if (param1.size() != param2.size()) return false; for (int i = 0; i < param1.size(); ++i) { if (!isAssignableTo(param1.get(i), param2.get(i))) return false; } return true; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/ModifierCode.java000066400000000000000000000042631501674766700300370ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Modifier; import java.util.EnumSet; /** * Definitions for JPype modifiers. *

* These pretty much match Java plus a few codes we need. * * @author nelson85 */ public enum ModifierCode { // we need // fields: static, final // methods: static, final, varargs, constructor // class: interface, throwable, abstract, final PUBLIC(Modifier.PUBLIC), PRIVATE(Modifier.PRIVATE), PROTECTED(Modifier.PROTECTED), STATIC(Modifier.STATIC), FINAL(Modifier.FINAL), VARARGS(0x0080), ENUM(0x4000), ABSTRACT(0x0400), // Special flags for classes required for JPype SPECIAL(0x00010000), THROWABLE(0x00020000), SERIALIZABLE(0x00040000), ANONYMOUS(0x00080000), FUNCTIONAL(0x00100000), CALLER_SENSITIVE(0x00200000), PRIMITIVE_ARRAY(0x00400000), COMPARABLE(0x00800000), BUFFER(0x01000000), CTOR(0x10000000), BEAN_ACCESSOR(0x20000000), BEAN_MUTATOR(0x40000000); final public int value; ModifierCode(int value) { this.value = value; } public static int get(EnumSet set) { int out = 0; for (ModifierCode m : set) { out |= m.value; } return out; } public static EnumSet decode(long modifiers) { EnumSet out = EnumSet.noneOf(ModifierCode.class); for (ModifierCode code : ModifierCode.values()) { if ((modifiers & code.value) == code.value) out.add(code); } return out; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/TypeAudit.java000066400000000000000000000021241501674766700274100ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Method; /** * Auditing class for TypeManager used during testing. *

* This is not used during operation. * * @author nelson85 */ public interface TypeAudit { void dump(ClassDescriptor desc); void verifyMembers(ClassDescriptor desc); public void failFindMethod(ClassDescriptor desc, Method method); } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/TypeFactory.java000066400000000000000000000117451501674766700277620ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Executable; import java.lang.reflect.Field; /** * Interface for creating new resources used by JPype. *

* This calls the C++ constructors with all of the required fields for each * class. This pattern eliminates the need for C++ layer probing Java for * resources. *

* This is an interface for testing. * * @author nelson85 */ public interface TypeFactory { // /** * Create a new wrapper type for Python. * * @param context * @param cls is the pointer to the JClass. */ void newWrapper(long context, long cls); /** * Create a JPArray class. * * @param context JPContext object * @param cls is the class type. * @param name * @param superClass * @param componentPtr * @param modifiers * @return the pointer to the JPArrayClass. */ long defineArrayClass( long context, Class cls, String name, long superClass, long componentPtr, int modifiers); /** * Create a class type. * * @param context JPContext object * @param cls * @param superClass * @param interfaces * @param modifiers * @param name * @return the pointer to the JPClass. */ long defineObjectClass( long context, Class cls, String name, long superClass, long[] interfaces, int modifiers); /** * Define a primitive types. * * @param context JPContext object * @param cls is the Java class for this primitive. * @param boxedPtr is the JPClass for the boxed class. * @param modifiers * @return */ long definePrimitive( long context, String name, Class cls, long boxedPtr, int modifiers); // // /** * Called after a class is constructed to populate the required fields and * methods. * * @param context JPContext object * @param cls is the JPClass to populate * @param ctorMethod is the JPMethod for the constructor. * @param methodList is a list of JPMethod for the method list. * @param fieldList is a list of JPField for the field list. */ void assignMembers( long context, long cls, long ctorMethod, long[] methodList, long[] fieldList); /** * Create a Method. * * @param context JPContext object * @param cls is the class holding this. * @param name * @param field * @param fieldType * @param modifiers * @return the pointer to the JPMethod. */ long defineField( long context, long cls, String name, Field field, // This will convert to a field id long fieldType, int modifiers); /** * Create a Method. * * @param context JPContext object * @param cls is the class holding this. * @param name * @param method is the Java method that will be called, converts to a method * id. * @param overloadList * @param modifiers * @return the pointer to the JPMethod. */ long defineMethod( long context, long cls, String name, Executable method, long[] overloadList, int modifiers); void populateMethod( long context, long method, long returnType, long[] argumentTypes); /** * Create a Method dispatch for Python by name. * * @param context JPContext object * @param cls is the class that owns this dispatch. * @param name is the name of the dispatch. * @param overloadList is the list of all methods constructed for this class. * @param modifiers contains if the method is (CTOR, STATIC), * @return the pointer to the JPMethodDispatch. */ long defineMethodDispatch( long context, long cls, String name, long[] overloadList, int modifiers); // // /** * Destroy the resources. * * @param context JPContext object * @param resources * @param sz */ void destroy( long context, long[] resources, int sz); // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/TypeFactoryNative.java000066400000000000000000000051571501674766700311310ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Executable; import java.lang.reflect.Field; /** * This is the interface for creating C++ object in JPype. *

* These methods are all native. *

*/ public class TypeFactoryNative implements TypeFactory { public long context; public native void newWrapper(long context, long cls); @Override public native long defineArrayClass( long context, Class cls, String name, long superClass, long componentPtr, int modifiers); @Override public native long defineObjectClass( long context, Class cls, String name, long superClass, long[] interfaces, int modifiers); @Override public native long definePrimitive( long context, String name, Class cls, long boxedPtr, int modifiers); @Override public native void assignMembers( long context, long cls, long ctorMethod, long[] methodList, long[] fieldList); @Override public native long defineField( long context, long cls, String name, Field field, long fieldType, int modifiers); @Override public native long defineMethod( long context, long cls, String name, Executable method, long[] overloadList, int modifiers); @Override public native void populateMethod( long context, long method, long returnType, long[] argumentTypes); @Override public native long defineMethodDispatch( long context, long cls, String name, long[] overloadList, int modifiers); @Override public native void destroy( long context, long[] resources, int sz); } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/manager/TypeManager.java000066400000000000000000000671341501674766700277300ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.util.Arrays; import java.nio.Buffer; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.TreeSet; import org.jpype.JPypeContext; import org.jpype.JPypeUtilities; import org.jpype.proxy.JPypeProxy; /** * */ public class TypeManager { public long context = 0; public boolean isStarted = false; public boolean isShutdown = false; public HashMap classMap = new HashMap<>(); public TypeFactory typeFactory = null; public TypeAudit audit = null; private ClassDescriptor java_lang_Object; // For reasons that are less than clear, this object cannot be created // during shutdown private Destroyer destroyer = new Destroyer(); public TypeManager() { } public TypeManager(long context, TypeFactory typeFactory) { this.context = context; this.typeFactory = typeFactory; } // public synchronized void init() { try { if (isStarted) throw new RuntimeException("Cannot be restarted"); isStarted = true; isShutdown = false; // Create the required minimum classes this.java_lang_Object = createClass(Object.class, true); // Note that order is very important when creating these initial wrapper // types. If something inherits from another type then the super class // will be created without the special flag and the type system won't // be able to handle the duplicate type properly. Class[] cls = { Class.class, Number.class, CharSequence.class, Throwable.class, Void.class, Boolean.class, Byte.class, Character.class, Short.class, Integer.class, Long.class, Float.class, Double.class, String.class, JPypeProxy.class, Method.class, Field.class }; for (Class c : cls) { createClass(c, true); } // Create the primitive types // Link boxed and primitive types so that the wrappers can find them. createPrimitive("void", Void.TYPE, Void.class); createPrimitive("boolean", Boolean.TYPE, Boolean.class); createPrimitive("byte", Byte.TYPE, Byte.class); createPrimitive("char", Character.TYPE, Character.class); createPrimitive("short", Short.TYPE, Short.class); createPrimitive("int", Integer.TYPE, Integer.class); createPrimitive("long", Long.TYPE, Long.class); createPrimitive("float", Float.TYPE, Float.class); createPrimitive("double", Double.TYPE, Double.class); } catch (Throwable ex) { // We can't get debugging information at this point in the process. ex.printStackTrace(); throw ex; } } /** * Find a wrapper for a class. *

* Creates one if needed. This a front end used by JPype. * * @param cls * @return the JPClass, or 0 it one cannot be created. */ public synchronized long findClass(Class cls) { if (cls == null) return 0; if (this.isShutdown) return 0; long out; if (cls.isSynthetic() && cls.getSimpleName().contains("$Lambda$")) { // If is it lambda, we need a special wrapper // we don't want to create a class each time in that case. // Thus use the parent interface for this class out = getClass(cls.getInterfaces()[0]).classPtr; } else if (cls.isAnonymousClass()) { // This one is more of a burden. It depends what whether is was // anonymous extends or implements. if (cls.getInterfaces().length == 1) out = getClass(cls.getInterfaces()[0]).classPtr; else { ClassDescriptor parent = getClass(cls.getSuperclass()); out = createAnonymous(parent); } } else { // Just a regular class out = getClass(cls).classPtr; } return out; } /** * Get a class by name. * * @param name is the class name. * @return the C++ portion. */ public long findClassByName(String name) { Class cls = lookupByName(name); if (cls == null) return 0; return this.findClass(cls); } public Class lookupByName(String name) { ClassLoader classLoader = JPypeContext.getInstance().getClassLoader(); // Handle arrays if (name.endsWith("[]")) { int dims = 0; while (name.endsWith("[]")) { dims++; name = name.substring(0, name.length() - 2); } Class cls = lookupByName(name); if (cls == null) return null; return Array.newInstance(cls, new int[dims]).getClass(); } try { // Attempt direct lookup return Class.forName(name, true, classLoader); } catch (ClassNotFoundException ex) { } // Deal with JNI style names if (name.contains("/")) { try { return Class.forName(name.replaceAll("/", "."), true, classLoader); } catch (ClassNotFoundException ex) { } } // Special case for primitives if (!name.contains(".")) { if ("boolean".equals(name)) return Boolean.TYPE; if ("byte".equals(name)) return Byte.TYPE; if ("char".equals(name)) return Character.TYPE; if ("short".equals(name)) return Short.TYPE; if ("long".equals(name)) return Long.TYPE; if ("int".equals(name)) return Integer.TYPE; if ("float".equals(name)) return Float.TYPE; if ("double".equals(name)) return Double.TYPE; } // Attempt to find an inner class String[] parts = name.split("\\."); StringBuilder sb = new StringBuilder(); sb.append(parts[0]); for (int i = 1; i < parts.length; ++i) { try { sb.append("."); sb.append(parts[i]); Class cls = Class.forName(sb.toString()); for (int j = i + 1; j < parts.length; ++j) { sb.append("$"); sb.append(parts[j]); } return Class.forName(sb.toString()); } catch (ClassNotFoundException ex) { } } return null; } public synchronized void populateMethod(long wrapper, Executable method) { if (method == null) return; long returnType = 0; if (method instanceof Method) { returnType = getClass(((Method) method).getReturnType()).classPtr; } Class[] params = method.getParameterTypes(); int i = 0; long[] paramPtrs; if (!Modifier.isStatic(method.getModifiers()) && !(method instanceof Constructor)) { paramPtrs = new long[params.length + 1]; paramPtrs[0] = getClass(method.getDeclaringClass()).classPtr; i++; } else { paramPtrs = new long[params.length]; } // Copy in the parameters for (Class p : params) { paramPtrs[i] = getClass(p).classPtr; i++; } try { typeFactory.populateMethod(context, wrapper, returnType, paramPtrs); } catch (Exception ex) { ex.printStackTrace(); } } /** * Returns the number of arguments an interface only unimplemented method * accept. * * @param interfaceClass The class of the interface * @return the number of arguments the only unimplemented method of the * interface accept. */ public int interfaceParameterCount(Class interfaceClass) { ClassDescriptor classDescriptor = classMap.get(interfaceClass); return classDescriptor.functional_interface_parameter_count; } /** * Get a class for an object. * * @param object is the object to interrogate. * @return the C++ portion or null if the object is null. * @throws java.lang.InterruptedException */ public long findClassForObject(Object object) throws InterruptedException { JPypeContext.clearInterrupt(true); if (object == null) return 0; Class cls = object.getClass(); if (Proxy.isProxyClass(cls) && (Proxy.getInvocationHandler(object) instanceof JPypeProxy)) { return this.findClass(JPypeProxy.class); } return this.findClass(cls); } /** * Called to delete all C++ resources */ public synchronized void shutdown() { // First and most important, we can't operate from this // point forward. this.isShutdown = true; // Destroy all the resources held in C++ for (ClassDescriptor entry : this.classMap.values()) { destroyer.add(entry.constructorDispatch); destroyer.add(entry.constructors); destroyer.add(entry.methodDispatch); destroyer.add(entry.methods); destroyer.add(entry.fields); destroyer.add(entry.anonymous); destroyer.add(entry.classPtr); // The same wrapper can appear more than once so blank as we go. entry.constructorDispatch = 0; entry.constructors = null; entry.methodDispatch = null; entry.methods = null; entry.fields = null; entry.anonymous = 0; entry.classPtr = 0; } destroyer.flush(); // FIXME. If someone attempts to shutdown the JVM within a Python // proxy, everything will crash here. We would lose the class // that is calling things and the ability to throw exceptions. // Most likely this will go splat. We need to catch this // from within JPype and hard fault our way to safety. this.classMap.clear(); } // // private ClassDescriptor getClass(Class cls) { if (cls == null) return null; // Look up the current description ClassDescriptor ptr = this.classMap.get(cls); if (ptr != null) return ptr; // If we can't find it create a new class return createClass(cls, false); } /** * Allocate a new wrapper for a java class. *

* Boxed types require special handlers, as does java.lang.String * * @param cls is the Java class to wrap. * @param special marks class as requiring a specialized C++ wrapper. * @return a C++ wrapper handle for a jp_classtype */ private ClassDescriptor createClass(Class cls, boolean special) { if (cls.isArray()) return this.createArrayClass(cls); return createOrdinaryClass(cls, special, true); } private ClassDescriptor createOrdinaryClass(Class cls, boolean special, boolean bases) { // Verify the class will be loadable prior to creating the class. // If we fail to do this then the class may end up crashing later when the // members get populated which could leave us in a bad state. cls.getMethods(); cls.getFields(); // Object classes are more work as we need the super information as well. // Make sure all base classes are loaded Class superClass = cls.getSuperclass(); Class[] interfaces = cls.getInterfaces(); ClassDescriptor[] parents = new ClassDescriptor[interfaces.length + 1]; long[] interfacesPtr = null; long superClassPtr = 0; superClassPtr = 0; if (superClass != null) { parents[0] = this.getClass(superClass); superClassPtr = parents[0].classPtr; } if (bases) { interfacesPtr = new long[interfaces.length]; // Make sure all interfaces are loaded. for (int i = 0; i < interfaces.length; ++i) { parents[i + 1] = this.getClass(interfaces[i]); interfacesPtr[i] = parents[i + 1].classPtr; } } else { interfacesPtr = new long[0]; } // Set up the modifiers int modifiers = cls.getModifiers() & 0xffff; if (special) modifiers |= ModifierCode.SPECIAL.value; if (Throwable.class.isAssignableFrom(cls)) modifiers |= ModifierCode.THROWABLE.value; if (Serializable.class.isAssignableFrom(cls)) modifiers |= ModifierCode.SERIALIZABLE.value; if (Arrays.asList(cls.getInterfaces()).contains(Comparable.class)) modifiers |= ModifierCode.COMPARABLE.value; if (Buffer.class.isAssignableFrom(cls)) modifiers |= ModifierCode.BUFFER.value | ModifierCode.SPECIAL.value; // Check if is Functional class Method method = JPypeUtilities.getFunctionalInterfaceMethod(cls); if (method != null) modifiers |= ModifierCode.FUNCTIONAL.value | ModifierCode.SPECIAL.value; // FIXME watch out for anonyous and lambda here. String name = cls.getCanonicalName(); if (name == null) name = cls.getName(); // Create the JPClass long classPtr = typeFactory.defineObjectClass(context, cls, name, superClassPtr, interfacesPtr, modifiers); // Cache the wrapper. ClassDescriptor out = new ClassDescriptor(cls, classPtr, method); this.classMap.put(cls, out); return out; } private long createAnonymous(ClassDescriptor parent) { if (parent.anonymous != 0) return parent.anonymous; parent.anonymous = typeFactory.defineObjectClass(context, parent.cls, parent.cls.getCanonicalName() + "$Anonymous", parent.classPtr, null, ModifierCode.ANONYMOUS.value); return parent.anonymous; } ClassDescriptor createArrayClass(Class cls) { // Array classes are simple, we just need the component type Class componentType = cls.getComponentType(); long componentTypePtr = this.getClass(componentType).classPtr; int modifiers = cls.getModifiers() & 0xffff; String name = cls.getName(); if (!name.endsWith(";")) modifiers |= ModifierCode.PRIMITIVE_ARRAY.value; long classPtr = typeFactory .defineArrayClass(context, cls, cls.getCanonicalName(), this.java_lang_Object.classPtr, componentTypePtr, modifiers); ClassDescriptor out = new ClassDescriptor(cls, classPtr, null); this.classMap.put(cls, out); return out; } /** * Tell JPype to make a primitive Class. * * @param name * @param cls * @param boxed */ private void createPrimitive(String name, Class cls, Class boxed) { long classPtr = typeFactory.definePrimitive(context, name, cls, this.getClass(boxed).classPtr, cls.getModifiers() & 0xffff); this.classMap.put(cls, new ClassDescriptor(cls, classPtr, null)); } // // public synchronized void populateMembers(Class cls) { ClassDescriptor desc = this.classMap.get(cls); if (desc == null) throw new RuntimeException("Class not loaded"); if (desc.fields != null) return; try { createMembers(desc); } catch (Exception ex) { ex.printStackTrace(System.out); throw ex; } } private void createMembers(ClassDescriptor desc) { this.createFields(desc); this.createConstructorDispatch(desc); this.createMethodDispatches(desc); // Verify integrity if (audit != null) audit.verifyMembers(desc); // Pass this to JPype this.typeFactory.assignMembers(context, desc.classPtr, desc.constructorDispatch, desc.methodDispatch, desc.fields); } // private void createFields(ClassDescriptor desc) { // We only need declared fields as the wrappers for previous classes hold // members declared earlier LinkedList fields = filterPublic(desc.cls.getDeclaredFields()); long[] fieldPtr = new long[fields.size()]; int i = 0; for (Field field : fields) { fieldPtr[i++] = this.typeFactory.defineField(context, desc.classPtr, field.getName(), field, getClass(field.getType()).classPtr, field.getModifiers() & 0xffff); } desc.fields = fieldPtr; } // // /** * Load the constructors for a class. * * @param desc */ public void createConstructorDispatch(ClassDescriptor desc) { Class cls = desc.cls; // Get the list of declared constructors LinkedList constructors = filterPublic(cls.getDeclaredConstructors()); if (constructors.isEmpty()) return; // Sort them by precedence order List overloads = MethodResolution.sortMethods(constructors); // Convert overload list to a list of overloads pointers desc.constructors = this.createConstructors(desc, overloads); // Create the dispatch for it desc.constructorDispatch = typeFactory .defineMethodDispatch(context, desc.classPtr, "", desc.constructors, ModifierCode.PUBLIC.value | ModifierCode.CTOR.value); } /** * Construct a set of constructor overloads for an OverloadResolution. *

* These will be added to the shutdown destruction list. * * @param desc * @param overloads * @return */ private long[] createConstructors(ClassDescriptor desc, List overloads) { int n = overloads.size(); long[] overloadPtrs = new long[overloads.size()]; for (MethodResolution ov : overloads) { Constructor constructor = (Constructor) ov.executable; int i = 0; long[] precedencePtrs = new long[ov.children.size()]; for (MethodResolution ch : ov.children) { precedencePtrs[i++] = ch.ptr; } int modifiers = constructor.getModifiers() & 0xffff; modifiers |= ModifierCode.CTOR.value; ov.ptr = typeFactory.defineMethod(context, desc.classPtr, constructor.toString(), constructor, precedencePtrs, modifiers); overloadPtrs[--n] = ov.ptr; } return overloadPtrs; } // // /** * Load the methods for a class. * * @param desc */ public void createMethodDispatches(ClassDescriptor desc) { Class cls = desc.cls; // Get the list of all public, non-overrided methods we will process LinkedList methods = filterOverridden(cls, cls.getMethods()); // Get the list of public declared methods LinkedList declaredMethods = filterOverridden(cls, cls.getDeclaredMethods()); // We only need one dispatch per name TreeSet resolve = new TreeSet<>(); for (Method method : declaredMethods) { resolve.add(method.getName()); } // Reserve memory for our lookup table desc.methods = new long[declaredMethods.size()]; desc.methodIndex = new Method[declaredMethods.size()]; desc.methodDispatch = new long[resolve.size()]; int i = 0; for (String name : resolve) { desc.methodDispatch[i++] = this.createMethodDispatch(desc, name, methods); } } private long createMethodDispatch( ClassDescriptor desc, String key, LinkedList candidates) { // Find all the methods that match the key LinkedList methods = new LinkedList<>(); Iterator iter = candidates.iterator(); int modifiers = 0; while (iter.hasNext()) { Method next = iter.next(); if (!next.getName().equals(key)) continue; iter.remove(); methods.add(next); if (Modifier.isStatic(next.getModifiers())) modifiers |= ModifierCode.STATIC.value; if (isBeanAccessor(next)) modifiers |= ModifierCode.BEAN_ACCESSOR.value; if (isBeanMutator(next)) modifiers |= ModifierCode.BEAN_MUTATOR.value; } // Convert overload list to a list of overloads pointers List overloads = MethodResolution.sortMethods(methods); long[] overloadPtrs = this.createMethods(desc, overloads); long methodContainer = typeFactory.defineMethodDispatch(context, desc.classPtr, key, overloadPtrs, modifiers); return methodContainer; } /** * Convert a list of executable overload resolutions into a executable * overload list. *

* These will be added to the shutdown destruction list. * * @param desc * @param overloads * @return a list of method overload wrappers. */ private long[] createMethods( ClassDescriptor desc, List overloads) { int n = overloads.size(); long[] overloadPtrs = new long[overloads.size()]; for (MethodResolution ov : overloads) { Method method = (Method) ov.executable; // We may already have built a methodoverload for this Class decl = method.getDeclaringClass(); if (method.getDeclaringClass() != desc.cls) { this.populateMembers(decl); ov.ptr = this.classMap.get(decl).getMethod(method); if (ov.ptr == 0) { if (audit != null) audit.failFindMethod(desc, method); throw new RuntimeException("Fail"); } overloadPtrs[--n] = ov.ptr; continue; } // Determine what takes precedence int i = 0; long[] precedencePtrs = new long[ov.children.size()]; for (MethodResolution ch : ov.children) { precedencePtrs[i++] = ch.ptr; } int modifiers = method.getModifiers() & 0xffff; if (isBeanMutator(method)) modifiers |= ModifierCode.BEAN_MUTATOR.value; if (isBeanAccessor(method)) modifiers |= ModifierCode.BEAN_ACCESSOR.value; if (isCallerSensitive(method)) modifiers |= ModifierCode.CALLER_SENSITIVE.value; ov.ptr = typeFactory.defineMethod(context, desc.classPtr, method.toString(), method, precedencePtrs, modifiers); overloadPtrs[--n] = ov.ptr; desc.methods[desc.methodCounter] = ov.ptr; desc.methodIndex[desc.methodCounter] = method; desc.methodCounter++; } return overloadPtrs; } static boolean hasCallerSensitive = false; static { try { java.lang.reflect.Method method = java.lang.Class.class.getDeclaredMethod("forName", String.class); for (Annotation annotation : method.getAnnotations()) { if ("@jdk.internal.reflect.CallerSensitive()".equals(annotation.toString())) { hasCallerSensitive = true; } } } catch (NoSuchMethodException | SecurityException ex) { } } /** * Checks to see if the method is caller sensitive. * * As the annotation is a private internal, we must check by name. * * @param method is the method to be probed. * @return true if caller sensitive. */ public static boolean isCallerSensitive(Method method) { if (hasCallerSensitive) { for (Annotation annotation : method.getAnnotations()) { if ("@jdk.internal.reflect.CallerSensitive()".equals(annotation.toString())) { return true; } } } else { // JDK prior versions prior to 9 do not annotate methods that // require special handling, thus we will just blanket those // classes known to have issues. Class cls = method.getDeclaringClass(); if (cls.equals(java.lang.Class.class) || cls.equals(java.lang.ClassLoader.class) || cls.equals(java.sql.DriverManager.class)) { return true; } } return false; } // // // // // /** * Remove any methods that are not public from a list. * * @param * @param methods * @return a new list containing only public members. */ public static LinkedList filterPublic(T[] methods) { LinkedList out = new LinkedList<>(); for (T method : methods) { if (!Modifier.isPublic(method.getModifiers())) continue; out.add(method); } return out; } /** * Remove any methods that are not public and have been overridden from a * list. * * @param cls * @param methods * @return a new list containing only public members that are not overridden. */ public static LinkedList filterOverridden(Class cls, Method[] methods) { LinkedList out = new LinkedList<>(); for (Method method : methods) { if (!Modifier.isPublic(method.getModifiers()) || isOverridden(cls, method)) continue; out.add(method); } return out; } // // /** * Determines if a method is masked by another in a class. * * @param cls is the class to investigate. * @param method is a method that applies to the class. * @return true if the method is hidden by another method. */ public static boolean isOverridden(Class cls, Method method) { try { return !method.equals(cls.getMethod(method.getName(), method.getParameterTypes())); } catch (NoSuchMethodException | SecurityException ex) { return false; } } /** * Bean accessor is flag is used for property module. *

* Accessors need * * @param method * @return */ private boolean isBeanAccessor(Method method) { if (Modifier.isStatic(method.getModifiers())) return false; if (method.getReturnType().equals(void.class)) return false; if (method.getParameterCount() > 0) return false; if (method.getName().length() < 4) return false; return (method.getName().startsWith("get")); } /** * Bean mutator is flag is used for property module. * * @param method * @return */ private boolean isBeanMutator(Method method) { if (Modifier.isStatic(method.getModifiers())) return false; if (!method.getReturnType().equals(void.class)) return false; if (method.getParameterCount() != 1) return false; if (method.getName().length() < 4) return false; return (method.getName().startsWith("set")); } // // private class Destroyer { final int BLOCK_SIZE = 1024; long[] queue = new long[BLOCK_SIZE]; int index = 0; void add(long v) { if (v == 0) return; queue[index++] = v; if (index == BLOCK_SIZE) flush(); } void add(long[] v) { if (v == null) return; if (v.length > BLOCK_SIZE / 2) { typeFactory.destroy(context, v, v.length); return; } if (index + v.length > BLOCK_SIZE) { flush(); } for (int j = 0; j < v.length; ++j) { queue[index++] = v[j]; } if (index == BLOCK_SIZE) flush(); } void flush() { typeFactory.destroy(context, queue, index); index = 0; } } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pickle/000077500000000000000000000000001501674766700244735ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pickle/ByteBufferInputStream.java000066400000000000000000000052151501674766700315720ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pickle; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.LinkedList; public class ByteBufferInputStream extends InputStream { private LinkedList buffers = new LinkedList<>(); public void put(byte[] bytes) { // We can just wrap the buffer instead of copying it, since the buffer is // wrapped we don't need to write the bytes to it. Wrapping the bytes relies // on the array not being changed before being read. ByteBuffer buffer = ByteBuffer.wrap(bytes); buffers.add(buffer); } @Override public int read() throws IOException { if (buffers.isEmpty()) return -1; ByteBuffer b = buffers.getFirst(); while (b.remaining() == 0) { buffers.removeFirst(); if (buffers.isEmpty()) return -1; // EOF b = buffers.getFirst(); } return b.get() & 0xFF; // Mask with 0xFF to convert signed byte to int (range 0-255) } @Override public int read(byte[] arg0) throws IOException { return read(arg0, 0, arg0.length); } @Override public int read(byte[] buffer, int offset, int len) throws IOException { if (buffer == null) throw new NullPointerException("Buffer cannot be null"); if (offset < 0 || len < 0 || len > buffer.length - offset) throw new IndexOutOfBoundsException("Invalid offset/length parameters"); if (len == 0) return 0; int total = 0; while (len > 0 && !buffers.isEmpty()) { ByteBuffer b = buffers.getFirst(); int remaining = b.remaining(); if (remaining == 0) { buffers.removeFirst(); continue; } int toRead = Math.min(len, remaining); b.get(buffer, offset, toRead); total += toRead; len -= toRead; offset += toRead; } return (total == 0) ? -1 : total; } @Override public void close() throws IOException { buffers.clear(); } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pickle/Decoder.java000066400000000000000000000022471501674766700267100ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pickle; import java.io.IOException; import java.io.ObjectInputStream; public class Decoder { ByteBufferInputStream bb; ObjectInputStream ois = null; public Decoder() throws IOException { bb = new ByteBufferInputStream(); } public Object unpack(byte[] data) throws IOException, ClassNotFoundException { bb.put(data); if (ois == null) ois = new ObjectInputStream(bb); return ois.readObject(); } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pickle/Encoder.java000066400000000000000000000024201501674766700267130ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pickle; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; /** * * @author Karl Einar Nelson */ public class Encoder { ByteArrayOutputStream baos; ObjectOutputStream oos; public Encoder() throws IOException { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); } public byte[] pack(Object obj) throws IOException { oos.writeObject(obj); oos.flush(); byte[] out = baos.toByteArray().clone(); baos.reset(); return out; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pkg/000077500000000000000000000000001501674766700240055ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pkg/JPypePackage.java000066400000000000000000000167241501674766700271650ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pkg; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Modifier; import java.net.URI; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Map; import org.jpype.JPypeClassLoader; import org.jpype.JPypeContext; import org.jpype.JPypeKeywords; /** * Representation of a JPackage in Java. * * This provides the dir and attributes for a JPackage and by extension jpype * imports. Almost all of the actual work happens in the PackageManager which * acts like the classloader to figure out what resource are available. * */ public class JPypePackage { // Name of the package final String pkg; // A mapping from Python names into Paths into the module/jar file system. Map contents; int code; private final JPypeClassLoader classLoader; public JPypePackage(String pkg) { this.pkg = pkg; this.contents = JPypePackageManager.getContentMap(pkg); this.classLoader = ((JPypeClassLoader) (JPypeContext.getInstance().getClassLoader())); this.code = classLoader.getCode(); } /** * Get an object from the package. * * This is used by the importer to create the attributes for `getattro`. The * type returned is polymorphic. We can potentially support any type of * resource (package, classes, property files, xml, data, etc). But for now we * are primarily interested in packages and classes. Packages are returned as * strings as loading the package info is not guaranteed to work. Classes are * returned as classes which are immediately converted into Python wrappers. * We can return other resource types so long as they have either a wrapper * type to place the instance into an Python object directly or a magic * wrapper which will load the resource into a Python object type. * * This should match the acceptable types in getContents so that everything in * the `dir` is also an attribute of JPackage. * * @param name is the name of the resource. * @return the object or null if no resource is found with a matching name. */ public Object getObject(String name) { // We can't use the url contents as the contents may be incomplete due // to bugs in the JVM classloaders. Instead we will have to probe. String basename = pkg + "." + JPypeKeywords.unwrap(name); ClassLoader cl = JPypeContext.getInstance().getClassLoader(); try { // Check if it a package if (JPypePackageManager.isPackage(basename)) { return basename; } // Else probe for a class. Class cls = Class.forName(basename, false, JPypeContext.getInstance().getClassLoader()); if (Modifier.isPublic(cls.getModifiers())) return Class.forName(basename, true, cl); } catch (ClassNotFoundException ex) { // Continue } return null; } /** * Get a list of contents from a Java package. * * This will be used when creating the package `dir` * * @return */ public String[] getContents() { checkCache(); ArrayList out = new ArrayList<>(); for (String key : contents.keySet()) { URI uri = contents.get(key); // If there is anything null, then skip it. if (uri == null) continue; Path p = JPypePackageManager.getPath(uri); // package are acceptable if (Files.isDirectory(p)) out.add(key); // classes must be public else if (uri.toString().endsWith(".class")) { // Make sure it is public if (isPublic(p)) out.add(key); } } return out.toArray(new String[out.size()]); } /** * Determine if a class is public. * * This checks if a class file contains a public class. When importing classes * we do not want to instantiate a class which is not public as it may result * in instantiation of static variables or unwanted class resources. The only * alternative is to read the class file and get the class modifier flags. * Unfortunately, the developers of Java were rather stingy on their byte * allocation and thus the field we want is not in the header but rather * buried after the constant pool. Further as they didn't give the actual size * of the tables in bytes, but rather in entries, that means we have to parse * the whole table just to get the access flags after it. * * @param p * @return */ static boolean isPublic(Path p) { try (InputStream is = Files.newInputStream(p)) { // Allocate a three byte buffer for traversing the constant pool. // The minumum entry is a byte for the type and 2 data bytes. We // will read these three bytes and then based on the type advance // the read pointer to the next entry. ByteBuffer buffer3 = ByteBuffer.allocate(3); // Check the magic ByteBuffer header = ByteBuffer.allocate(4 + 2 + 2 + 2); is.read(header.array()); ((Buffer) header).rewind(); int magic = header.getInt(); if (magic != (int) 0xcafebabe) return false; header.getShort(); // skip major header.getShort(); // skip minor short cpitems = header.getShort(); // get the number of items // Traverse the cp pool for (int i = 0; i < cpitems - 1; ++i) { is.read(buffer3.array()); ((Buffer) buffer3).rewind(); byte type = buffer3.get(); // First byte is the type // Now based on the entry type we will advance the pointer switch (type) { case 1: // Strings are variable length is.skip(buffer3.getShort()); break; case 7: case 8: case 16: case 19: case 20: break; case 15: is.skip(1); break; case 3: case 4: case 9: case 10: case 11: case 12: case 17: case 18: is.skip(2); break; case 5: case 6: is.skip(6); // double and long are special as they are double entries i++; // long and double take two slots break; default: return false; } } // Get the flags is.read(buffer3.array()); ((Buffer) buffer3).rewind(); short flags = buffer3.getShort(); return (flags & 1) == 1; // it is public if bit zero is set } catch (IOException ex) { return false; // If anything goes wrong then it won't be considered a public class. } } void checkCache() { int current = classLoader.getCode(); if (this.code == current) return; this.code = current; this.contents = JPypePackageManager.getContentMap(pkg); } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/pkg/JPypePackageManager.java000066400000000000000000000351431501674766700304540ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pkg; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.ProviderNotFoundException; import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.jpype.JPypeContext; import org.jpype.JPypeKeywords; /** * Manager for the contents of a package. * * This class uses a number of tricks to provide a way to determine what * packages are available in the class loader. It searches the jar path, the * boot path (Java 8), and the module path (Java 9+). It does not currently work * with alternative classloaders. This class was rumored to be unobtainium as * endless posts indicated that it wasn't possible to determine the contents of * a package in general nor to retrieve the package contents, but this appears * to be largely incorrect as the jar and jrt file system provide all the * required methods. * */ public class JPypePackageManager { final static List bases = new ArrayList(); final static List modules = getModules(); final static FileSystemProvider jfsp = getFileSystemProvider("jar"); final static Map env = new HashMap<>(); final static LinkedList fs = new LinkedList<>(); /** * Checks if a package exists. * * @param name is the name of the package. * @return true if this is a Java package either in a jar, module, or in the * boot path. */ public static boolean isPackage(String name) { if (name.indexOf('.') != -1) name = name.replace(".", "/"); if (isModulePackage(name) || isBasePackage(name) || isJarPackage(name)) return true; return false; } /** * Get the list of the contents of a package. * * @param packageName * @return the list of all resources found. */ public static Map getContentMap(String packageName) { Map out = new HashMap<>(); packageName = packageName.replace(".", "/"); // We need to merge all the file systems into one view like the classloader getJarContents(out, packageName); getBaseContents(out, packageName); getModuleContents(out, packageName); return out; } /** * Convert a URI into a path. * * This has special magic methods to deal with jar file systems. * * @param uri is the location of the resource. * @return the path to the uri resource. */ static Path getPath(URI uri) { try { return Paths.get(uri); } catch (java.nio.file.FileSystemNotFoundException ex) { } if (uri.getScheme().equals("jar")) { try { // Limit the number of filesystems open at any one time fs.add(jfsp.newFileSystem(uri, env)); if (fs.size() > 8) fs.removeFirst().close(); return Paths.get(uri); } catch (IOException ex) { } } throw new FileSystemNotFoundException("Unknown filesystem for " + uri); } /** * Retrieve the Jar file system. * * @return */ private static FileSystemProvider getFileSystemProvider(String str) { for (FileSystemProvider fsp : FileSystemProvider.installedProviders()) { if (fsp.getScheme().equals(str)) return fsp; } throw new FileSystemNotFoundException("Unable to find filesystem for " + str); } // /** * Older versions of Java do not have a file system for boot packages. Thus * rather working through the classloader, we will instead probe java to get * the rt.jar. Crypto is a special case as it has its own jar. All other * resources are sourced through the regular jar loading method. */ static { env.put("create", "true"); ClassLoader cl = ClassLoader.getSystemClassLoader(); URI uri = null; try { // This is for Java 8 and earlier in which the API jars are in rt.jar // and jce.jar uri = cl.getResource("java/lang/String.class").toURI(); if (uri != null && uri.getScheme().equals("jar")) { FileSystem fs = jfsp.newFileSystem(uri, env); if (fs != null) bases.add(fs); } uri = cl.getResource("javax/crypto/Cipher.class").toURI(); if (uri != null && uri.getScheme().equals("jar")) { FileSystem fs = jfsp.newFileSystem(uri, env); if (fs != null) bases.add(fs); } } catch (URISyntaxException | IOException ex) { } } private static void getBaseContents(Map out, String packageName) { for (FileSystem b : bases) { collectContents(out, b.getPath(packageName)); } } /** * Check if a name is a package in the java bootstrap classloader. * * @param name * @return */ private static boolean isBasePackage(String name) { try { if (name.isEmpty()) return false; for (FileSystem jar : bases) { if (Files.isDirectory(jar.getPath(name))) return true; } return false; } catch (Exception ex) { throw new RuntimeException("Fail checking package '" + name + "'", ex); } } // // /** * Get a list of all modules. * * This may be many modules or just a few. Limited distributes created using * jlink will only have a portion of the usual modules. * * @return */ static List getModules() { ArrayList out = new ArrayList<>(); try { FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); Path modulePath = fs.getPath("modules"); for (Path module : Files.newDirectoryStream(modulePath)) { out.add(new ModuleDirectory(module)); } } catch (ProviderNotFoundException | IOException ex) { } return out; } /** * Check if a name corresponds to a package in a module. * * @param name * @return true if it is a package. */ private static boolean isModulePackage(String name) { if (modules.isEmpty()) return false; String[] split = name.split("/"); String search = name; if (split.length > 3) search = String.join("/", Arrays.copyOfRange(split, 0, 3)); for (ModuleDirectory module : modules) { if (module.contains(search)) { if (Files.isDirectory(module.modulePath.resolve(name))) return true; } } return false; } /** * Retrieve the contents of a module by package name. * * @param out * @param name */ private static void getModuleContents(Map out, String name) { if (modules.isEmpty()) return; String[] split = name.split("/"); String search = name; if (split.length > 3) search = String.join("/", Arrays.copyOfRange(split, 0, 3)); for (ModuleDirectory module : modules) { if (module.contains(search)) { Path path2 = module.modulePath.resolve(name); if (Files.isDirectory(path2)) collectContents(out, path2); } } } /** * Modules are stored in the jrt filesystem. * * However, that is not a simple flat filesystem by path as the jrt files are * structured by package name. Thus we will need a separate structure which is * rooted at the top of each module. */ private static class ModuleDirectory { List contents = new ArrayList<>(); private final Path modulePath; ModuleDirectory(Path module) { this.modulePath = module; listPackages(contents, module, module, 0); } boolean contains(String path) { for (String s : contents) { if (s.equals(path)) return true; } return false; } private static void listPackages(List o, Path base, Path p, int depth) { try { if (depth >= 3) return; for (Path d : Files.newDirectoryStream(p)) { if (Files.isDirectory(d)) { o.add(base.relativize(d).toString()); listPackages(o, base, d, depth + 1); } } } catch (IOException ex) { } } } // // /** * Checks if a name corresponds to package in a jar file or on the classpath * filesystem. * * Classloaders provide a method to get all resources with a given name. This * is needed because the same package name may appear in multiple jars or * filesystems. We do not need to disambiguate it here, but just get a listing * that we can use to find a resource later. * * @param name is the name of the package to search for. * @return true if the name corresponds to a Java package. */ private static boolean isJarPackage(String name) { ClassLoader cl = JPypeContext.getInstance().getClassLoader(); try { Enumeration resources = cl.getResources(name); while (resources.hasMoreElements()) { URI uri = resources.nextElement().toURI(); if (Files.isDirectory(getPath(uri))) return true; } } catch (IOException | URISyntaxException ex) { } return false; } /** * Retrieve a list of packages and classes stored on a file system or in a * jar. * * @param out is the map to store the result in. * @param packageName is the name of the package */ private static void getJarContents(Map out, String packageName) { ClassLoader cl = JPypeContext.getInstance().getClassLoader(); try { String path = packageName.replace('.', '/'); Enumeration resources = cl.getResources(path); while (resources.hasMoreElements()) { URI resource = resources.nextElement().toURI(); // Handle MRJAR format // MRJAR may not report every directory but instead just the overlay. // So we need to find the original and interogate it first before // checking the version specific part. Worst case we collect the // contents twice. String schemePart = resource.getSchemeSpecificPart(); int index = schemePart.indexOf("!"); if (index != -1) { if (schemePart.substring(index + 1).startsWith("/META-INF/versions/")) { int index2 = schemePart.indexOf('/', index + 20); schemePart = schemePart.substring(0, index + 1) + schemePart.substring(index2); URI resource2 = new URI(resource.getScheme() + ":" + schemePart); Path path3 = getPath(resource2); collectContents(out, path3); } } Path path2 = getPath(resource); collectContents(out, path2); } } catch (IOException | URISyntaxException ex) { } } // // /** * Collect the contents from a path. * * This operates on jars, modules, and filesystems to collect the names of all * resources found. We skip over inner classes as those are accessed under * their included classes. For now we are not screening against other private * symbols. * * @param out is the map to store the result in. * @param path2 is a path holding a directory to probe. */ private static void collectContents(Map out, Path path2) { try { for (Path file : Files.newDirectoryStream(path2)) { String filename = file.getFileName().toString(); if (Files.isDirectory(file)) { // Same implementations add the path separator to the end of toString(). if (filename.endsWith(file.getFileSystem().getSeparator())) filename = filename.substring(0, filename.length() - 1); out.put(JPypeKeywords.wrap(filename), toURI(file)); continue; } // Skip inner classes if (filename.contains("$")) continue; // Include class files if (filename.endsWith(".class")) { String key = JPypeKeywords.wrap(filename.substring(0, filename.length() - 6)); out.put(key, toURI(file)); } // We can add other types of files here and import them in JPypePackage // as required. } } catch (IOException ex) { } } private static URI toURI(Path path) { URI uri = path.toUri(); try { // Java 8 bug https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8131067 // Zip file system provider returns doubly % encoded URIs. We resolve this // by re-encoding the URI after decoding it. uri = new URI( uri.getScheme(), URLDecoder.decode(uri.getSchemeSpecificPart(), StandardCharsets.UTF_8), uri.getFragment() ); // `toASCIIString` ensures the URI is URL encoded with only ascii // characters. This avoids issues in `sun.nio.fs.UnixUriUtils.fromUri` that // naively uses `uri.getRawPath()` despite the possibility that it contains // non-ascii characters that will cause errors. By using `toASCIIString` and // re-wrapping it in a URI object we ensure that the URI is properly // encoded. See: https://github.com/jpype-project/jpype/issues/1194 return new URI(uri.toASCIIString()); } catch (Exception e) { // This exception *should* never occur as we are re-encoding a valid URI. // Throwing a runtime exception avoids java exception handling boilerplate // for a situation that *should* never occur. throw new RuntimeException("Failed to encode URI: " + uri, e); } } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/proxy/000077500000000000000000000000001501674766700244055ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/proxy/JPypeProxy.java000066400000000000000000000124521501674766700273450ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.proxy; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.logging.Level; import java.util.logging.Logger; import org.jpype.JPypeContext; import org.jpype.manager.TypeManager; import org.jpype.ref.JPypeReferenceQueue; /** * * @author Karl Einar Nelson */ public class JPypeProxy implements InvocationHandler { private final static Constructor constructor; private final static JPypeReferenceQueue referenceQueue = JPypeReferenceQueue.getInstance(); JPypeContext context; public long instance; public long cleanup; Class[] interfaces; ClassLoader cl = ClassLoader.getSystemClassLoader(); public static Object missing = new Object(); // See following link for Java 8 default access implementation // https://blog.jooq.org/correct-reflective-access-to-interface-default-methods-in-java-8-9-10/ static { Constructor c = null; if (System.getProperty("java.version").startsWith("1.")) { try { c = Lookup.class .getDeclaredConstructor(Class.class); c.setAccessible(true); } catch (NoSuchMethodException | SecurityException ex) { Logger.getLogger(JPypeProxy.class.getName()).log(Level.SEVERE, null, ex); } } constructor = c; } public static JPypeProxy newProxy(JPypeContext context, long instance, long cleanup, Class[] interfaces) { JPypeProxy proxy = new JPypeProxy(); proxy.context = context; proxy.instance = instance; proxy.interfaces = interfaces; proxy.cleanup = cleanup; // Proxies must point to the correct class loader. For most cases the // system classloader is find. But if the class is in a custom classloader // we need to use that one instead for (Class cls : interfaces) { ClassLoader icl = cls.getClassLoader(); if (icl != null && icl != proxy.cl) proxy.cl = icl; } return proxy; } public Object newInstance() { Object out = Proxy.newProxyInstance(cl, interfaces, this); referenceQueue.registerRef(out, instance, cleanup); return out; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (context.isShutdown()) throw new RuntimeException("Proxy called during shutdown"); // We can save a lot of effort on the C++ side by doing all the // type lookup work here. TypeManager typeManager = context.getTypeManager(); long returnType; long[] parameterTypes; synchronized (typeManager) { returnType = typeManager.findClass(method.getReturnType()); Class[] types = method.getParameterTypes(); parameterTypes = new long[types.length]; for (int i = 0; i < types.length; ++i) { parameterTypes[i] = typeManager.findClass(types[i]); } } // Check first to see if Python has implementated it Object result = hostInvoke(context.getContext(), method.getName(), instance, returnType, parameterTypes, args, missing); // If we get a good result than return it if (result != missing) return result; // If it is a default method in the interface then we have to invoke it using special reflection. if (method.isDefault()) { try { Class cls = method.getDeclaringClass(); // Java 8 if (constructor != null) { return constructor.newInstance(cls) .findSpecial(cls, method.getName(), MethodType.methodType(method.getReturnType()), cls) .bindTo(proxy) .invokeWithArguments(args); } return MethodHandles.lookup() .findSpecial(cls, method.getName(), MethodType.methodType(method.getReturnType()), cls) .bindTo(proxy) .invokeWithArguments(args); } catch (java.lang.IllegalAccessException ex) { throw new RuntimeException(ex); } } // Else throw... (this should never happen as proxies are checked when created.) throw new NoSuchMethodError(method.getName()); } private static native Object hostInvoke(long context, String name, long pyObject, long returnType, long[] argsTypes, Object[] args, Object bad); } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/ref/000077500000000000000000000000001501674766700240005ustar00rootroot00000000000000jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/ref/JPypeReference.java000066400000000000000000000026661501674766700275230ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.ref; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; /** * (internal) Reference to a PyObject*. */ class JPypeReference extends PhantomReference { long hostReference; long cleanup; int pool; int index; public JPypeReference(ReferenceQueue arg1, Object javaObject, long host, long cleanup) { super(javaObject, arg1); this.hostReference = host; this.cleanup = cleanup; } @Override public int hashCode() { return (int) hostReference; } @Override public boolean equals(Object arg0) { if (!(arg0 instanceof JPypeReference)) { return false; } return ((JPypeReference) arg0).hostReference == hostReference; } } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/ref/JPypeReferenceNative.java000066400000000000000000000011501501674766700306550ustar00rootroot00000000000000package org.jpype.ref; import java.lang.reflect.Method; /** * * @author nelson85 */ public class JPypeReferenceNative { /** * Native hook to delete a native resource. * * @param host is the address of memory in C. * @param cleanup is the address the function to cleanup the memory. */ public static native void removeHostReference(long host, long cleanup); /** * Triggered by the sentinel when a GC starts. */ public static native void wake(); /** * Initialize resources. * * @param self * @param m */ public static native void init(Object self, Method m); } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/ref/JPypeReferenceQueue.java000066400000000000000000000120341501674766700305160ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.ref; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; /** * Reference queue holds the life of python objects to be as long as java items. *

* Any java class that holds a pointer into python needs to have a reference so * that python object does not go away if the references in python fall to zero. * JPype will add an extra reference to the object which is removed when the * python object dies. * * @author smenard * */ final public class JPypeReferenceQueue extends ReferenceQueue { private final static JPypeReferenceQueue INSTANCE = new JPypeReferenceQueue(); private JPypeReferenceSet hostReferences; private boolean isStopped = false; private Thread queueThread; private Object queueStopMutex = new Object(); private PhantomReference sentinel = null; public static JPypeReferenceQueue getInstance() { return INSTANCE; } private JPypeReferenceQueue() { super(); this.hostReferences = new JPypeReferenceSet(); addSentinel(); JPypeReferenceNative.removeHostReference(0, 0); try { JPypeReferenceNative.init(this, getClass().getDeclaredMethod("registerRef", Object.class, Long.TYPE, Long.TYPE)); } catch (NoSuchMethodException | SecurityException ex) { throw new RuntimeException(ex); } } /** * (internal) Binds the lifetime of a Python object to a Java object. *

* JPype adds an extra reference to a PyObject* and then calls this method to * hold that reference until the Java object is garbage collected. * * @param javaObject is the object to bind the lifespan to. * @param host is the pointer to the host object. * @param cleanup is the pointer to the function to call to delete the * resource. */ public void registerRef(Object javaObject, long host, long cleanup) { if (cleanup == 0) return; if (isStopped) { JPypeReferenceNative.removeHostReference(host, cleanup); } else { JPypeReference ref = new JPypeReference(this, javaObject, host, cleanup); hostReferences.add(ref); } } /** * Start the threading queue. */ public void start() { isStopped = false; queueThread = new Thread(new Worker(), "Python Reference Queue"); queueThread.setDaemon(true); queueThread.start(); } /** * Stops the reference queue. *

* This is called by jpype when the jvm shuts down. */ public void stop() { try { synchronized (queueStopMutex) { synchronized (this) { isStopped = true; queueThread.interrupt(); } // wait for the thread to finish ... queueStopMutex.wait(10000); } } catch (InterruptedException ex) { // who cares ... } // Empty the queue. hostReferences.flush(); } /** * Checks the status of the reference queue. * * @return true is the queue is running. */ public boolean isRunning() { return !isStopped; } /** * Get the number of items in the reference queue. * * @return the number of python resources held. */ public int getQueueSize() { return this.hostReferences.size(); } // /** * Thread to monitor the queue and delete resources. */ private class Worker implements Runnable { @Override public void run() { while (!isStopped) { try { // Check if a ref has been queued. and check if the thread has been // stopped every 0.25 seconds JPypeReference ref = (JPypeReference) remove(250); if (ref == sentinel) { addSentinel(); JPypeReferenceNative.wake(); continue; } if (ref != null) { long hostRef = ref.hostReference; long cleanup = ref.cleanup; hostReferences.remove(ref); JPypeReferenceNative.removeHostReference(hostRef, cleanup); } } catch (InterruptedException ex) { // don't know why ... don't really care ... } } synchronized (queueStopMutex) { queueStopMutex.notifyAll(); } } } final void addSentinel() { sentinel = new JPypeReference(this, new byte[0], 0, 0); } // } jpype-1.6.0/native/jpype_module/src/main/java/org/jpype/ref/JPypeReferenceSet.java000066400000000000000000000060611501674766700301700ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.ref; import java.util.ArrayList; /** * * @author nelson85 */ public class JPypeReferenceSet { static final int SIZE = 256; ArrayList pools = new ArrayList<>(); Pool current; private int items; JPypeReferenceSet() { } int size() { return items; } /** * Add a reference to the set. * * This should be O(1). * * @param ref */ synchronized void add(JPypeReference ref) { if (ref.cleanup == 0) return; this.items++; if (current == null) { current = new Pool(pools.size()); pools.add(current); } if (current.add(ref)) { // It is full current = null; // Find a free pool for (Pool pool : pools) { if (pool.tail < SIZE) { current = pool; return; } } } } /** * Remove a reference from the set. * * @param ref */ synchronized void remove(JPypeReference ref) { if (ref.cleanup == 0) return; pools.get(ref.pool).remove(ref); this.items--; ref.cleanup = 0; ref.pool = -1; } /** * Release all resources. * * This is triggered by shutdown to release an current Python references that * are being held. * */ void flush() { for (Pool pool : pools) { for (int i = 0; i < pool.tail; ++i) { JPypeReference ref = pool.entries[i]; long hostRef = ref.hostReference; long cleanup = ref.cleanup; // This is a sanity check to prevent calling a cleanup with a null // pointer, it would only occur if we failed to manage a deleted // item. if (cleanup == 0) continue; ref.cleanup = 0; JPypeReferenceNative.removeHostReference(hostRef, cleanup); } pool.tail = 0; } } // static class Pool { JPypeReference[] entries = new JPypeReference[SIZE]; int tail; int id; Pool(int id) { this.id = id; } boolean add(JPypeReference ref) { ref.pool = id; ref.index = tail; entries[tail++] = ref; return (tail == entries.length); } void remove(JPypeReference ref) { entries[ref.index] = entries[--tail]; entries[ref.index].index = ref.index; } } // } jpype-1.6.0/native/python/000077500000000000000000000000001501674766700154775ustar00rootroot00000000000000jpype-1.6.0/native/python/include/000077500000000000000000000000001501674766700171225ustar00rootroot00000000000000jpype-1.6.0/native/python/include/jp_pythontypes.h000077500000000000000000000234761501674766700224110ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef JP_PYTHONTYPES_H_ #define JP_PYTHONTYPES_H_ #include #include /** * This set of source are mostly sugar with some light weight * reference management. Each type holds a reference for the duration of its * scope. If the PyObject must survive the reference container lifespan * then a keep must be called. keep() should only appear when returning from * the python module. Any earlier than the return provides an opportunity for * an exception to be throw which may leak. * * For this module to function properly, each call needs to be check against the * python object model to verify. * 1) Does a call return an object as a new reference or a borrowed one. * 2) Does a call set an error that must be checked. * 3) Does the call steal a reference to an object passed into it. * * Calls should always accept a raw PyObject* and return JPPyObject if * the return can be an new object or PyObject* if the return is a borrowed * reference. Obviously holding a borrowed reference can create issues as * nothing prevents a borrowed object from falling out of the list and * being deleted. * */ // Note: Python uses a sized size type. Thus we will map it to jlong. // Note: for conversions we will use jint and jlong types so that we map directly to java. // Note: Where possible we will use std::string in place of const char* #ifndef PyObject_HEAD struct _object; typedef _object PyObject; #endif /** Reference to a Python object. * * This creates a reference on creation and deletes it on destruction. * * Because there is a cost associated with creating wrappers, most * methods should be static if they don't actually require management. * * Methods will return either a bare PyObject* if they hold the * object like a container, or a wrapper which will control the lifespan * of the object. * * Python has a lot of difference cases for how it returns an object. * - It can give a new object or set an error. * - It can return NULL indicating there is no object. * - It can give a borrowed object or NULL if it doesn't exist. * - Or we can just have an existing object we want to use. * * With all these different methods, we need to have a policy that * state how we want this object reference to be treated. Each * policy will produce different actions on the creation of * a reference wrapper. * */ class JPPyObject { /** Create a new reference to a Python object. * * @param obj is the python object. */ explicit JPPyObject(PyObject* obj); public: /** * This policy is used if we need to hold a reference to an existing * object for some duration. The object may be null. * * Increment reference count if not null, and decrement when done. */ static JPPyObject use(PyObject* obj); /** * This policy is used when we are given a new reference that we must * destroy. This will steal a reference. * * claim reference, and decremented when done. Clears errors if NULL. */ static JPPyObject accept(PyObject* obj); /** * This policy is used when we are given a new reference that we must * destroy. This will steal a reference. * * Assert not null, claim reference, and decremented when done. * Will throw an exception in the object is null. */ static JPPyObject claim(PyObject* obj); /** * This policy is used when we are capturing an object returned from a python * call that we are responsible for. This will steal a reference. * * Check for errors, assert not null, then claim. * Will throw an exception an error occurs. */ static JPPyObject call(PyObject* obj); JPPyObject() : m_PyObject(nullptr) { } JPPyObject(const JPPyObject &self); ~JPPyObject(); JPPyObject& operator=(const JPPyObject& o); /** * Keep an object by creating a reference. * * This should only appear in the return statement in the cpython module. * The reference must not be null. Keep invalidates this handle from any * further use as you were supposed to have called return. * * @return the pointer to the Python object. */ PyObject* keep(); /** Used in special case of exception handling. */ PyObject* keepNull() { PyObject *out = m_PyObject; m_PyObject = nullptr; return out; } /** Access the object. This should never appear in * a return statement. */ PyObject* get() { return m_PyObject; } /** Determine if this python reference is null. * * @returns true if null. */ bool isNull() const { return m_PyObject == nullptr; } /** * Get a reference to Python None. */ static JPPyObject getNone(); void incref(); void decref(); protected: PyObject* m_PyObject{nullptr}; } ; /**************************************************************************** * String ***************************************************************************/ /** Wrapper for the concept of a Python string. * * For the purposes of this interface we will hide the differences * between unicode and string. */ class JPPyString : public JPPyObject { public: /** Check if the object is a bytes or unicode. * * @returns true if the object is bytes or unicode. */ static bool check(PyObject* obj); /** Create a new string from utf8 encoded string. * Note: java utf8 is not utf8. * * Python2 produced str unless unicode is set to * true. Python3 will always produce a unicode string. * * @param str is the string to convert */ static JPPyObject fromStringUTF8(const string& str); /** Get a UTF-8 encoded string from Python */ static string asStringUTF8(PyObject* obj); static JPPyObject fromCharUTF16(jchar c); static bool checkCharUTF16(PyObject* obj); static jchar asCharUTF16(PyObject* obj); } ; /**************************************************************************** * Container types ***************************************************************************/ /** Wrapper for a Python sequence. * * In most cases, we will not use this directly, but rather convert to * a JPPyObjectVector for easy access. */ class JPPySequence { JPPyObject m_Sequence; explicit JPPySequence(PyObject* obj) { m_Sequence = JPPyObject::use(obj); } public: /** Needed for named constructor. */ JPPySequence(const JPPySequence& seq) = default; /** Use an existing Python sequence in C++. */ static JPPySequence use(PyObject* obj) { return JPPySequence(obj); } JPPyObject operator[](jlong i); jlong size(); JPPySequence& operator= (const JPPySequence&) = delete; } ; /** For purposes of efficiency, we should only convert a sequence once per * method call. This class is to support that operation. * * THis object is read only. */ class JPPyObjectVector { public: /** Use an existing sequence members as a vector. */ explicit JPPyObjectVector(PyObject* sequence); /** Use an existing sequence members as a vector plus the * object instance. */ JPPyObjectVector(PyObject* inst, PyObject* sequence); size_t size() const { return m_Contents.size(); } PyObject* operator[](Py_ssize_t i) { return m_Contents[i].get(); } JPPyObject& getInstance() { return m_Instance; } // disallow copying JPPyObjectVector& operator= (const JPPyObjectVector& ) = delete; JPPyObjectVector(const JPPyObjectVector& ) = delete; private: JPPyObject m_Instance; JPPyObject m_Sequence; vector m_Contents; } ; /**************************************************************************** * Error handling ***************************************************************************/ /** Front end for all Python exception handling. * * To issue an error from within the C++ layer use the appropriate * JP_RAISE_* macro. If within the Python extension module use * the Python interface. * */ namespace JPPyErr { bool fetch(JPPyObject& exceptionClass, JPPyObject& exceptionValue, JPPyObject& exceptionTrace); void restore(JPPyObject& exceptionClass, JPPyObject& exceptionValue, JPPyObject& exceptionTrace); } /** Memory management for handling a python exception currently in progress. */ class JPPyErrFrame { public: JPPyObject m_ExceptionClass; JPPyObject m_ExceptionValue; JPPyObject m_ExceptionTrace; bool good; JPPyErrFrame(); ~JPPyErrFrame(); void clear(); void normalize(); } ; /** Used to establish a python lock when called from a * thread external to python such a java. */ class JPPyCallAcquire { public: /** Acquire the lock. */ JPPyCallAcquire(); /* Release the lock. */ ~JPPyCallAcquire(); private: long m_State; } ; /** Used when leaving python to an external potentially * blocking call */ class JPPyCallRelease { public: /** Release the lock. */ JPPyCallRelease(); /** Reacquire the lock. */ ~JPPyCallRelease(); private: PyThreadState* m_State1; } ; class JPPyBuffer { public: /** * Attempt to create a buffer view. * * If this fails then valid will return false and * PyExc_BufferError will be set. Clear the exception if * the alternative methods are used. * * @param obj * @param flags */ JPPyBuffer(PyObject* obj, int flags); ~JPPyBuffer(); char *getBufferPtr(std::vector& indices) const; Py_buffer& getView() { return m_View; } bool valid() const { return m_Valid; } private: Py_buffer m_View{}; bool m_Valid; } ; #endif jpype-1.6.0/native/python/include/pyjp.h000077500000000000000000000155711501674766700202710ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #ifndef PYJP_H #define PYJP_H #include #include "jpype.h" #include "jp_pythontypes.h" class JPStackInfo; #ifdef JP_TRACING_ENABLE #define JP_PY_TRY(...) \ JPypeTracer _trace(__VA_ARGS__); \ try { do {} while(0) #define JP_PY_CATCH(...) \ } catch(...) { \ PyJPModule_rethrow(JP_STACKINFO()); } \ return __VA_ARGS__ #define JP_PY_CATCH_NONE(...) } catch(...) {} return __VA_ARGS__ #else #ifndef JP_INSTRUMENTATION #define JP_PY_TRY(...) try { do {} while(0) #else #define JP_PY_TRY(...) JP_TRACE_IN(__VA_ARGS__) #endif #define JP_PY_CATCH(...) } catch(...) \ { PyJPModule_rethrow(JP_STACKINFO()); } \ return __VA_ARGS__ #define JP_PY_CATCH_NONE(...) } catch(...) {} return __VA_ARGS__ #endif // Macro to all after executing a Python command that can result in // a failure to convert it to an exception. #define JP_PY_CHECK() { if (PyErr_Occurred() != 0) JP_RAISE_PYTHON(); } // GCOVR_EXCL_LINE #ifdef __cplusplus extern "C" { #endif // Needed to write common code with older versions #ifndef Py_TRASHCAN_BEGIN // Introduced in Python 3.8 #define Py_TRASHCAN_BEGIN(X, Y) #define Py_TRASHCAN_END #endif PyMODINIT_FUNC PyInit__jpype(); /** * Set the current exception as the cause of a new exception. * * @param exception * @param str */ void PyJP_SetStringWithCause(PyObject *exception, const char *str); /** * Get a new reference to a method or property in the type dictionary without * dereferencing. * * @param type * @param attr_name * @return */ PyObject* PyJP_GetAttrDescriptor(PyTypeObject *type, PyObject *attr_name); /** * Fast check to see if a type derives from another. * * This depends on the MRO order. It is useful of our base types where * the order is fixed. * * @param type * @param obj * @return 1 if object derives from type. */ int PyJP_IsInstanceSingle(PyObject* obj, PyTypeObject* type); int PyJP_IsSubClassSingle(PyTypeObject* type, PyTypeObject* obj); struct PyJPArray { PyObject_HEAD JPArray *m_Array; JPArrayView *m_View; } ; struct PyJPClassHints { PyObject_HEAD JPClassHints *m_Hints; } ; struct PyJPProxy { PyObject_HEAD JPProxy* m_Proxy; PyObject* m_Target; PyObject* m_Dispatch; bool m_Convert; } ; struct JPConversionInfo { PyObject *ret; PyObject *exact; PyObject *implicit; PyObject *attributes; PyObject *expl; PyObject *none; } ; // JPype types extern PyTypeObject *PyJPArray_Type; extern PyTypeObject *PyJPArrayPrimitive_Type; extern PyTypeObject *PyJPBuffer_Type; extern PyTypeObject *PyJPClass_Type; extern PyTypeObject *PyJPComparable_Type; extern PyTypeObject *PyJPMethod_Type; extern PyTypeObject *PyJPObject_Type; extern PyTypeObject *PyJPProxy_Type; extern PyTypeObject *PyJPException_Type; extern PyTypeObject *PyJPNumberLong_Type; extern PyTypeObject *PyJPNumberFloat_Type; extern PyTypeObject *PyJPNumberBool_Type; extern PyTypeObject *PyJPChar_Type; // JPype resources extern PyObject *PyJPModule; extern PyObject *_JArray; extern PyObject *_JChar; extern PyObject *_JObject; extern PyObject *_JInterface; extern PyObject *_JException; extern PyObject *_JClassPre; extern PyObject *_JClassPost; extern PyObject *_JClassDoc; extern PyObject *_JMethodDoc; extern PyObject *_JMethodAnnotations; extern PyObject *_JMethodCode; extern PyObject *_JObjectKey; extern PyObject *_JVMNotRunning; extern PyObject* PyJPClassMagic; extern JPContext* JPContext_global; // Class wrapper functions int PyJPClass_Check(PyObject* obj); PyObject *PyJPClass_FromSpecWithBases(PyType_Spec *spec, PyObject *bases); // Class methods to add to the spec tables PyObject *PyJPValue_alloc(PyTypeObject* type, Py_ssize_t nitems ); void PyJPValue_free(void* obj); void PyJPValue_finalize(void* obj); int PyJPValue_traverse(PyObject *self, visitproc visit, void *arg); int PyJPValue_clear(PyObject *self); // Generic methods that operate on any object with a Java slot PyObject *PyJPValue_str(PyObject* self); bool PyJPValue_hasJavaSlot(PyTypeObject* type); Py_ssize_t PyJPValue_getJavaSlotOffset(PyObject* self); JPValue *PyJPValue_getJavaSlot(PyObject* obj); // Access point for creating classes PyObject *PyJPModule_getClass(PyObject* module, PyObject *obj); PyObject *PyJPValue_getattro(PyObject *obj, PyObject *name); int PyJPValue_setattro(PyObject *self, PyObject *name, PyObject *value); PyObject *PyJPChar_Create(PyTypeObject *type, Py_UCS2 p); #ifdef __cplusplus } #endif void PyJPClass_hook(JPJavaFrame &frame, JPClass* cls); // C++ methods JPPyObject PyJPArray_create(JPJavaFrame &frame, PyTypeObject* wrapper, const JPValue& value); JPPyObject PyJPBuffer_create(JPJavaFrame &frame, PyTypeObject *type, const JPValue & value); JPPyObject PyJPClass_create(JPJavaFrame &frame, JPClass* cls); JPPyObject PyJPNumber_create(JPJavaFrame &frame, JPPyObject& wrapper, const JPValue& value); JPPyObject PyJPField_create(JPField* m); JPPyObject PyJPMethod_create(JPMethodDispatch *m, PyObject *instance); JPClass* PyJPClass_getJPClass(PyObject* obj); JPProxy* PyJPProxy_getJPProxy(PyObject* obj); void PyJPModule_rethrow(const JPStackInfo& info); void PyJPValue_assignJavaSlot(JPJavaFrame &frame, PyObject* obj, const JPValue& value); bool PyJPValue_isSetJavaSlot(PyObject* self); JPPyObject PyTrace_FromJavaException(JPJavaFrame& frame, jthrowable th, jthrowable prev); void PyJPException_normalize(JPJavaFrame frame, JPPyObject exc, jthrowable th, jthrowable enclosing); #define _ASSERT_JVM_RUNNING(context) assertJVMRunning((JPContext*)context, JP_STACKINFO()) /** * Use this when getting the context where the context must be running. * * The context needs to be accessed before accessing and JPClass* or other * internal structured. Those resources are owned by the JVM and thus * will be deleted when the JVM is shutdown. This method will throw if the * JVM is not running. * * If the context may or many not be running access JPContext_global directly. */ inline JPContext* PyJPModule_getContext() { #ifdef JP_INSTRUMENTATION PyJPModuleFault_throw(compile_hash("PyJPModule_getContext")); #endif JPContext* context = JPContext_global; _ASSERT_JVM_RUNNING(context); // GCOVR_EXCL_LINE return context; } void PyJPModule_loadResources(PyObject* module); #endif /* PYJP_H */ jpype-1.6.0/native/python/jp_pythontypes.cpp000066400000000000000000000300541501674766700213040ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" /**************************************************************************** * Base object ***************************************************************************/ static void assertValid(PyObject *obj) { #ifndef Py_GIL_DISABLED if (Py_REFCNT(obj) >= 1) return; // GCOVR_EXCL_START // At this point our car has traveled beyond the end of the // cliff and it will hit the ground some twenty // python calls later with a nearly untracable fault, thus // rather than waiting for the inevitable, we chose to take // a noble death here. JP_TRACE_PY("pyref FAULT", obj); JP_RAISE(PyExc_SystemError, "Deleted reference"); // GCOVR_EXCL_STOP #else return; // GIL is disabled; we assume obj is valid #endif } /** * This policy is used if we need to hold a reference to an existing * object for some duration. The object may be null. * * Increment reference count if not null, and decrement when done. */ JPPyObject JPPyObject::use(PyObject* obj) { JP_TRACE_PY("pyref use(inc)", obj); if (obj != nullptr) { assertValid(obj); Py_INCREF(obj); } return JPPyObject(obj); } /** * This policy is used when we are given a new reference that we must * destroy. This will steal a reference. * * claim reference, and decremented when done. */ JPPyObject JPPyObject::accept(PyObject* obj) { JP_TRACE_PY("pyref new(accept)", obj); if (obj == nullptr) PyErr_Clear(); return JPPyObject(obj); } /** * This policy is used when we are given a new reference that we must * destroy. This will steal a reference. * * Assert not null, claim reference, and decremented when done. * Will throw an exception if the object is null. */ JPPyObject JPPyObject::claim(PyObject* obj) { JP_TRACE_PY("pyref new(claim)", obj); ASSERT_NOT_NULL(obj); assertValid(obj); return JPPyObject(obj); } /** * This policy is used when we are capturing an object returned from a python * call that we are responsible for. This will steal a reference. * * Check for errors, assert not null, then claim. * Will throw an exception an error occurs. */ JPPyObject JPPyObject::call(PyObject* obj) { JP_TRACE_PY("pyref new(call)", obj); JP_PY_CHECK(); ASSERT_NOT_NULL(obj); assertValid(obj); return JPPyObject(obj); } JPPyObject::JPPyObject(PyObject* obj) : m_PyObject(obj) { } JPPyObject::JPPyObject(const JPPyObject &self) : m_PyObject(self.m_PyObject) { if (m_PyObject != nullptr) { incref(); JP_TRACE_PY("pyref copy ctor(inc)", m_PyObject); } } JPPyObject::~JPPyObject() { if (m_PyObject != nullptr) { JP_TRACE_PY("pyref dtor(dec)", m_PyObject); decref(); } else { JP_TRACE_PY("pyref dtor(null)", m_PyObject); } } JPPyObject& JPPyObject::operator=(const JPPyObject& self) { if (m_PyObject == self.m_PyObject) return *this; if (m_PyObject != nullptr) { JP_TRACE_PY("pyref op=(dec)", m_PyObject); decref(); } m_PyObject = self.m_PyObject; if (m_PyObject != nullptr) { incref(); JP_TRACE_PY("pyref op=(inc)", m_PyObject); } return *this; } PyObject* JPPyObject::keep() { // This can only happen if we have a fatal error in our reference // management system. It should never be triggered by the user. if (m_PyObject == nullptr) { JP_RAISE(PyExc_SystemError, "Attempt to keep null reference"); // GCOVR_EXCL_LINE } JP_TRACE_PY("pyref keep ", m_PyObject); PyObject *out = m_PyObject; m_PyObject = nullptr; return out; } void JPPyObject::incref() { assertValid(m_PyObject); Py_INCREF(m_PyObject); } void JPPyObject::decref() { assertValid(m_PyObject); Py_DECREF(m_PyObject); m_PyObject = nullptr; } JPPyObject JPPyObject::getNone() { return JPPyObject::use(Py_None); } /**************************************************************************** * String ***************************************************************************/ JPPyObject JPPyString::fromCharUTF16(jchar c) { #if defined(PYPY_VERSION) wchar_t buf[1]; buf[0] = c; return JPPyObject::call(PyUnicode_FromWideChar(buf, 1)); #else if (c < 128) { char c1 = (char) c; return JPPyObject::call(PyUnicode_FromStringAndSize(&c1, 1)); } JPPyObject buf = JPPyObject::call(PyUnicode_New(1, 65535)); Py_UCS4 c2 = c; PyUnicode_WriteChar(buf.get(), 0, c2); JP_PY_CHECK(); PyUnicode_READY(buf.get()); return buf; #endif } bool JPPyString::checkCharUTF16(PyObject* pyobj) { JP_TRACE_IN("JPPyString::checkCharUTF16"); if (PyIndex_Check(pyobj)) return true; if (PyUnicode_Check(pyobj) && PyUnicode_GetLength(pyobj) == 1) return true; if (PyBytes_Check(pyobj) && PyBytes_Size(pyobj) == 1) return true; return false; JP_TRACE_OUT; // GCOVR_EXCL_LINE } jchar JPPyString::asCharUTF16(PyObject* pyobj) { if (PyIndex_Check(pyobj)) { jlong val = PyLong_AsLongLong(pyobj); if (val < 0 || val > 65535) JP_RAISE(PyExc_OverflowError, "Unable to convert int into char range"); return (jchar) val; } #if defined(PYPY_VERSION) if (PyBytes_Check(pyobj)) { int sz = PyBytes_Size(pyobj); if (sz != 1) JP_RAISE(PyExc_ValueError, "Char must be length 1"); jchar c = PyBytes_AsString(pyobj)[0]; if (PyErr_Occurred()) JP_RAISE_PYTHON(); return c; } if (PyUnicode_Check(pyobj)) { if (PyUnicode_GetLength(pyobj) > 1) JP_RAISE(PyExc_ValueError, "Char must be length 1"); PyUnicode_READY(pyobj); Py_UCS4 value = PyUnicode_READ_CHAR(pyobj, 0); if (value > 0xffff) { JP_RAISE(PyExc_ValueError, "Unable to pack 4 byte unicode into Java char"); } return value; } #else if (PyBytes_Check(pyobj)) { Py_ssize_t sz = PyBytes_Size(pyobj); if (sz != 1) JP_RAISE(PyExc_ValueError, "Char must be length 1"); jchar c = PyBytes_AsString(pyobj)[0]; JP_PY_CHECK(); return c; } if (PyUnicode_Check(pyobj)) { if (PyUnicode_GetLength(pyobj) > 1) JP_RAISE(PyExc_ValueError, "Char must be length 1"); PyUnicode_READY(pyobj); Py_UCS4 value = PyUnicode_ReadChar(pyobj, 0); if (value > 0xffff) { JP_RAISE(PyExc_ValueError, "Unable to pack 4 byte unicode into Java char"); } return value; } #endif PyErr_Format(PyExc_TypeError, "Unable to convert '%s' to Java char", Py_TYPE(pyobj)->tp_name); JP_RAISE_PYTHON(); } /** Check if the object is a bytes or unicode. * * @returns true if the object is bytes or unicode. */ bool JPPyString::check(PyObject* obj) { return PyUnicode_Check(obj) || PyBytes_Check(obj); } /** Create a new string from utf8 encoded string. * Note: java utf8 is not utf8. */ JPPyObject JPPyString::fromStringUTF8(const string& str) { auto len = static_cast(str.size()); // Python 3 is always unicode JPPyObject bytes = JPPyObject::call(PyBytes_FromStringAndSize(str.c_str(), len)); return JPPyObject::call(PyUnicode_FromEncodedObject(bytes.get(), "UTF-8", "strict")); } string JPPyString::asStringUTF8(PyObject* pyobj) { JP_TRACE_IN("JPPyUnicode::asStringUTF8"); ASSERT_NOT_NULL(pyobj); if (PyUnicode_Check(pyobj)) { Py_ssize_t size = 0; char *buffer = nullptr; JPPyObject val = JPPyObject::call(PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict")); PyBytes_AsStringAndSize(val.get(), &buffer, &size); JP_PY_CHECK(); if (buffer != nullptr) return {buffer, static_cast(size)}; else return {}; } else if (PyBytes_Check(pyobj)) { Py_ssize_t size = 0; char *buffer = nullptr; PyBytes_AsStringAndSize(pyobj, &buffer, &size); JP_PY_CHECK(); return {buffer, static_cast(size)}; } // GCOVR_EXCL_START JP_RAISE(PyExc_TypeError, "Failed to convert to string."); return {}; JP_TRACE_OUT; // GCOVR_EXCL_STOP } /**************************************************************************** * Container types ***************************************************************************/ jlong JPPySequence::size() { return PySequence_Size(m_Sequence.get()); } JPPyObject JPPySequence::operator[](jlong i) { return JPPyObject::call(PySequence_GetItem(m_Sequence.get(), (Py_ssize_t) i)); // new reference } JPPyObjectVector::JPPyObjectVector(PyObject* sequence) { m_Sequence = JPPyObject::use(sequence); auto n = PySequence_Size(m_Sequence.get()); m_Contents.resize(n); for (decltype(n) i = 0; i < n; ++i) { m_Contents[i] = JPPyObject::call(PySequence_GetItem(m_Sequence.get(), i)); } } JPPyObjectVector::JPPyObjectVector(PyObject* inst, PyObject* sequence) { m_Instance = JPPyObject::use(inst); m_Sequence = JPPyObject::use(sequence); Py_ssize_t n = 0; if (sequence != nullptr) n = PySequence_Size(m_Sequence.get()); m_Contents.resize(n + 1); for (decltype(n) i = 0; i < n; ++i) { m_Contents[i + 1] = JPPyObject::call(PySequence_GetItem(m_Sequence.get(), i)); } m_Contents[0] = m_Instance; } bool JPPyErr::fetch(JPPyObject& exceptionClass, JPPyObject& exceptionValue, JPPyObject& exceptionTrace) { PyObject *v1, *v2, *v3; PyErr_Fetch(&v1, &v2, &v3); if (v1 == nullptr && v2 == nullptr && v3 == nullptr) return false; exceptionClass = JPPyObject::accept(v1); exceptionValue = JPPyObject::accept(v2); exceptionTrace = JPPyObject::accept(v3); return true; } void JPPyErr::restore(JPPyObject& exceptionClass, JPPyObject& exceptionValue, JPPyObject& exceptionTrace) { PyErr_Restore(exceptionClass.keepNull(), exceptionValue.keepNull(), exceptionTrace.keepNull()); } JPPyCallAcquire::JPPyCallAcquire() { m_State = (long) PyGILState_Ensure(); } JPPyCallAcquire::~JPPyCallAcquire() { PyGILState_Release((PyGILState_STATE) m_State); } // This is used when leaving python from to perform some JPPyCallRelease::JPPyCallRelease() { // Release the lock and set the thread state to NULL m_State1 = PyEval_SaveThread(); } JPPyCallRelease::~JPPyCallRelease() { // Re-acquire the lock PyEval_RestoreThread(m_State1); } JPPyBuffer::JPPyBuffer(PyObject* obj, int flags) { int ret = PyObject_GetBuffer(obj, &m_View, flags); m_Valid = (ret != -1); } JPPyBuffer::~JPPyBuffer() { if (m_Valid) PyBuffer_Release(&m_View); } char *JPPyBuffer::getBufferPtr(std::vector& indices) const { char *pointer = (char*) m_View.buf; // No shape is just a 1D array if (m_View.shape == nullptr) { return pointer; } // No strides is C contiguous ND array if (m_View.strides == nullptr) { Py_ssize_t index = 0; for (int i = 0; i < m_View.ndim; i++) { index = index * m_View.shape[i] + indices[i]; } index *= m_View.itemsize; return pointer + index; } // Otherwise we can be a full array for (int i = 0; i < m_View.ndim; i++) { pointer += m_View.strides[i] * indices[i]; if (m_View.suboffsets != nullptr && m_View.suboffsets[i] >= 0 ) { pointer = *((char**) pointer) + m_View.suboffsets[i]; } } return pointer; } JPPyErrFrame::JPPyErrFrame() { good = JPPyErr::fetch(m_ExceptionClass, m_ExceptionValue, m_ExceptionTrace); } JPPyErrFrame::~JPPyErrFrame() { try { if (good) JPPyErr::restore(m_ExceptionClass, m_ExceptionValue, m_ExceptionTrace); } catch (...) // GCOVR_EXCL_LINE { // No throw is allowed in dtor. } } void JPPyErrFrame::clear() { good = false; } void JPPyErrFrame::normalize() { // Python uses lazy evaluation on exceptions thus we can't modify it until // we have forced it to realize the exception. if (!PyExceptionInstance_Check(m_ExceptionValue.get())) { JPPyObject args = JPPyTuple_Pack(m_ExceptionValue.get()); m_ExceptionValue = JPPyObject::call(PyObject_Call(m_ExceptionClass.get(), args.get(), nullptr)); PyException_SetTraceback(m_ExceptionValue.get(), m_ExceptionTrace.get()); JPPyErr::restore(m_ExceptionClass, m_ExceptionValue, m_ExceptionTrace); JPPyErr::fetch(m_ExceptionClass, m_ExceptionValue, m_ExceptionTrace); } } jpype-1.6.0/native/python/pyjp_array.cpp000066400000000000000000000336171501674766700203750ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_arrayclass.h" #ifdef __cplusplus extern "C" { #endif /** * Create a new object. * * This is only called from the Python side. * * @param type * @param args * @param kwargs * @return */ static PyObject *PyJPArray_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPArray_new"); auto* self = (PyJPArray*) type->tp_alloc(type, 0); JP_PY_CHECK(); self->m_Array = nullptr; self->m_View = nullptr; return (PyObject*) self; JP_PY_CATCH(nullptr); } static int PyJPArray_init(PyObject *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPArray_init"); // Cases here. // - We got here with a JPValue // - We get an integer. Just create a new array with desired size. // - We get a sequence. Allocate with desired size and call setItems. // - We get something else.... ??? PyObject* v; if (!PyArg_ParseTuple(args, "O", &v)) return -1; JPClass *cls = PyJPClass_getJPClass((PyObject*) Py_TYPE(self)); auto* arrayClass = dynamic_cast (cls); if (arrayClass == nullptr) JP_RAISE(PyExc_TypeError, "Class must be array type"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue *value = PyJPValue_getJavaSlot(v); if (value != nullptr) { auto* arrayClass2 = dynamic_cast (value->getClass()); if (arrayClass2 == nullptr) JP_RAISE(PyExc_TypeError, "Class must be array type"); if (arrayClass2 != arrayClass) JP_RAISE(PyExc_TypeError, "Array class mismatch"); ((PyJPArray*) self)->m_Array = new JPArray(*value); PyJPValue_assignJavaSlot(frame, self, *value); return 0; } if (PySequence_Check(v)) { JP_TRACE("Sequence"); jlong length = PySequence_Size(v); if (length < 0 || length > 2147483647) JP_RAISE(PyExc_ValueError, "Array size invalid"); JPValue newArray = arrayClass->newArray(frame, (int) length); ((PyJPArray*) self)->m_Array = new JPArray(newArray); ((PyJPArray*) self)->m_Array->setRange(0, (jsize) length, 1, v); PyJPValue_assignJavaSlot(frame, self, newArray); return 0; } if (PyIndex_Check(v)) { JP_TRACE("Index"); long long length = PyLong_AsLongLong(v); if (length < 0 || length > 2147483647) JP_RAISE(PyExc_ValueError, "Array size invalid"); JPValue newArray = arrayClass->newArray(frame, (int) length); ((PyJPArray*) self)->m_Array = new JPArray(newArray); PyJPValue_assignJavaSlot(frame, self, newArray); return 0; } JP_FAULT_RETURN("PyJPArray_init.null", 0); JP_RAISE(PyExc_TypeError, "Invalid type"); JP_PY_CATCH(-1); } static void PyJPArray_dealloc(PyJPArray *self) { JP_PY_TRY("PyJPArray_dealloc"); delete self->m_Array; Py_TYPE(self)->tp_free(self); JP_PY_CATCH(); // GCOVR_EXCL_LINE } static PyObject *PyJPArray_repr(PyJPArray *self) { JP_PY_TRY("PyJPArray_repr"); return PyUnicode_FromFormat("", Py_TYPE(self)->tp_name); JP_PY_CATCH(nullptr); } static Py_ssize_t PyJPArray_len(PyJPArray *self) { JP_PY_TRY("PyJPArray_len"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Array == nullptr) JP_RAISE(PyExc_ValueError, "Null array"); // GCOVR_EXCL_LINE return self->m_Array->getLength(); JP_PY_CATCH(-1); } static PyObject* PyJPArray_length(PyJPArray *self, PyObject *closure) { return PyLong_FromSsize_t(PyJPArray_len(self)); } static PyObject *PyJPArray_getItem(PyJPArray *self, PyObject *item) { JP_PY_TRY("PyJPArray_getArrayItem"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Array == nullptr) JP_RAISE(PyExc_ValueError, "Null array"); if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return nullptr; // GCOVR_EXCL_LINE return self->m_Array->getItem((jsize) i).keep(); } if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength; auto length = (Py_ssize_t) self->m_Array->getLength(); if (PySlice_Unpack(item, &start, &stop, &step) < 0) return nullptr; slicelength = PySlice_AdjustIndices(length, &start, &stop, step); if (slicelength <= 0) { // This should point to a null array so we don't hold worthless // memory, but this is a low priority start = stop = 0; step = 1; } JPPyObject tuple = JPPyObject::call(PyTuple_New(0)); JPPyObject newArray = JPPyObject::claim(Py_TYPE(self)->tp_new(Py_TYPE(self), tuple.get(), nullptr)); // Copy over the JPValue PyJPValue_assignJavaSlot(frame, newArray.get(), *PyJPValue_getJavaSlot((PyObject*) self)); // Set up JPArray as slice JPArray *array = ((PyJPArray*) self)->m_Array; ((PyJPArray*) newArray.get())->m_Array = new JPArray(array, (jsize) start, (jsize) stop, (jsize) step); return newArray.keep(); } JP_RAISE(PyExc_TypeError, "Unsupported getItem type"); JP_PY_CATCH(nullptr); } static int PyJPArray_assignSubscript(PyJPArray *self, PyObject *item, PyObject *value) { JP_PY_TRY("PyJPArray_assignSubscript"); JPJavaFrame frame = JPJavaFrame::outer(); // Verified with numpy that item deletion on immutable should // be ValueError if ( value == nullptr) JP_RAISE(PyExc_ValueError, "item deletion not supported"); if (self->m_Array == nullptr) JP_RAISE(PyExc_ValueError, "Null array"); // Watch out for self assignment if (PyObject_IsInstance(value, (PyObject*) PyJPArray_Type)) { JPValue *v1 = PyJPValue_getJavaSlot((PyObject*) self); JPValue *v2 = PyJPValue_getJavaSlot((PyObject*) value); if (frame.equals(v1->getJavaObject(), v2->getJavaObject())) JP_RAISE(PyExc_ValueError, "self assignment not support currently"); } if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return -1; // GCOVR_EXCL_LINE self->m_Array->setItem((jsize) i, value); return 0; } if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength; auto length = (Py_ssize_t) self->m_Array->getLength(); if (PySlice_Unpack(item, &start, &stop, &step) < 0) return -1; slicelength = PySlice_AdjustIndices(length, &start, &stop, step); if (slicelength <= 0) return 0; self->m_Array->setRange((jsize) start, (jsize) slicelength, (jsize) step, value); return 0; } PyErr_Format(PyExc_TypeError, "Java array indices must be integers or slices, not '%s'", Py_TYPE(item)->tp_name); JP_PY_CATCH(-1); } static void PyJPArray_releaseBuffer(PyJPArray *self, Py_buffer *view) { JP_PY_TRY("PyJPArrayPrimitive_releaseBuffer"); JPContext* context = JPContext_global; if (context->isRunning()) { JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_View == nullptr || !self->m_View->unreference()) return; } delete self->m_View; self->m_View = nullptr; JP_PY_CATCH(); // GCOVR_EXCL_LINE } int PyJPArray_getBuffer(PyJPArray *self, Py_buffer *view, int flags) { JP_PY_TRY("PyJPArray_getBuffer"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Array == nullptr) JP_RAISE(PyExc_ValueError, "Null array"); if (!self->m_Array->getClass()->isPrimitiveArray()) { PyErr_SetString(PyExc_BufferError, "Java array is not primitive array"); return -1; } if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { PyErr_SetString(PyExc_BufferError, "Java array buffer is not writable"); return -1; } //Check to see if we are a slice and clone it if necessary jarray obj = self->m_Array->getJava(); if (self->m_Array->isSlice()) obj = self->m_Array->clone(frame, (PyObject*) self); jobject result; try { // Collect the members into a rectangular array if possible. result = frame.collectRectangular(obj); } catch (JPypeException &ex) { // No matter what happens we are only allowed to throw BufferError (void) ex; PyErr_SetString(PyExc_BufferError, "Problem in Java buffer extraction"); return -1; } if (result == nullptr) { PyErr_SetString(PyExc_BufferError, "Java array buffer is not rectangular primitives"); return -1; } // If it is rectangular so try to create a view try { if (self->m_View == nullptr) self->m_View = new JPArrayView(self->m_Array, result); JP_PY_CHECK(); self->m_View->reference(); *view = self->m_View->m_Buffer; // If strides are not requested and this is a slice then fail if ((flags & PyBUF_STRIDES) != PyBUF_STRIDES) view->strides = nullptr; // If shape is not requested if ((flags & PyBUF_ND) != PyBUF_ND) view->shape = nullptr; // If format is not requested if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) view->format = nullptr; // Okay all successful so reference the parent object view->obj = (PyObject*) self; Py_INCREF(view->obj); return 0; } catch (JPypeException &ex) // GCOVR_EXCL_LINE { (void) ex; // GCOVR_EXCL_START // Release the partial buffer so we don't leak PyJPArray_releaseBuffer(self, view); // We are only allowed to raise BufferError PyErr_SetString(PyExc_BufferError, "Java array view failed"); return -1; // GCOVR_EXCL_STOP } JP_PY_CATCH(-1); // GCOVR_EXCL_LINE } int PyJPArrayPrimitive_getBuffer(PyJPArray *self, Py_buffer *view, int flags) { JP_PY_TRY("PyJPArrayPrimitive_getBuffer"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Array == nullptr) JP_RAISE(PyExc_ValueError, "Null array"); try { if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { PyErr_SetString(PyExc_BufferError, "Java array buffer is not writable"); return -1; } if (self->m_View == nullptr) { self->m_View = new JPArrayView(self->m_Array); } self->m_View->reference(); *view = self->m_View->m_Buffer; // We are always contiguous so no need to check that here. view->readonly = 1; // If strides are not requested and this is a slice then fail if ((flags & PyBUF_STRIDES) != PyBUF_STRIDES) { if (view->strides[0] != view->itemsize) JP_RAISE(PyExc_BufferError, "slices required strides"); view->strides = nullptr; } // If shape is not requested if ((flags & PyBUF_ND) != PyBUF_ND) { view->shape = nullptr; } // If format is not requested if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) view->format = nullptr; // Okay all successful so reference the parent object view->obj = (PyObject*) self; Py_INCREF(view->obj); return 0; } catch (JPypeException &ex) { (void) ex; PyJPArray_releaseBuffer(self, view); // We are only allowed to raise BufferError PyErr_SetString(PyExc_BufferError, "Java array view failed"); return -1; } JP_PY_CATCH(-1); } static const char *length_doc = "Get the length of a Java array\n" "\n" "This method is provided for compatibility with Java syntax.\n" "Generally, the Python style ``len(array)`` should be preferred.\n"; static PyMethodDef arrayMethods[] = { {"__getitem__", (PyCFunction) (&PyJPArray_getItem), METH_O | METH_COEXIST, ""}, {nullptr}, }; static PyGetSetDef arrayGetSets[] = { {"length", (getter) (&PyJPArray_length), nullptr, (length_doc)}, {nullptr} }; static PyType_Slot arraySlots[] = { { Py_tp_new, (void*) PyJPArray_new}, { Py_tp_init, (void*) PyJPArray_init}, { Py_tp_dealloc, (void*) PyJPArray_dealloc}, { Py_tp_repr, (void*) PyJPArray_repr}, { Py_tp_methods, (void*) &arrayMethods}, { Py_mp_subscript, (void*) &PyJPArray_getItem}, { Py_sq_length, (void*) &PyJPArray_len}, { Py_tp_getset, (void*) &arrayGetSets}, { Py_mp_ass_subscript, (void*) &PyJPArray_assignSubscript}, #if PY_VERSION_HEX >= 0x03090000 { Py_bf_getbuffer, (void*) &PyJPArray_getBuffer}, { Py_bf_releasebuffer, (void*) &PyJPArray_releaseBuffer}, #endif {0} }; #if PY_VERSION_HEX < 0x03090000 static PyBufferProcs arrayBuffer = { (getbufferproc) & PyJPArray_getBuffer, (releasebufferproc) & PyJPArray_releaseBuffer }; #endif PyTypeObject *PyJPArray_Type = nullptr; static PyType_Spec arraySpec = { "_jpype._JArray", sizeof (PyJPArray), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, arraySlots }; #if PY_VERSION_HEX < 0x03090000 static PyBufferProcs arrayPrimBuffer = { (getbufferproc) & PyJPArrayPrimitive_getBuffer, (releasebufferproc) & PyJPArray_releaseBuffer }; #endif static PyType_Slot arrayPrimSlots[] = { #if PY_VERSION_HEX >= 0x03090000 { Py_bf_getbuffer, (void*) &PyJPArrayPrimitive_getBuffer}, { Py_bf_releasebuffer, (void*) &PyJPArray_releaseBuffer}, #endif {0} }; PyTypeObject *PyJPArrayPrimitive_Type = nullptr; static PyType_Spec arrayPrimSpec = { "_jpype._JArrayPrimitive", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, arrayPrimSlots }; #ifdef __cplusplus } #endif void PyJPArray_initType(PyObject * module) { JPPyObject tuple = JPPyTuple_Pack(PyJPObject_Type); PyJPArray_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&arraySpec, tuple.get()); JP_PY_CHECK(); #if PY_VERSION_HEX < 0x03090000 PyJPArray_Type->tp_as_buffer = &arrayBuffer; #endif PyModule_AddObject(module, "_JArray", (PyObject*) PyJPArray_Type); JP_PY_CHECK(); tuple = JPPyTuple_Pack(PyJPArray_Type); PyJPArrayPrimitive_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&arrayPrimSpec, tuple.get()); #if PY_VERSION_HEX < 0x03090000 PyJPArrayPrimitive_Type->tp_as_buffer = &arrayPrimBuffer; #endif JP_PY_CHECK(); PyModule_AddObject(module, "_JArrayPrimitive", (PyObject*) PyJPArrayPrimitive_Type); JP_PY_CHECK(); } JPPyObject PyJPArray_create(JPJavaFrame &frame, PyTypeObject *type, const JPValue & value) { PyObject *obj = type->tp_alloc(type, 0); JP_PY_CHECK(); ((PyJPArray*) obj)->m_Array = new JPArray(value); PyJPValue_assignJavaSlot(frame, obj, value); return JPPyObject::claim(obj); } jpype-1.6.0/native/python/pyjp_buffer.cpp000066400000000000000000000103441501674766700205200ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_buffer.h" #include "jp_buffertype.h" #ifdef __cplusplus extern "C" { #endif struct PyJPBuffer { PyObject_HEAD JPBuffer *m_Buffer; } ; static void PyJPBuffer_dealloc(PyJPBuffer *self) { JP_PY_TRY("PyJPBuffer_dealloc"); delete self->m_Buffer; Py_TYPE(self)->tp_free(self); JP_PY_CATCH(); // GCOV_EXCL_LINE } static PyObject *PyJPBuffer_repr(PyJPBuffer *self) { JP_PY_TRY("PyJPBuffer_repr"); return PyUnicode_FromFormat("", Py_TYPE(self)->tp_name); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static void PyJPBuffer_releaseBuffer(PyJPBuffer *self, Py_buffer *view) { JP_PY_TRY("PyJPBufferPrimitive_releaseBuffer"); JP_PY_CATCH(); // GCOVR_EXCL_LINE } int PyJPBuffer_getBuffer(PyJPBuffer *self, Py_buffer *view, int flags) { JP_PY_TRY("PyJPBufferPrimitive_getBuffer"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Buffer == nullptr) JP_RAISE(PyExc_ValueError, "Null buffer"); // GCOVR_EXCL_LINE try { JPBuffer *buffer = self->m_Buffer; if (!buffer->isValid()) { PyErr_SetString(PyExc_BufferError, "Java buffer is not direct"); return -1; } if (buffer->isReadOnly() && (flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { PyErr_SetString(PyExc_BufferError, "Java buffer is not writable"); return -1; } *view = buffer->getView(); // If strides are not requested and this is a slice then fail if ((flags & PyBUF_STRIDES) != PyBUF_STRIDES) { if (view->strides[0] != view->itemsize) JP_RAISE(PyExc_BufferError, "slices required strides"); view->strides = nullptr; } // If shape is not requested if ((flags & PyBUF_ND) != PyBUF_ND) { view->shape = nullptr; } // If format is not requested if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) view->format = nullptr; // Okay all successful so reference the parent object view->obj = (PyObject*) self; Py_INCREF(view->obj); return 0; } catch (JPypeException &ex) // GCOVR_EXCL_LINE { (void) ex; // GCOVR_EXCL_START PyJPBuffer_releaseBuffer(self, view); // We are only allowed to raise BufferError PyErr_SetString(PyExc_BufferError, "Java buffer view failed"); return -1; // GCOVR_EXCL_STOP } JP_PY_CATCH(-1); // GCOVR_EXCL_LINE } static PyType_Slot bufferSlots[] = { { Py_tp_dealloc, (void*) PyJPBuffer_dealloc}, { Py_tp_repr, (void*) PyJPBuffer_repr}, #if PY_VERSION_HEX >= 0x03090000 { Py_bf_getbuffer, (void*) PyJPBuffer_getBuffer}, { Py_bf_releasebuffer, (void*) PyJPBuffer_releaseBuffer}, #endif {0} }; #if PY_VERSION_HEX < 0x03090000 static PyBufferProcs directBuffer = { (getbufferproc) & PyJPBuffer_getBuffer, (releasebufferproc) & PyJPBuffer_releaseBuffer }; #endif PyTypeObject *PyJPBuffer_Type = nullptr; static PyType_Spec bufferSpec = { "_jpype._JBuffer", sizeof (PyJPBuffer), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, bufferSlots }; #ifdef __cplusplus } #endif void PyJPBuffer_initType(PyObject * module) { JPPyObject tuple = JPPyTuple_Pack(PyJPObject_Type); PyJPBuffer_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&bufferSpec, tuple.get()); #if PY_VERSION_HEX < 0x03090000 PyJPBuffer_Type->tp_as_buffer = &directBuffer; #endif JP_PY_CHECK(); PyModule_AddObject(module, "_JBuffer", (PyObject*) PyJPBuffer_Type); JP_PY_CHECK(); } JPPyObject PyJPBuffer_create(JPJavaFrame &frame, PyTypeObject *type, const JPValue& value) { JPPyObject obj = JPPyObject::call(type->tp_alloc(type, 0)); ((PyJPBuffer*) obj.get())->m_Buffer = new JPBuffer(value); PyJPValue_assignJavaSlot(frame, obj.get(), value); return obj; } jpype-1.6.0/native/python/pyjp_char.cpp000066400000000000000000000426661501674766700202000ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include #include "jp_boxedtype.h" #ifdef __cplusplus extern "C" { #endif PyTypeObject *PyJPChar_Type = nullptr; struct PyJPChar { PyCompactUnicodeObject m_Obj; char m_Data[4]; } ; #define _PyUnicode_LENGTH(op) (((PyASCIIObject *)(op))->length) #define _PyUnicode_STATE(op) (((PyASCIIObject *)(op))->state) #define _PyUnicode_HASH(op) (((PyASCIIObject *)(op))->hash) #define _PyUnicode_WSTR(op) (((PyASCIIObject*)(op))->wstr) #define _PyUnicode_WSTR_LENGTH(op) (((PyCompactUnicodeObject*)(op))->wstr_length) static Py_UCS4 ord(PyObject *c) { if (PyUnicode_Check(c)) { if (PyUnicode_READY(c) == -1) return -1; if (PyUnicode_GET_LENGTH(c) == 1) return PyUnicode_READ_CHAR(c, 0); } else if (PyBytes_Check(c) && PyBytes_GET_SIZE(c) == 1) return (Py_UCS4) ((unsigned char) *PyBytes_AS_STRING(c)); else if (PyByteArray_Check(c) && PyByteArray_GET_SIZE(c)) return (Py_UCS4) ((unsigned char) *PyByteArray_AS_STRING(c)); return (Py_UCS4) - 1; } static int isNull(JPValue *javaSlot) { if (javaSlot != nullptr ) { JPClass *cls = javaSlot->getClass(); if (cls->isPrimitive() || javaSlot->getValue().l != nullptr) return 0; } return 1; } static PyObject* notSupported() { PyErr_SetString(PyExc_TypeError, "unsupported operation"); return nullptr; } static int assertNotNull(JPValue *javaSlot) { if (!isNull(javaSlot)) return 0; PyErr_SetString(PyExc_TypeError, "jchar cast of null pointer"); return 1; } PyObject *PyJPChar_Create(PyTypeObject *type, Py_UCS2 p) { // Allocate a new string type (derived from UNICODE) PyJPChar *self = (PyJPChar*) PyJPValue_alloc(type, 0); if (self == nullptr) return nullptr; // Set up a wide char with value of zero self->m_Data[0] = 0; self->m_Data[1] = 0; self->m_Data[2] = 0; self->m_Data[3] = 0; // Values taken from internal/cpython/unicode.h // Mark the type in unicode _PyUnicode_LENGTH(self) = 1; _PyUnicode_HASH(self) = -1; _PyUnicode_STATE(self).compact = 1; _PyUnicode_STATE(self).interned = 0; #if PY_VERSION_HEX < 0x030c0000 _PyUnicode_STATE(self).ready = 1; #endif // Copy the value based on the length if (p < 128) { _PyUnicode_STATE(self).ascii = 1; _PyUnicode_STATE(self).kind = PyUnicode_1BYTE_KIND; char *data = (char*) (((PyASCIIObject*) self) + 1); data[0] = (char) p; data[1] = 0; } else if (p < 256) { _PyUnicode_STATE(self).ascii = 0; _PyUnicode_STATE(self).kind = PyUnicode_1BYTE_KIND; char *data = (char*) ( ((PyCompactUnicodeObject*) self) + 1); data[0] = (char) p; data[1] = 0; #if PY_VERSION_HEX < 0x030c0000 _PyUnicode_WSTR_LENGTH(self) = 0; _PyUnicode_WSTR(self) = nullptr; #endif self->m_Obj.utf8 = NULL; self->m_Obj.utf8_length = 0; } else { _PyUnicode_STATE(self).ascii = 0; _PyUnicode_STATE(self).kind = PyUnicode_2BYTE_KIND; auto *data = (Py_UCS2*) ( ((PyCompactUnicodeObject*) self) + 1); data[0] = p; data[1] = 0; #if PY_VERSION_HEX < 0x030c0000 if (sizeof (wchar_t) == 2) { _PyUnicode_WSTR_LENGTH(self) = 1; _PyUnicode_WSTR(self) = (wchar_t *) data; } else { _PyUnicode_WSTR_LENGTH(self) = 0; _PyUnicode_WSTR(self) = nullptr; } #endif self->m_Obj.utf8 = nullptr; self->m_Obj.utf8_length = 0; } return (PyObject*) self; } /** This one is just used for initializing so the local copy matches. */ Py_UCS2 fromJPValue(const JPValue & value) { JPJavaFrame frame = JPJavaFrame::outer(); JPClass* cls = value.getClass(); if (cls->isPrimitive()) return (Py_UCS2) (value.getValue().c); JPPrimitiveType* pcls = (dynamic_cast( cls))->getPrimitive(); if (value.getValue().l == nullptr) return (Py_UCS2) - 1; else return (Py_UCS2) (pcls->getValueFromObject(frame, value).getValue().c); } /** Get the value of the char. Does not touch Java. */ Py_UCS2 fromJPChar(PyJPChar *self) { if (_PyUnicode_STATE(self).ascii == 1) { auto *data = (Py_UCS1*) (((PyASCIIObject*) self) + 1); return data[0]; } if (_PyUnicode_STATE(self).kind == PyUnicode_1BYTE_KIND) { auto *data = (Py_UCS1*) ( ((PyCompactUnicodeObject*) self) + 1); return data[0]; } auto *data = (Py_UCS2*) ( ((PyCompactUnicodeObject*) self) + 1); return data[0]; } static PyObject * PyJPChar_new(PyTypeObject *type, PyObject *pyargs, PyObject * kwargs) { JP_PY_TRY("PyJPChar_new"); // Get the Java class from the type. JPClass *cls = PyJPClass_getJPClass((PyObject*) type); if (cls == nullptr) { // GCOVR_EXCL_START PyErr_SetString(PyExc_TypeError, "Java class type is incorrect"); return nullptr; } // GCOVR_EXCL_STOP // Create an instance (this may fail) if (PyTuple_Size(pyargs) != 1) { PyErr_SetString(PyExc_TypeError, "Java chars require one argument"); return nullptr; } JPValue jv; PyObject *in = PyTuple_GetItem(pyargs, 0); Py_UCS4 cv = ord(in); JPJavaFrame frame = JPJavaFrame::outer(); if (cv != (Py_UCS4) - 1) { JPPyObject v = JPPyObject::call(PyLong_FromLong(cv)); JPPyObject args0 = JPPyTuple_Pack(v.get()); JPPyObjectVector args(args0.get()); jv = cls->newInstance(frame, args); } else if (PyIndex_Check(in)) { JPPyObjectVector args(pyargs); jv = cls->newInstance(frame, args); } else if (PyFloat_Check(in)) { JPPyObject v = JPPyObject::call(PyNumber_Long(in)); JPPyObject args0 = JPPyTuple_Pack(v.get()); JPPyObjectVector args(args0.get()); jv = cls->newInstance(frame, args); } else { // This is not strictly true as we can cast a float to a char PyErr_SetString(PyExc_TypeError, "Java require index or str with length 1"); return nullptr; } PyObject *self = PyJPChar_Create(type, fromJPValue(jv)); JP_PY_CHECK(); PyJPValue_assignJavaSlot(frame, self, jv); return self; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_str(PyJPChar *self) { JP_PY_TRY("PyJPChar_str"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (javaSlot == nullptr) { // GCOVR_EXCL_START // A slot is required PyErr_SetString(PyExc_TypeError, "Java slot is not set on Java char"); return nullptr; } // GCOVR_EXCL_STOP if (isNull(javaSlot)) return JPPyString::fromStringUTF8("None").keep(); return PyUnicode_FromOrdinal(fromJPChar(self)); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_repr(PyJPChar *self) { JP_PY_TRY("PyJPChar_repr"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (javaSlot == nullptr) { // GCOVR_EXCL_START // A slot is required PyErr_SetString(PyExc_TypeError, "Java slot is not set on Java char"); return nullptr; } // GCOVR_EXCL_STOP if (isNull(javaSlot)) return JPPyString::fromStringUTF8("None").keep(); return PyUnicode_Type.tp_repr((PyObject*) self); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_index(PyJPChar *self) { JP_PY_TRY("PyJPChar_index"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return nullptr; return PyLong_FromLong(fromJPChar(self)); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_float(PyJPChar *self) { JP_PY_TRY("PyJPChar_float"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return nullptr; return PyFloat_FromDouble(fromJPChar(self)); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_abs(PyJPChar *self) { JP_PY_TRY("PyJPChar_nop"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return nullptr; // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); return PyLong_Type.tp_as_number->nb_absolute(v.get()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static Py_ssize_t PyJPChar_len(PyJPChar *self) { JP_PY_TRY("PyJPChar_nop"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return -1; return 1; JP_PY_CATCH(-1); // GCOVR_EXCL_LINE } static PyObject *apply(PyObject *first, PyObject *second, PyObject* (*func)(PyObject*, PyObject*)) { JPValue *slot0 = PyJPValue_getJavaSlot(first); JPValue *slot1 = PyJPValue_getJavaSlot(second); if (slot0 != nullptr && slot1 != nullptr) { if (assertNotNull(slot0)) return nullptr; if (assertNotNull(slot1)) return nullptr; JPPyObject v1 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); JPPyObject v2 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); return func(v1.get(), v2.get()); } else if (slot0 != nullptr) { if (assertNotNull(slot0)) return nullptr; // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); return func(v.get(), second); } else if (slot1 != nullptr) { if (assertNotNull(slot1)) return nullptr; // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); return func(first, v.get()); } return notSupported(); } static PyObject *PyJPChar_and(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_and"); return apply(first, second, PyNumber_And); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_or(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_or"); return apply(first, second, PyNumber_Or); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_xor(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_xor"); return apply(first, second, PyNumber_Xor); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_add(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_add"); JPValue *slot0 = PyJPValue_getJavaSlot(first); JPValue *slot1 = PyJPValue_getJavaSlot(second); if (slot1 != nullptr && slot0 != nullptr) { if (assertNotNull(slot0)) return nullptr; if (assertNotNull(slot1)) return nullptr; JPPyObject v1 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); JPPyObject v2 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); return PyNumber_Add(v1.get(), v2.get()); } else if (slot0 != nullptr) { if (assertNotNull(slot0)) return nullptr; if (PyUnicode_Check(second)) return PyUnicode_Concat(first, second); // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); return PyNumber_Add(v.get(), second); } else if (slot1 != nullptr) { if (assertNotNull(slot1)) return nullptr; if (PyUnicode_Check(first)) return PyUnicode_Concat(first, second); // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); return PyNumber_Add(first, v.get()); } return notSupported(); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_subtract(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_subtract"); return apply(first, second, PyNumber_Subtract); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_mult(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_mult"); return apply(first, second, PyNumber_Multiply); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_rshift(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_rshift"); return apply(first, second, PyNumber_Rshift); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_lshift(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_lshift"); return apply(first, second, PyNumber_Lshift); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_floordiv(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_floordiv"); return apply(first, second, PyNumber_FloorDivide); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_divmod(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_divmod"); return apply(first, second, PyNumber_Divmod); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_neg(PyJPChar *self) { JP_PY_TRY("PyJPChar_neg"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return nullptr; // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); return PyNumber_Negative(v.get()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_pos(PyJPChar *self) { JP_PY_TRY("PyJPChar_pos"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return nullptr; // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); return PyNumber_Positive(v.get()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPChar_inv(PyJPChar *self) { JP_PY_TRY("PyJPObject_neg"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (assertNotNull(javaSlot)) return nullptr; // Promote to int as per Java rules JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); return PyNumber_Invert(v.get()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPJChar_compare(PyObject *self, PyObject *other, int op) { JP_PY_TRY("PyJPJChar_compare"); JPValue *javaSlot1 = PyJPValue_getJavaSlot(other); JPValue *javaSlot0 = PyJPValue_getJavaSlot(self); if (isNull(javaSlot0)) { if (javaSlot1 != nullptr && isNull(javaSlot1)) other = Py_None; if (op == Py_EQ) return PyBool_FromLong(other == Py_None ); if (op == Py_NE) return PyBool_FromLong(other != Py_None); PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; JP_RAISE_PYTHON(); } if (javaSlot1 != nullptr && isNull(javaSlot1)) return PyBool_FromLong(op == Py_NE); if (PyUnicode_Check(other)) { // char <=> char // char <=> str return PyUnicode_Type.tp_richcompare(self, other, op); } if (PyFloat_Check(other)) { JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*) self))); if (op < 2) op += 4; else if (op > 3) op -= 4; return PyFloat_Type.tp_richcompare(other, v.get(), op); } if (PyNumber_Check(other)) { JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*) self))); return PyLong_Type.tp_richcompare(v.get(), other, op); } if (javaSlot1 != nullptr) { // char <=> object // object <=> char // object <=> object switch (op) { case Py_EQ: Py_RETURN_FALSE; case Py_NE: Py_RETURN_TRUE; } PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; } PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static Py_hash_t PyJPChar_hash(PyObject *self) { JP_PY_TRY("PyJPObject_hash"); JPValue *javaSlot = PyJPValue_getJavaSlot(self); if (isNull(javaSlot)) return Py_TYPE(Py_None)->tp_hash(Py_None); return PyUnicode_Type.tp_hash((PyObject*) self); JP_PY_CATCH(0); // GCOVR_EXCL_LINE } static int PyJPChar_bool(PyJPChar *self) { JP_PY_TRY("PyJPObject_bool"); JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); if (isNull(javaSlot)) return 0; return fromJPChar(self) != 0; JP_PY_CATCH(0); // GCOVR_EXCL_LINE } static PyMethodDef charMethods[] = { {nullptr}, }; struct PyGetSetDef charGetSet[] = { {nullptr}, }; static PyType_Slot charSlots[] = { {Py_tp_new, (void*) PyJPChar_new}, {Py_tp_methods, (void*) charMethods}, {Py_tp_getset, (void*) charGetSet}, {Py_tp_str, (void*) PyJPChar_str}, {Py_tp_repr, (void*) PyJPChar_repr}, {Py_nb_index, (void*) PyJPChar_index}, #if PY_VERSION_HEX<0x03080000 {Py_nb_int, (void*) PyJPChar_index}, #endif {Py_nb_float, (void*) PyJPChar_float}, {Py_nb_absolute, (void*) PyJPChar_abs}, {Py_nb_and, (void*) PyJPChar_and}, {Py_nb_or, (void*) PyJPChar_or}, {Py_nb_xor, (void*) PyJPChar_xor}, {Py_nb_add, (void*) PyJPChar_add}, {Py_nb_subtract, (void*) PyJPChar_subtract}, {Py_nb_multiply, (void*) PyJPChar_mult}, {Py_nb_rshift, (void*) PyJPChar_rshift}, {Py_nb_lshift, (void*) PyJPChar_lshift}, {Py_tp_richcompare, (void*) PyJPJChar_compare}, {Py_tp_hash, (void*) PyJPChar_hash}, {Py_nb_bool, (void*) PyJPChar_bool}, {Py_nb_negative, (void*) PyJPChar_neg}, {Py_nb_positive, (void*) PyJPChar_pos}, {Py_nb_invert, (void*) PyJPChar_inv}, {Py_nb_floor_divide, (void*) PyJPChar_floordiv}, {Py_nb_divmod, (void*) PyJPChar_divmod}, {Py_tp_getattro, (void*) PyJPValue_getattro}, {Py_tp_setattro, (void*) PyJPValue_setattro}, { Py_sq_length, (void*) PyJPChar_len}, {0} }; static PyType_Spec charSpec = { "_jpype._JChar", sizeof (PyJPChar), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // | Py_TPFLAGS_HAVE_GC, charSlots }; #ifdef __cplusplus } #endif void PyJPChar_initType(PyObject* module) { // We will inherit from str and JObject JPPyObject bases = JPPyTuple_Pack(&PyUnicode_Type, PyJPObject_Type); PyJPChar_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&charSpec, bases.get()); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JChar", (PyObject*) PyJPChar_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE } jpype-1.6.0/native/python/pyjp_class.cpp000066400000000000000000001054131501674766700203560ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include #include #include "jpype.h" #include "pyjp.h" #include "jp_array.h" #include "jp_arrayclass.h" #include "jp_boxedtype.h" #include "jp_field.h" #include "jp_method.h" #include "jp_methoddispatch.h" #include "jp_primitive_accessor.h" struct PyJPClass { PyHeapTypeObject ht_type; JPClass *m_Class; PyObject *m_Doc; } ; PyObject* PyJPClassMagic = nullptr; #ifdef __cplusplus extern "C" { #endif int PyJPClass_Check(PyObject* obj) { return PyJP_IsInstanceSingle(obj, PyJPClass_Type); } static int PyJPClass_traverse(PyJPClass *self, visitproc visit, void *arg) { Py_VISIT(self->m_Doc); return 0; } static int PyJPClass_clear(PyJPClass *self) { Py_CLEAR(self->m_Doc); return 0; } PyObject* examine(PyObject *module, PyObject *other); PyObject* PyJPClass_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) { JP_PY_TRY("PyJPClass_FromSpecWithBases"); #if PY_VERSION_HEX>=0x030c0000 // Starting in Python 3.12 there is a function for creating from a meta class // that replaces this madeness. PyTypeObject *type = (PyTypeObject*) PyType_FromMetaclass((PyTypeObject*) PyJPClass_Type, NULL, spec, bases); if (type == nullptr) return (PyObject*) type; #else // Python lacks a FromSpecWithMeta so we are going to have to fake it here. auto* type = (PyTypeObject*) PyJPClass_Type->tp_alloc(PyJPClass_Type, 0); auto* heap = (PyHeapTypeObject*) type; type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE; type->tp_name = spec->name; const char *s = strrchr(spec->name, '.'); if (s == nullptr) s = spec->name; else s++; heap->ht_qualname = PyUnicode_FromString(s); heap->ht_name = heap->ht_qualname; Py_INCREF(heap->ht_name); if (bases == nullptr) { // do NOT use JPPyTuple_Pack here type->tp_bases = PyTuple_Pack(1, (PyObject*) & PyBaseObject_Type); } else { type->tp_bases = bases; Py_INCREF(bases); } type->tp_base = (PyTypeObject*) PyTuple_GetItem(type->tp_bases, 0); Py_INCREF(type->tp_base); type->tp_as_async = &heap->as_async; type->tp_as_buffer = &heap->as_buffer; type->tp_as_mapping = &heap->as_mapping; type->tp_as_number = &heap->as_number; type->tp_as_sequence = &heap->as_sequence; type->tp_basicsize = spec->basicsize; if (spec->basicsize == 0) type->tp_basicsize = type->tp_base->tp_basicsize; type->tp_itemsize = spec->itemsize; if (spec->itemsize == 0) type->tp_itemsize = type->tp_base->tp_itemsize; type->tp_alloc = PyJPValue_alloc; type->tp_free = PyJPValue_free; type->tp_finalize = (destructor) PyJPValue_finalize; for (PyType_Slot* slot = spec->slots; slot->slot; slot++) { switch (slot->slot) { case Py_tp_finalize: type->tp_finalize = (destructor) slot->pfunc; break; case Py_tp_alloc: type->tp_alloc = (allocfunc) slot->pfunc; break; case Py_tp_free: type->tp_free = (freefunc) slot->pfunc; break; case Py_tp_new: type->tp_new = (newfunc) slot->pfunc; break; case Py_tp_init: type->tp_init = (initproc) slot->pfunc; break; case Py_tp_getattro: type->tp_getattro = (getattrofunc) slot->pfunc; break; case Py_tp_setattro: type->tp_setattro = (setattrofunc) slot->pfunc; break; case Py_tp_dealloc: type->tp_dealloc = (destructor) slot->pfunc; break; case Py_tp_str: type->tp_str = (reprfunc) slot->pfunc; break; case Py_tp_repr: type->tp_repr = (reprfunc) slot->pfunc; break; case Py_tp_methods: type->tp_methods = (PyMethodDef*) slot->pfunc; break; case Py_sq_item: heap->as_sequence.sq_item = (ssizeargfunc) slot->pfunc; break; case Py_sq_length: heap->as_sequence.sq_length = (lenfunc) slot->pfunc; break; case Py_mp_ass_subscript: heap->as_mapping.mp_ass_subscript = (objobjargproc) slot->pfunc; break; case Py_tp_hash: type->tp_hash = (hashfunc) slot->pfunc; break; case Py_nb_int: heap->as_number.nb_int = (unaryfunc) slot->pfunc; break; case Py_nb_float: heap->as_number.nb_float = (unaryfunc) slot->pfunc; break; case Py_tp_richcompare: type->tp_richcompare = (richcmpfunc) slot->pfunc; break; case Py_mp_subscript: heap->as_mapping.mp_subscript = (binaryfunc) slot->pfunc; break; case Py_nb_index: heap->as_number.nb_index = (unaryfunc) slot->pfunc; break; case Py_nb_absolute: heap->as_number.nb_absolute = (unaryfunc) slot->pfunc; break; case Py_nb_and: heap->as_number.nb_and = (binaryfunc) slot->pfunc; break; case Py_nb_or: heap->as_number.nb_or = (binaryfunc) slot->pfunc; break; case Py_nb_xor: heap->as_number.nb_xor = (binaryfunc) slot->pfunc; break; case Py_nb_add: heap->as_number.nb_add = (binaryfunc) slot->pfunc; break; case Py_nb_subtract: heap->as_number.nb_subtract = (binaryfunc) slot->pfunc; break; case Py_nb_multiply: heap->as_number.nb_multiply = (binaryfunc) slot->pfunc; break; case Py_nb_rshift: heap->as_number.nb_rshift = (binaryfunc) slot->pfunc; break; case Py_nb_lshift: heap->as_number.nb_lshift = (binaryfunc) slot->pfunc; break; case Py_nb_negative: heap->as_number.nb_negative = (unaryfunc) slot->pfunc; break; case Py_nb_bool: heap->as_number.nb_bool = (inquiry) slot->pfunc; break; case Py_nb_invert: heap->as_number.nb_invert = (unaryfunc) slot->pfunc; break; case Py_nb_positive: heap->as_number.nb_positive = (unaryfunc) slot->pfunc; break; case Py_nb_floor_divide: heap->as_number.nb_floor_divide = (binaryfunc) slot->pfunc; break; case Py_nb_divmod: heap->as_number.nb_divmod = (binaryfunc) slot->pfunc; break; case Py_tp_getset: type->tp_getset = (PyGetSetDef*) slot->pfunc; break; #if PY_VERSION_HEX >= 0x03090000 case Py_bf_getbuffer: type->tp_as_buffer->bf_getbuffer = (getbufferproc) slot->pfunc; break; case Py_bf_releasebuffer: type->tp_as_buffer->bf_releasebuffer = (releasebufferproc) slot->pfunc; break; #endif // GCOVR_EXCL_START default: PyErr_Format(PyExc_TypeError, "slot %d not implemented", slot->slot); JP_RAISE_PYTHON(); // GCOVR_EXCL_STOP } } // GC objects are required to implement clear and traverse, this is a // safety check to make sure we implemented all properly. This error should // never happen in production code. if (PyType_IS_GC(type) && ( type->tp_traverse==nullptr || type->tp_clear==nullptr)) { PyErr_Format(PyExc_TypeError, "GC requirements failed for %s", spec->name); JP_RAISE_PYTHON(); } #endif // Make sure our memory model is used type->tp_alloc = (allocfunc) PyJPValue_alloc; type->tp_finalize = (destructor) PyJPValue_finalize; PyType_Ready(type); PyDict_SetItemString(type->tp_dict, "__module__", PyUnicode_FromString("_jpype")); return (PyObject*) type; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } int PyJPClass_init(PyObject *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPClass_init"); if (!PyObject_IsInstance(self, (PyObject*) PyJPClass_Type)) { PyErr_SetString(PyExc_TypeError, "Type incorrect"); return -1; } PyTypeObject *type = (PyTypeObject*) self; #if PY_VERSION_HEX >= 0x030d0000 // Python 3.13 - This flag will try to place the dictionary are part of the object which // adds an unknown number of bytes to the end of the object making it impossible // to attach our needed data. If we kill the flag then we get usable behavior. type->tp_flags &= ~Py_TPFLAGS_INLINE_VALUES; #endif // Verify that we were called internally int magic = 0; if (kwargs == PyJPClassMagic || (kwargs != nullptr && PyDict_GetItemString(kwargs, "internal") != nullptr)) { magic = 1; kwargs = nullptr; } if (magic == 0) { PyErr_Format(PyExc_TypeError, "Java classes cannot be extended in Python"); return -1; } // Set the host object PyObject *name = nullptr; PyObject *bases = nullptr; PyObject *members = nullptr; if (!PyArg_ParseTuple(args, "OOO", &name, &bases, &members)) return -1; // Check that all types are Java types if (!PyTuple_Check(bases)) { PyErr_SetString(PyExc_TypeError, "Bases must be a tuple"); return -1; } JP_BLOCK("PyJPClass_new::verify") { // Watch for final classes Py_ssize_t len = PyTuple_Size(bases); for (Py_ssize_t i = 0; i < len; ++i) { PyObject *item = PyTuple_GetItem(bases, i); JPClass *cls = PyJPClass_getJPClass(item); if (cls != nullptr && cls->isFinal()) { PyErr_Format(PyExc_TypeError, "Cannot extend final class '%s'", ((PyTypeObject*) item)->tp_name); } } } // We must make sure that all classes have our allocator type->tp_alloc = (allocfunc) PyJPValue_alloc; type->tp_finalize = (destructor) PyJPValue_finalize; ((PyJPClass*) self)->m_Doc = nullptr; // Call the type init int rc = PyType_Type.tp_init(self, args, nullptr); if (rc == -1) return rc; // GCOVR_EXCL_LINE no clue how to trigger this one // GCOVR_EXCL_START // Sanity checks. Not testable if (type == nullptr) return -1; if (type->tp_finalize != nullptr && type->tp_finalize != (destructor) PyJPValue_finalize) { PyErr_SetString(PyExc_TypeError, "finalizer conflict"); return -1; } // This sanity check is trigger if the user attempts to build their own // type wrapper with a __del__ method defined. It is hard to trigger. if (type->tp_alloc != (allocfunc) PyJPValue_alloc && type->tp_alloc != PyBaseObject_Type.tp_alloc) { PyErr_SetString(PyExc_TypeError, "alloc conflict"); return -1; } // GCOVR_EXCL_STOP #if PY_VERSION_HEX < 0x03090000 // This was required at one point but I don't know what version it applied to. if (PyObject_IsSubclass((PyObject*) type, (PyObject*) PyJPException_Type)) { type->tp_new = PyJPException_Type->tp_new; } #endif return rc; JP_PY_CATCH(-1); } static void PyJPClass_dealloc(PyJPClass *self) { JP_PY_TRY("PyJPClass_dealloc"); PyObject_GC_UnTrack(self); PyJPClass_clear(self); Py_TYPE(self)->tp_free(self); JP_PY_CATCH_NONE(); // GCOVR_EXCL_LINE } PyObject* PyJPClass_mro(PyTypeObject *self) { Py_ssize_t sz = PySequence_Size(self->tp_bases); std::list bases; bases.push_back((PyObject*) self); // Merge together all bases std::list out; for (auto iter = bases.begin(); iter != bases.end(); ++iter) { PyObject *l = ((PyTypeObject*) * iter)->tp_bases; sz = PySequence_Size(l); for (Py_ssize_t i = 0; i < sz; ++i) { PyObject *obj = PyTuple_GetItem(l, i); bool found = (std::find(bases.begin(), bases.end(), obj) != bases.end()); if (!found) { bases.push_back(obj); } } } while (!bases.empty()) { PyObject* front = bases.front(); bases.pop_front(); for (auto iter = bases.begin(); iter != bases.end(); ++iter) { if (PySequence_Contains(((PyTypeObject*) * iter)->tp_bases, front)) { bases.push_back(front); front = nullptr; break; } } if (front != nullptr) { out.push_back(front); auto* next = (PyObject*) ((PyTypeObject*) front)->tp_base; if (next) { bases.remove(next); bases.push_front(next); } } } PyObject *obj = PyTuple_New(out.size()); int j = 0; for (auto iter = out.begin(); iter != out.end(); ++iter) { Py_INCREF(*iter); PyTuple_SetItem(obj, j++, *iter); } return obj; } PyObject *PyJPClass_getattro(PyObject *obj, PyObject *name) { JP_PY_TRY("PyJPClass_getattro"); if (!PyUnicode_Check(name)) { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", Py_TYPE(name)->tp_name); return nullptr; } // Private members are accessed directly PyObject* pyattr = PyType_Type.tp_getattro(obj, name); if (pyattr == nullptr) return nullptr; JPPyObject attr = JPPyObject::claim(pyattr); // Private members go regardless if (PyUnicode_GetLength(name) && PyUnicode_ReadChar(name, 0) == '_') return attr.keep(); // Methods if (Py_TYPE(attr.get()) == PyJPMethod_Type) return attr.keep(); // Don't allow properties to be rewritten if (!PyObject_IsInstance(attr.get(), (PyObject*) & PyProperty_Type)) return attr.keep(); const char *name_str = PyUnicode_AsUTF8(name); PyErr_Format(PyExc_AttributeError, "Field '%s' is static", name_str); return nullptr; JP_PY_CATCH(nullptr); } int PyJPClass_setattro(PyObject *self, PyObject *attr_name, PyObject *v) { JP_PY_TRY("PyJPClass_setattro"); PyJPModule_getContext(); if (!PyUnicode_Check(attr_name)) { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", attr_name->ob_type->tp_name); return -1; } // Private members are accessed directly if (PyUnicode_GetLength(attr_name) && PyUnicode_ReadChar(attr_name, 0) == '_') return PyType_Type.tp_setattro(self, attr_name, v); JPPyObject f = JPPyObject::accept(PyJP_GetAttrDescriptor((PyTypeObject*) self, attr_name)); if (f.isNull()) { const char *name_str = PyUnicode_AsUTF8(attr_name); PyErr_Format(PyExc_AttributeError, "Field '%s' is not found", name_str); return -1; } descrsetfunc desc = Py_TYPE(f.get())->tp_descr_set; if (desc != nullptr) return desc(f.get(), self, v); // Not a descriptor const char *name_str = PyUnicode_AsUTF8(attr_name); PyErr_Format(PyExc_AttributeError, "Static field '%s' is not settable on Java '%s' object", name_str, ((PyTypeObject*) self)->tp_name); return -1; JP_PY_CATCH(-1); } PyObject* PyJPClass_subclasscheck(PyTypeObject *type, PyTypeObject *test) { JP_PY_TRY("PyJPClass_subclasscheck"); if (test == type) Py_RETURN_TRUE; // GCOVR_EXCL_START // This is triggered only if the user asks for isInstance when the // JVM is shutdown. It should not happen in normal operations. if (!JPContext_global->isRunning()) { if ((PyObject*) type == _JObject) return PyBool_FromLong(PyJP_IsSubClassSingle(PyJPObject_Type, test)); return PyBool_FromLong(PyJP_IsSubClassSingle(type, test)); } // GCOVR_EXCL_STOP JPJavaFrame frame = JPJavaFrame::outer(); // Check for class inheritance first JPClass *testClass = PyJPClass_getJPClass((PyObject*) test); JPClass *typeClass = PyJPClass_getJPClass((PyObject*) type); if (testClass == nullptr) Py_RETURN_FALSE; if (testClass == typeClass) Py_RETURN_TRUE; if (typeClass != nullptr) { if (typeClass->isPrimitive()) Py_RETURN_FALSE; bool b = frame.IsAssignableFrom(testClass->getJavaClass(), typeClass->getJavaClass()) != 0; return PyBool_FromLong(b); } // Otherwise check for special cases if ((PyObject*) type == _JInterface) return PyBool_FromLong(testClass->isInterface()); if ((PyObject*) type == _JObject) return PyBool_FromLong(!testClass->isPrimitive()); if ((PyObject*) type == _JArray) return PyBool_FromLong(testClass->isArray()); if ((PyObject*) type == _JException) return PyBool_FromLong(testClass->isThrowable()); PyObject* mro1 = test->tp_mro; Py_ssize_t n1 = PyTuple_Size(mro1); for (int i = 0; i < n1; ++i) { if (PyTuple_GetItem(mro1, i) == (PyObject*) type) Py_RETURN_TRUE; } Py_RETURN_FALSE; JP_PY_CATCH(nullptr); } static PyObject *PyJPClass_class(PyObject *self, PyObject *closure) { JP_PY_TRY("PyJPClass_class"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue* javaSlot = PyJPValue_getJavaSlot(self); if (javaSlot == nullptr) { PyErr_SetString(PyExc_AttributeError, "Java slot is null"); return nullptr; } return javaSlot->getClass()->convertToPythonObject(frame, javaSlot->getValue(), false).keep(); JP_PY_CATCH(nullptr); } static int PyJPClass_setClass(PyObject *self, PyObject *type, PyObject *closure) { JP_PY_TRY("PyJPClass_setClass", self); JPJavaFrame frame = JPJavaFrame::outer(); JPContext *context = frame.getContext(); JPValue* javaSlot = PyJPValue_getJavaSlot(type); if (javaSlot == nullptr || javaSlot->getClass() != context->_java_lang_Class) { PyErr_SetString(PyExc_TypeError, "Java class instance is required"); return -1; } if (PyJPValue_isSetJavaSlot(self)) { PyErr_SetString(PyExc_AttributeError, "Java class can't be set"); return -1; } PyJPValue_assignJavaSlot(frame, self, *javaSlot); JPClass* cls = frame.findClass((jclass) javaSlot->getJavaObject()); JP_TRACE("Set host", cls, javaSlot->getClass()->getCanonicalName().c_str()); if (cls->getHost() == nullptr) cls->setHost(self); ((PyJPClass*) self)->m_Class = cls; return 0; JP_PY_CATCH(-1); } static PyObject *PyJPClass_hints(PyJPClass *self, PyObject *closure) { JP_PY_TRY("PyJPClass_hints"); PyJPModule_getContext(); JPPyObject hints = JPPyObject::use(self->m_Class->getHints()); if (hints.get() == nullptr) Py_RETURN_NONE; // GCOVR_EXCL_LINE only triggered if JClassPost failed if (PyObject_HasAttrString((PyObject*) self, "returns") == 1) return hints.keep(); // Copy in info. JPConversionInfo info; JPPyObject ret = JPPyObject::call(PyList_New(0)); JPPyObject implicit = JPPyObject::call(PyList_New(0)); JPPyObject attribs = JPPyObject::call(PyList_New(0)); JPPyObject exact = JPPyObject::call(PyList_New(0)); JPPyObject expl = JPPyObject::call(PyList_New(0)); JPPyObject none = JPPyObject::call(PyList_New(0)); info.ret = ret.get(); info.implicit = implicit.get(); info.attributes = attribs.get(); info.exact = exact.get(); info.expl = expl.get(); info.none = none.get(); self->m_Class->getConversionInfo(info); PyObject_SetAttrString(hints.get(), "returns", ret.get()); PyObject_SetAttrString(hints.get(), "implicit", implicit.get()); PyObject_SetAttrString(hints.get(), "exact", exact.get()); PyObject_SetAttrString(hints.get(), "explicit", expl.get()); PyObject_SetAttrString(hints.get(), "none", none.get()); PyObject_SetAttrString(hints.get(), "attributes", attribs.get()); return hints.keep(); JP_PY_CATCH(nullptr); } static int PyJPClass_setHints(PyObject *self, PyObject *value, PyObject *closure) { JP_PY_TRY("PyJPClass_setHints", self); PyJPModule_getContext(); auto *cls = (PyJPClass*) self; PyObject *hints = cls->m_Class->getHints(); if (hints != nullptr) { PyErr_SetString(PyExc_AttributeError, "_hints can't be set"); return -1; } cls->m_Class->setHints(value); return 0; JP_PY_CATCH(-1); } PyObject* PyJPClass_instancecheck(PyTypeObject *self, PyObject *test) { // JInterface is a meta if ((PyObject*) self == _JInterface) { JPJavaFrame frame = JPJavaFrame::outer(); JPClass *testClass = PyJPClass_getJPClass((PyObject*) test); return PyBool_FromLong(testClass != nullptr && testClass->isInterface()); } if ((PyObject*) self == _JException) { JPJavaFrame frame = JPJavaFrame::outer(); JPClass *testClass = PyJPClass_getJPClass((PyObject*) test); if (testClass) return PyBool_FromLong(testClass->isThrowable()); } return PyJPClass_subclasscheck(self, Py_TYPE(test)); } static PyObject *PyJPClass_canCast(PyJPClass *self, PyObject *other) { JP_PY_TRY("PyJPClass_canCast"); JPJavaFrame frame = JPJavaFrame::outer(); JPClass *cls = self->m_Class; // Test the conversion JPMatch match(&frame, other); cls->findJavaConversion(match); // Report to user return PyBool_FromLong(match.type == JPMatch::_exact || match.type == JPMatch::_implicit); JP_PY_CATCH(nullptr); } // Added for auditing static PyObject *PyJPClass_canConvertToJava(PyJPClass *self, PyObject *other) { JP_PY_TRY("PyJPClass_canConvertToJava"); JPJavaFrame frame = JPJavaFrame::outer(); JPClass *cls = self->m_Class; // Test the conversion JPMatch match(&frame, other); cls->findJavaConversion(match); // Report to user if (match.type == JPMatch::_none) return JPPyString::fromStringUTF8("none").keep(); if (match.type == JPMatch::_explicit) return JPPyString::fromStringUTF8("explicit").keep(); if (match.type == JPMatch::_implicit) return JPPyString::fromStringUTF8("implicit").keep(); if (match.type == JPMatch::_derived) return JPPyString::fromStringUTF8("derived").keep(); if (match.type == JPMatch::_exact) return JPPyString::fromStringUTF8("exact").keep(); // Not sure how this could happen Py_RETURN_NONE; // GCOVR_EXCL_LINE JP_PY_CATCH(nullptr); } // Return true if the slice is all indices static bool PySlice_CheckFull(PyObject *item) { if (!PySlice_Check(item)) return false; Py_ssize_t start, stop, step; int rc = PySlice_Unpack(item, &start, &stop, &step); #if defined(ANDROID) return (rc == 0)&&(start == 0)&&(step == 1)&&((int) stop >= 0x7fffffff); #else return (rc == 0)&&(start == 0)&&(step == 1)&&((int) stop == -1); #endif } static PyObject *PyJPClass_array(PyJPClass *self, PyObject *item) { JP_PY_TRY("PyJPClass_array"); JPJavaFrame frame = JPJavaFrame::outer(); JPContext *context = frame.getContext(); if (self->m_Class == NULL) { // This is only reachable through an internal Jpype type that doesn't have a // Java class equivalent such as JArray. // __getitem__ takes precedence over __class_getitem__ which forces us through // this path. // If __class_getitem__ is not implemented in this class, the raised AttributeError // will make its way back to Python. PyObject *res = PyObject_CallMethod((PyObject *)self, "__class_getitem__", "O", item); Py_DECREF(item); return res; } if (PyIndex_Check(item)) { long sz = PyLong_AsLong(item); auto *cls = dynamic_cast( self->m_Class->newArrayType(frame, 1)); JPValue v = cls->newArray(frame, sz); return cls->convertToPythonObject(frame, v.getValue(), true).keep(); } if (PySlice_Check(item)) { if (PySlice_CheckFull(item)) { JPClass *cls = self->m_Class->newArrayType(frame, 1); return PyJPClass_create(frame, cls).keep(); } PyErr_Format(PyExc_TypeError, "Bad array specification on slice"); return nullptr; } if (PyTuple_Check(item)) { Py_ssize_t dims = PyTuple_Size(item); Py_ssize_t i = 0; Py_ssize_t defined = 0; Py_ssize_t undefined = 0; std::vector sz; for (; i < dims; ++i) { PyObject* t = PyTuple_GetItem(item, i); if (PyIndex_Check(t) && PyLong_AsLong(t) > 0) { defined++; sz.push_back(PyLong_AsLong(t)); } else break; } for (; i < dims; ++i) if (PySlice_CheckFull(PyTuple_GetItem(item, i))) undefined++; else break; if (defined + undefined != dims) { PyErr_SetString(PyExc_TypeError, "Invalid array definition"); return nullptr; } // Get the type JPClass *cls; if (undefined > 0) cls = self->m_Class->newArrayType(frame, undefined); else cls = self->m_Class; // If no dimensions were defined then just return the type if (defined == 0) return PyJPClass_create(frame, cls).keep(); // Otherwise create an array jintArray u = frame.NewIntArray(defined); JPPrimitiveArrayAccessor accessor(frame, u, &JPJavaFrame::GetIntArrayElements, &JPJavaFrame::ReleaseIntArrayElements); for (size_t j = 0; j < sz.size(); ++j) accessor.get()[j] = sz[j]; accessor.commit(); jvalue v; v.l = frame.newArrayInstance(cls->getJavaClass(), u); return context->_java_lang_Object->convertToPythonObject(frame, v, false).keep(); } PyErr_Format(PyExc_TypeError, "Bad array specification"); JP_PY_CATCH(nullptr); } static PyObject *PyJPClass_cast(PyJPClass *self, PyObject *other) { JP_PY_TRY("PyJPClass_cast"); JPJavaFrame frame = JPJavaFrame::outer(); JPClass *type = self->m_Class; JPValue *val = PyJPValue_getJavaSlot(other); // Cast on non-Java if (val == nullptr || val->getClass()->isPrimitive()) { JPMatch match(&frame, other); type->findJavaConversion(match); // Otherwise, see if we can convert it if (match.type == JPMatch::_none) { PyErr_Format(PyExc_TypeError, "Unable to cast '%s' to java type '%s'", Py_TYPE(other)->tp_name, type->getCanonicalName().c_str() ); return nullptr; } jvalue v = match.convert(); return type->convertToPythonObject(frame, v, true).keep(); } // Cast on java object // if (!type->isSubTypeOf(val->getClass())) jobject obj = val->getJavaObject(); if (obj == nullptr) { jvalue v; v.l = nullptr; return type->convertToPythonObject(frame, v, true).keep(); } JPClass *otherClass = frame.findClassForObject(obj); if (otherClass == nullptr) { return type->convertToPythonObject(frame, val->getValue(), true).keep(); } if (!otherClass->isAssignableFrom(frame, type)) { PyErr_Format(PyExc_TypeError, "Unable to cast '%s' to java type '%s'", otherClass->getCanonicalName().c_str(), type->getCanonicalName().c_str() ); return nullptr; } // Special case. If the otherClass is an array and the array is // a slice then we need to copy it here. if (PyObject_IsInstance(other, (PyObject*) PyJPArray_Type)) { auto *array = (PyJPArray*) other; if (array->m_Array->isSlice()) { jvalue v; v.l = array->m_Array->clone(frame, other); return type->convertToPythonObject(frame, v, true).keep(); } } return type->convertToPythonObject(frame, val->getValue(), true).keep(); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyObject *PyJPClass_castEq(PyJPClass *self, PyObject *other) { PyErr_Format(PyExc_TypeError, "Invalid operation"); return nullptr; } // Added for auditing static PyObject *PyJPClass_convertToJava(PyJPClass *self, PyObject *other) { JP_PY_TRY("PyJPClass_convertToJava"); JPJavaFrame frame = JPJavaFrame::outer(); JPClass *cls = self->m_Class; // Test the conversion JPMatch match(&frame, other); cls->findJavaConversion(match); // If there is no conversion report a failure if (match.type == JPMatch::_none) { PyErr_SetString(PyExc_TypeError, "Unable to create an instance."); return nullptr; } // Otherwise give back a PyJPValue jvalue v = match.convert(); return cls->convertToPythonObject(frame, v, true).keep(); JP_PY_CATCH(nullptr); } static PyObject *PyJPClass_repr(PyJPClass *self) { JP_PY_TRY("PyJPClass_repr"); string name = ((PyTypeObject*) self)->tp_name; return PyUnicode_FromFormat("", name.c_str()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPClass_getDoc(PyJPClass *self, void *ctxt) { JP_PY_TRY("PyJPMethod_getDoc"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Doc) { Py_INCREF(self->m_Doc); return self->m_Doc; } // Pack the arguments { JP_TRACE("Pack arguments"); JPPyObject args = JPPyTuple_Pack(self); JP_TRACE("Call Python"); self->m_Doc = PyObject_Call(_JClassDoc, args.get(), nullptr); Py_XINCREF(self->m_Doc); return self->m_Doc; } JP_PY_CATCH(nullptr); } int PyJPClass_setDoc(PyJPClass *self, PyObject *obj, void *ctxt) { JP_PY_TRY("PyJPClass_setDoc"); Py_CLEAR(self->m_Doc); self->m_Doc = obj; Py_XINCREF(self->m_Doc); return 0; JP_PY_CATCH(-1); } PyObject* PyJPClass_customize(PyJPClass *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPClass_customize"); PyObject *name = nullptr; PyObject *value = nullptr; if (!PyArg_ParseTuple(args, "OO", &name, &value)) return nullptr; if (PyType_Type.tp_setattro((PyObject*) self, name, value) == -1) return nullptr; Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyMethodDef classMethods[] = { {"__instancecheck__", (PyCFunction) PyJPClass_instancecheck, METH_O, ""}, {"__subclasscheck__", (PyCFunction) PyJPClass_subclasscheck, METH_O, ""}, {"mro", (PyCFunction) PyJPClass_mro, METH_NOARGS, ""}, {"_canConvertToJava", (PyCFunction) PyJPClass_canConvertToJava, METH_O, ""}, {"_convertToJava", (PyCFunction) PyJPClass_convertToJava, METH_O, ""}, {"_cast", (PyCFunction) PyJPClass_cast, METH_O, ""}, {"_canCast", (PyCFunction) PyJPClass_canCast, METH_O, ""}, {"__getitem__", (PyCFunction) PyJPClass_array, METH_O | METH_COEXIST, ""}, {"_customize", (PyCFunction) PyJPClass_customize, METH_VARARGS, ""}, {nullptr}, }; static PyGetSetDef classGetSets[] = { {"class_", (getter) PyJPClass_class, (setter) PyJPClass_setClass, ""}, {"_hints", (getter) PyJPClass_hints, (setter) PyJPClass_setHints, ""}, {"__doc__", (getter) PyJPClass_getDoc, (setter) PyJPClass_setDoc, nullptr, nullptr}, {nullptr} }; static PyType_Slot classSlots[] = { { Py_tp_alloc, (void*) PyJPValue_alloc}, { Py_tp_finalize, (void*) PyJPValue_finalize}, { Py_tp_init, (void*) PyJPClass_init}, { Py_tp_dealloc, (void*) PyJPClass_dealloc}, { Py_tp_traverse, (void*) PyJPClass_traverse}, { Py_tp_clear, (void*) PyJPClass_clear}, { Py_tp_repr, (void*) PyJPClass_repr}, { Py_tp_getattro, (void*) PyJPClass_getattro}, { Py_tp_setattro, (void*) PyJPClass_setattro}, { Py_tp_methods, (void*) classMethods}, { Py_tp_getset, (void*) classGetSets}, { Py_mp_subscript, (void*) PyJPClass_array}, { Py_nb_matrix_multiply, (void*) PyJPClass_cast}, { Py_nb_inplace_matrix_multiply, (void*) PyJPClass_castEq}, {0} }; PyTypeObject* PyJPClass_Type = nullptr; static PyType_Spec classSpec = { "_jpype._JClass", sizeof (PyJPClass), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, classSlots }; #ifdef __cplusplus } #endif void PyJPClass_initType(PyObject* module) { JPPyObject bases = JPPyTuple_Pack(&PyType_Type); PyJPClass_Type = (PyTypeObject*) PyType_FromSpecWithBases(&classSpec, bases.get()); JP_PY_CHECK(); PyModule_AddObject(module, "_JClass", (PyObject*) PyJPClass_Type); JP_PY_CHECK(); } JPClass* PyJPClass_getJPClass(PyObject* obj) { try { if (obj == nullptr) return nullptr; if (PyJPClass_Check(obj)) return ((PyJPClass*) obj)->m_Class; JPValue* javaSlot = PyJPValue_getJavaSlot(obj); if (javaSlot == nullptr) return nullptr; JPClass *cls = javaSlot->getClass(); JPJavaFrame frame = JPJavaFrame::outer(); if (cls != frame.getContext()->_java_lang_Class) return nullptr; return frame.findClass((jclass) javaSlot->getJavaObject()); } catch (...) // GCOVR_EXCL_LINE { return nullptr; // GCOVR_EXCL_LINE } } JPPyObject PyJPClass_getBases(JPJavaFrame &frame, JPClass* cls) { JP_TRACE_IN("PyJPClass_bases"); cls->ensureMembers(frame); // Decide the base for this object JPPyObject baseType; JPContext *context = frame.getContext(); JPClass *super = cls->getSuperClass(); if (dynamic_cast (cls) == cls) { if (cls == context->_java_lang_Boolean) { baseType = JPPyObject::use((PyObject*) PyJPNumberBool_Type); } else if (cls == context->_java_lang_Character) { baseType = JPPyObject::use((PyObject*) PyJPChar_Type); } else if (cls == context->_java_lang_Boolean || cls == context->_java_lang_Byte || cls == context->_java_lang_Short || cls == context->_java_lang_Integer || cls == context->_java_lang_Long ) { baseType = JPPyObject::use((PyObject*) PyJPNumberLong_Type); } else if (cls == context->_java_lang_Float || cls == context->_java_lang_Double ) { baseType = JPPyObject::use((PyObject*) PyJPNumberFloat_Type); } } else if (JPModifier::isBuffer(cls->getModifiers())) { baseType = JPPyObject::use((PyObject*) PyJPBuffer_Type); } else if (cls == context->_java_lang_Throwable) { baseType = JPPyObject::use((PyObject*) PyJPException_Type); } else if (cls->isArray()) { auto* acls = dynamic_cast( cls); if (acls->getComponentType()->isPrimitive()) baseType = JPPyObject::use((PyObject*) PyJPArrayPrimitive_Type); else baseType = JPPyObject::use((PyObject*) PyJPArray_Type); } else if (cls->getCanonicalName() == "java.lang.Comparable") { baseType = JPPyObject::use((PyObject*) PyJPComparable_Type); } else if (super == nullptr) { baseType = JPPyObject::use((PyObject*) PyJPObject_Type); } const JPClassList& baseItf = cls->getInterfaces(); Py_ssize_t count = baseItf.size() + (!baseType.isNull() ? 1 : 0) + (super != nullptr ? 1 : 0); // Pack into a tuple JPPyObject result = JPPyObject::call(PyList_New(count)); unsigned int i = 0; for (; i < baseItf.size(); i++) { PyList_SetItem(result.get(), i, PyJPClass_create(frame, baseItf[i]).keep()); } if (super != nullptr) { PyList_SetItem(result.get(), i++, PyJPClass_create(frame, super).keep()); } if (!baseType.isNull()) { PyList_SetItem(result.get(), i++, baseType.keep()); } return result; JP_TRACE_OUT; } /** * Internal method for wrapping a returned Java class instance. * * This checks the cache for existing wrappers and then * transfers control to JClassFactory. This is required because all of * the post load stuff needs to be in Python. * * @param cls * @return */ JPPyObject PyJPClass_create(JPJavaFrame &frame, JPClass* cls) { JP_TRACE_IN("PyJPClass_create", cls); // Check the cache for speed auto *host = (PyObject*) cls->getHost(); if (host == nullptr) { frame.newWrapper(cls); host = (PyObject*) cls->getHost(); } return JPPyObject::use(host); JP_TRACE_OUT; } void PyJPClass_hook(JPJavaFrame &frame, JPClass* cls) { JPContext *context = frame.getContext(); auto *host = (PyObject*) cls->getHost(); if (host != nullptr) return; JPPyObject members = JPPyObject::call(PyDict_New()); JPPyObject args = JPPyTuple_Pack( JPPyString::fromStringUTF8(cls->getCanonicalName()).get(), PyJPClass_getBases(frame, cls).get(), members.get()); // Catch creation loop, the process of creating our parent host = (PyObject*) cls->getHost(); if (host != nullptr) return; const JPFieldList & instFields = cls->getFields(); for (auto instField : instFields) { JPPyObject fieldName(JPPyString::fromStringUTF8(instField->getName())); PyDict_SetItem(members.get(), fieldName.get(), PyJPField_create(instField).get()); } const JPMethodDispatchList& m_Methods = cls->getMethods(); for (auto m_Method : m_Methods) { JPPyObject methodName(JPPyString::fromStringUTF8(m_Method->getName())); PyDict_SetItem(members.get(), methodName.get(), PyJPMethod_create(m_Method, nullptr).get()); } if (cls->isInterface()) { const JPMethodDispatchList& m_Methods2 = context->_java_lang_Object->getMethods(); for (auto m_Method : m_Methods2) { JPPyObject methodName(JPPyString::fromStringUTF8(m_Method->getName())); PyDict_SetItem(members.get(), methodName.get(), PyJPMethod_create(m_Method, nullptr).get()); } } // Call the customizer to make any required changes to the tables. JP_TRACE("call pre"); JPPyObject rc = JPPyObject::call(PyObject_Call(_JClassPre, args.get(), nullptr)); JP_TRACE("type new"); // Create the type using the meta class magic JPPyObject vself = JPPyObject::call(PyJPClass_Type->tp_call((PyObject*) PyJPClass_Type, rc.get(), PyJPClassMagic)); auto *self = (PyJPClass*) vself.get(); // Attach the javaSlot self->m_Class = cls; // self->m_Class->postLoad(); PyJPValue_assignJavaSlot(frame, (PyObject*) self, JPValue(context->_java_lang_Class, (jobject) self->m_Class->getJavaClass())); // Attach the cache (adds reference, thus wrapper lives to end of JVM) JP_TRACE("set host"); cls->setHost((PyObject*) self); // Call the post load routine to attach inner classes JP_TRACE("call post"); args = JPPyTuple_Pack(self); JPPyObject rc2 = JPPyObject::call(PyObject_Call(_JClassPost, args.get(), nullptr)); } jpype-1.6.0/native/python/pyjp_classhints.cpp000066400000000000000000000115771501674766700214330ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #ifdef __cplusplus extern "C" { #endif PyObject *PyJPClassHints_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPClassHints_new", type); auto *self = (PyJPClassHints*) type->tp_alloc(type, 0); self->m_Hints = new JPClassHints(); return (PyObject*) self; JP_PY_CATCH(nullptr); } int PyJPClassHints_init(PyJPClassHints *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPClassHints_init", self); return 0; JP_PY_CATCH(-1); } void PyJPClassHints_dealloc(PyJPClassHints *self) { JP_PY_TRY("PyJPClassHints_dealloc", self); delete self->m_Hints; // Free self Py_TYPE(self)->tp_free(self); JP_PY_CATCH(); // GCOVR_EXCL_LINE } PyObject *PyJPClassHints_str(PyJPClassHints *self) { JP_PY_TRY("PyJPClassHints_str", self); return PyUnicode_FromFormat(""); JP_PY_CATCH(nullptr); } PyObject *PyJPClassHints_addAttributeConversion(PyJPClassHints *self, PyObject* args, PyObject* kwargs) { JP_PY_TRY("PyJPClassHints_addAttributeConversion", self); char* attribute; PyObject *method; if (!PyArg_ParseTuple(args, "sO", &attribute, &method)) return nullptr; JP_TRACE(attribute); JP_TRACE(Py_TYPE(method)->tp_name); if (!PyCallable_Check(method)) { PyErr_SetString(PyExc_TypeError, "callable method is required"); return nullptr; } self->m_Hints->addAttributeConversion(string(attribute), method); Py_RETURN_NONE; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static void badType(PyObject* t) { PyErr_Format(PyExc_TypeError, "type or protocol is required, not '%s'", Py_TYPE(t)->tp_name); } PyObject *PyJPClassHints_addTypeConversion(PyJPClassHints *self, PyObject* args, PyObject* kwargs) { JP_PY_TRY("PyJPClassHints_addTypeConversion", self); PyObject *type; PyObject *method; unsigned char exact; if (!PyArg_ParseTuple(args, "OOb", &type, &method, &exact)) return nullptr; if (!PyType_Check(type) && !PyObject_HasAttrString((PyObject*) type, "__instancecheck__")) { badType(type); return nullptr; } if (!PyCallable_Check(method)) { PyErr_SetString(PyExc_TypeError, "callable method is required"); return nullptr; } self->m_Hints->addTypeConversion(type, method, exact != 0); Py_RETURN_NONE; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } PyObject *PyJPClassHints_excludeConversion(PyJPClassHints *self, PyObject* types, PyObject* kwargs) { JP_PY_TRY("PyJPClassHints_excludeConversion", self); if (PyTuple_Check(types)) { Py_ssize_t sz = PyTuple_Size(types); for (Py_ssize_t i = 0; i < sz; ++i) { PyObject *t = PyTuple_GetItem(types, i); if (!PyType_Check(t) && !PyObject_HasAttrString(t, "__instancecheck__")) { badType(t); return nullptr; } } for (Py_ssize_t i = 0; i < sz; ++i) { self->m_Hints->excludeConversion(PyTuple_GetItem(types, i)); } } else { if (!PyType_Check(types) && !PyObject_HasAttrString( types, "__instancecheck__")) { badType(types); return nullptr; } self->m_Hints->excludeConversion(types); } Py_RETURN_NONE; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyMethodDef classMethods[] = { {"_addAttributeConversion", (PyCFunction) & PyJPClassHints_addAttributeConversion, METH_VARARGS, ""}, {"_addTypeConversion", (PyCFunction) & PyJPClassHints_addTypeConversion, METH_VARARGS, ""}, {"_excludeConversion", (PyCFunction) & PyJPClassHints_excludeConversion, METH_O, ""}, {nullptr}, }; static PyType_Slot hintsSlots[] = { { Py_tp_new , (void*) PyJPClassHints_new}, { Py_tp_init, (void*) PyJPClassHints_init}, { Py_tp_dealloc, (void*) PyJPClassHints_dealloc}, { Py_tp_str, (void*) PyJPClassHints_str}, { Py_tp_doc, (void*) "Java Class Hints"}, { Py_tp_methods, (void*) classMethods}, {0} }; PyTypeObject *PyJPClassHints_Type = nullptr; PyType_Spec PyJPClassHintsSpec = { "_jpype._JClassHints", sizeof (PyJPClassHints), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, hintsSlots }; #ifdef __cplusplus } #endif void PyJPClassHints_initType(PyObject* module) { PyJPClassHints_Type = (PyTypeObject*) PyType_FromSpec(&PyJPClassHintsSpec); JP_PY_CHECK(); // GCOVR_EXCL_LINE sanity check PyModule_AddObject(module, "_JClassHints", (PyObject*) PyJPClassHints_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE sanity check } jpype-1.6.0/native/python/pyjp_field.cpp000066400000000000000000000072121501674766700203320ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_field.h" #ifdef __cplusplus extern "C" { #endif struct PyJPField { PyObject_HEAD JPField* m_Field; } ; static void PyJPField_dealloc(PyJPField *self) { self->m_Field = nullptr; Py_TYPE(self)->tp_free(self); } static PyObject *PyJPField_get(PyJPField *self, PyObject *obj, PyObject *type) { JP_PY_TRY("PyJPField_get"); JPJavaFrame frame = JPJavaFrame::outer(); // Clear any pending interrupts if we are on the main thread. if (hasInterrupt()) frame.clearInterrupt(false); if (self->m_Field->isStatic()) return self->m_Field->getStaticField().keep(); if (obj == nullptr) JP_RAISE(PyExc_AttributeError, "Field is not static"); JPValue *jval = PyJPValue_getJavaSlot(obj); if (jval == nullptr) JP_RAISE(PyExc_AttributeError, "Field requires instance value"); return self->m_Field->getField(jval->getValue().l).keep(); JP_PY_CATCH(nullptr); } static int PyJPField_set(PyJPField *self, PyObject *obj, PyObject *pyvalue) { JP_PY_TRY("PyJPField_set"); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Field->isFinal()) { PyErr_SetString(PyExc_AttributeError, "Field is final"); return -1; } if (self->m_Field->isStatic()) { self->m_Field->setStaticField(pyvalue); return 0; } if (obj == Py_None || PyJPClass_Check(obj)) { PyErr_SetString(PyExc_AttributeError, "Field is not static"); return -1; } JPValue *jval = PyJPValue_getJavaSlot(obj); if (jval == nullptr) { PyErr_Format(PyExc_AttributeError, "Field requires instance value, not '%s'", Py_TYPE(obj)->tp_name); return -1; } self->m_Field->setField(jval->getValue().l, pyvalue); return 0; JP_PY_CATCH(-1); } static PyObject *PyJPField_repr(PyJPField *self) { JP_PY_TRY("PyJPField_repr"); JPJavaFrame frame = JPJavaFrame::outer(); return PyUnicode_FromFormat("", self->m_Field->getName().c_str(), self->m_Field->getClass()->getCanonicalName().c_str() ); JP_PY_CATCH(nullptr); } static PyGetSetDef fieldGetSets[] = { {nullptr} }; static PyType_Slot fieldSlots[] = { { Py_tp_dealloc, (void*) PyJPField_dealloc}, { Py_tp_descr_get, (void*) PyJPField_get}, { Py_tp_descr_set, (void*) PyJPField_set}, { Py_tp_repr, (void*) &PyJPField_repr}, { Py_tp_getset, (void*) &fieldGetSets}, {0} }; PyTypeObject *PyJPField_Type = nullptr; PyType_Spec PyJPFieldSpec = { "_jpype._JField", sizeof (PyJPField), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, fieldSlots }; #ifdef __cplusplus } #endif void PyJPField_initType(PyObject* module) { PyJPField_Type = (PyTypeObject*) PyType_FromSpec(&PyJPFieldSpec); JP_PY_CHECK(); PyModule_AddObject(module, "_JField", (PyObject*) PyJPField_Type); JP_PY_CHECK(); } JPPyObject PyJPField_create(JPField* m) { JP_TRACE_IN("PyJPField_create"); auto* self = (PyJPField*) PyJPField_Type->tp_alloc(PyJPField_Type, 0); JP_PY_CHECK(); self->m_Field = m; return JPPyObject::claim((PyObject*) self); JP_TRACE_OUT; // GCOVR_EXCL_LINE } jpype-1.6.0/native/python/pyjp_method.cpp000066400000000000000000000306121501674766700205270ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_methoddispatch.h" #include "jp_method.h" #ifdef __cplusplus extern "C" { #endif struct PyJPMethod { PyFunctionObject func; JPMethodDispatch* m_Method; PyObject* m_Instance; PyObject* m_Doc; PyObject* m_Annotations; PyObject* m_CodeRep; } ; static int PyJPMethod_traverse(PyJPMethod *self, visitproc visit, void *arg) { Py_VISIT(self->m_Instance); Py_VISIT(self->m_Doc); Py_VISIT(self->m_Annotations); Py_VISIT(self->m_CodeRep); return 0; } static int PyJPMethod_clear(PyJPMethod *self) { Py_CLEAR(self->m_Instance); Py_CLEAR(self->m_Doc); Py_CLEAR(self->m_Annotations); Py_CLEAR(self->m_CodeRep); return 0; } static void PyJPMethod_dealloc(PyJPMethod *self) { JP_PY_TRY("PyJPMethod_dealloc"); PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, PyJPMethod_dealloc) PyJPMethod_clear(self); Py_TYPE(self)->tp_free(self); Py_TRASHCAN_END JP_PY_CATCH_NONE(); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_get(PyJPMethod *self, PyObject *obj, PyObject *type) { JP_PY_TRY("PyJPMethod_get"); PyJPModule_getContext(); if (obj == nullptr) { Py_INCREF((PyObject*) self); JP_TRACE_PY("method get (inc)", (PyObject*) self); return (PyObject*) self; } PyJPMethod *out = (PyJPMethod*) PyJPMethod_create(self->m_Method, obj).keep(); if (self->m_Doc != nullptr) { out->m_Doc = self->m_Doc; Py_INCREF(out->m_Doc); } if (self->m_Annotations != nullptr) { out->m_Annotations = self->m_Annotations; Py_INCREF(out->m_Annotations); } return (PyObject*) out; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_call(PyJPMethod *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPMethod_call"); JPJavaFrame frame = JPJavaFrame::outer(); JP_TRACE(self->m_Method->getName()); // Clear any pending interrupts if we are on the main thread if (hasInterrupt()) frame.clearInterrupt(false); PyObject *out = nullptr; if (self->m_Instance == nullptr) { JPPyObjectVector vargs(args); out = self->m_Method->invoke(frame, vargs, false).keep(); } else { JPPyObjectVector vargs(self->m_Instance, args); out = self->m_Method->invoke(frame, vargs, true).keep(); } return out; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_matches(PyJPMethod *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPMethod_matches"); JPJavaFrame frame = JPJavaFrame::outer(); JP_TRACE(self->m_Method->getName()); if (self->m_Instance == nullptr) { JPPyObjectVector vargs(args); return PyBool_FromLong(self->m_Method->matches(frame, vargs, false)); } else { JPPyObjectVector vargs(self->m_Instance, args); return PyBool_FromLong(self->m_Method->matches(frame, vargs, true)); } JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_str(PyJPMethod *self) { JP_PY_TRY("PyJPMethod_str"); JPJavaFrame frame = JPJavaFrame::outer(); return PyUnicode_FromFormat("%s.%s", self->m_Method->getClass()->getCanonicalName().c_str(), self->m_Method->getName().c_str()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_repr(PyJPMethod *self) { JP_PY_TRY("PyJPMethod_repr"); PyJPModule_getContext(); return PyUnicode_FromFormat("", (self->m_Instance != nullptr) ? "bound " : "", self->m_Method->getName().c_str(), self->m_Method->getClass()->getCanonicalName().c_str()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_getSelf(PyJPMethod *self, void *ctxt) { JP_PY_TRY("PyJPMethod_getSelf"); PyJPModule_getContext(); if (self->m_Instance == nullptr) Py_RETURN_NONE; Py_INCREF(self->m_Instance); return self->m_Instance; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_getNone(PyJPMethod *self, void *ctxt) { Py_RETURN_NONE; } static PyObject *PyJPMethod_getName(PyJPMethod *self, void *ctxt) { JP_PY_TRY("PyJPMethod_getName"); PyJPModule_getContext(); return JPPyString::fromStringUTF8(self->m_Method->getName()).keep(); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_getQualName(PyJPMethod *self, void *ctxt) { JP_PY_TRY("PyJPMethod_getQualName"); PyJPModule_getContext(); return PyUnicode_FromFormat("%s.%s", self->m_Method->getClass()->getCanonicalName().c_str(), self->m_Method->getName().c_str()); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPMethod_getDoc(PyJPMethod *self, void *ctxt) { JP_PY_TRY("PyJPMethod_getDoc"); JPContext *context = PyJPModule_getContext(); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Doc) { Py_INCREF(self->m_Doc); return self->m_Doc; } // Convert the overloads JP_TRACE("Convert overloads"); const JPMethodList& overloads = self->m_Method->getMethodOverloads(); JPPyObject ov = JPPyObject::call(PyTuple_New(overloads.size())); int i = 0; JPClass* methodClass = frame.findClassByName("java.lang.reflect.Method"); for (auto iter = overloads.begin(); iter != overloads.end(); ++iter) { JP_TRACE("Set overload", i); jvalue v; v.l = (*iter)->getJava(); JPPyObject obj(methodClass->convertToPythonObject(frame, v, true)); PyTuple_SetItem(ov.get(), i++, obj.keep()); } // Pack the arguments { JP_TRACE("Pack arguments"); jvalue v; v.l = (jobject) self->m_Method->getClass()->getJavaClass(); JPPyObject obj(context->_java_lang_Class->convertToPythonObject(frame, v, true)); JPPyObject args = JPPyTuple_Pack(self, obj.get(), ov.get()); JP_TRACE("Call Python"); self->m_Doc = PyObject_Call(_JMethodDoc, args.get(), nullptr); Py_XINCREF(self->m_Doc); return self->m_Doc; } JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } int PyJPMethod_setDoc(PyJPMethod *self, PyObject *obj, void *ctxt) { JP_PY_TRY("PyJPMethod_setDoc"); Py_CLEAR(self->m_Doc); self->m_Doc = obj; Py_XINCREF(self->m_Doc); return 0; JP_PY_CATCH(-1); // GCOVR_EXCL_LINE } PyObject *PyJPMethod_getAnnotations(PyJPMethod *self, void *ctxt) { JP_PY_TRY("PyJPMethod_getAnnotations"); JPContext *context = PyJPModule_getContext(); JPJavaFrame frame = JPJavaFrame::outer(); if (self->m_Annotations) { Py_INCREF(self->m_Annotations); return self->m_Annotations; } // Convert the overloads JP_TRACE("Convert overloads"); const JPMethodList& overloads = self->m_Method->getMethodOverloads(); JPPyObject ov = JPPyObject::call(PyTuple_New(overloads.size())); int i = 0; JPClass* methodClass = frame.findClassByName("java.lang.reflect.Method"); for (auto iter = overloads.begin(); iter != overloads.end(); ++iter) { JP_TRACE("Set overload", i); jvalue v; v.l = (*iter)->getJava(); JPPyObject obj(methodClass->convertToPythonObject(frame, v, true)); PyTuple_SetItem(ov.get(), i++, obj.keep()); } // Pack the arguments { JP_TRACE("Pack arguments"); jvalue v; v.l = (jobject) self->m_Method->getClass()->getJavaClass(); JPPyObject obj(context->_java_lang_Class->convertToPythonObject(frame, v, true)); JPPyObject args = JPPyTuple_Pack(self, obj.get(), ov.get()); JP_TRACE("Call Python"); self->m_Annotations = PyObject_Call(_JMethodAnnotations, args.get(), nullptr); } Py_XINCREF(self->m_Annotations); return self->m_Annotations; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } int PyJPMethod_setAnnotations(PyJPMethod *self, PyObject* obj, void *ctx) { Py_CLEAR(self->m_Annotations); self->m_Annotations = obj; Py_XINCREF(self->m_Annotations); return 0; } PyObject *PyJPMethod_getCodeAttr(PyJPMethod *self, void *ctx, const char *attr) { JP_PY_TRY("PyJPMethod_getCodeAttr"); PyJPModule_getContext(); if (self->m_CodeRep == nullptr) { JPPyObject args = JPPyTuple_Pack(self); JP_TRACE("Call Python"); self->m_CodeRep = PyObject_Call(_JMethodCode, args.get(), nullptr); } return PyObject_GetAttrString(self->m_CodeRep, attr); JP_PY_CATCH(nullptr); } PyObject *PyJPMethod_getCode(PyJPMethod *self, void *ctxt) { return PyJPMethod_getCodeAttr(self, ctxt, "__code__"); } PyObject *PyJPMethod_getClosure(PyJPMethod *self, void *ctxt) { return PyJPMethod_getCodeAttr(self, ctxt, "__closure__"); } PyObject *PyJPMethod_getGlobals(PyJPMethod *self, void *ctxt) { return PyJPMethod_getCodeAttr(self, ctxt, "__globals__"); } PyObject *PyJPMethod_isBeanAccessor(PyJPMethod *self, PyObject *arg) { JP_PY_TRY("PyJPMethod_isBeanAccessor"); PyJPModule_getContext(); return PyBool_FromLong(self->m_Method->isBeanAccessor()); JP_PY_CATCH(nullptr); } PyObject *PyJPMethod_isBeanMutator(PyJPMethod *self, PyObject *arg) { JP_PY_TRY("PyJPMethod_isBeanMutator"); PyJPModule_getContext(); return PyBool_FromLong(self->m_Method->isBeanMutator()); JP_PY_CATCH(nullptr); } PyObject *PyJPMethod_matchReport(PyJPMethod *self, PyObject *args) { JP_PY_TRY("PyJPMethod_matchReport"); PyJPModule_getContext(); JPPyObjectVector vargs(args); string report = self->m_Method->matchReport(vargs); return JPPyString::fromStringUTF8(report).keep(); JP_PY_CATCH(nullptr); } static PyMethodDef methodMethods[] = { {"_isBeanAccessor", (PyCFunction) (&PyJPMethod_isBeanAccessor), METH_NOARGS, ""}, {"_isBeanMutator", (PyCFunction) (&PyJPMethod_isBeanMutator), METH_NOARGS, ""}, {"matchReport", (PyCFunction) (&PyJPMethod_matchReport), METH_VARARGS, ""}, // This is currently private but may be promoted {"_matches", (PyCFunction) (&PyJPMethod_matches), METH_VARARGS, ""}, {nullptr}, }; struct PyGetSetDef methodGetSet[] = { {"__self__", (getter) (&PyJPMethod_getSelf), nullptr, nullptr, nullptr}, {"__name__", (getter) (&PyJPMethod_getName), nullptr, nullptr, nullptr}, {"__doc__", (getter) (&PyJPMethod_getDoc), (setter) (&PyJPMethod_setDoc), nullptr, nullptr}, {"__annotations__", (getter) (&PyJPMethod_getAnnotations), (setter) (&PyJPMethod_setAnnotations), nullptr, nullptr}, {"__closure__", (getter) (&PyJPMethod_getClosure), nullptr, nullptr, nullptr}, {"__code__", (getter) (&PyJPMethod_getCode), nullptr, nullptr, nullptr}, {"__defaults__", (getter) (&PyJPMethod_getNone), nullptr, nullptr, nullptr}, {"__kwdefaults__", (getter) (&PyJPMethod_getNone), nullptr, nullptr, nullptr}, {"__globals__", (getter) (&PyJPMethod_getGlobals), nullptr, nullptr, nullptr}, {"__qualname__", (getter) (&PyJPMethod_getQualName), nullptr, nullptr, nullptr}, {nullptr}, }; static PyType_Slot methodSlots[] = { {Py_tp_dealloc, (void*) PyJPMethod_dealloc}, {Py_tp_traverse, (void*) PyJPMethod_traverse}, {Py_tp_clear, (void*) PyJPMethod_clear}, {Py_tp_repr, (void*) PyJPMethod_repr}, {Py_tp_call, (void*) PyJPMethod_call}, {Py_tp_str, (void*) PyJPMethod_str}, {Py_tp_descr_get, (void*) PyJPMethod_get}, {Py_tp_methods, (void*) methodMethods}, {Py_tp_getset, (void*) methodGetSet}, {0} }; PyTypeObject *PyJPMethod_Type = nullptr; static PyType_Spec methodSpec = { "_jpype._JMethod", sizeof (PyJPMethod), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, methodSlots }; #ifdef __cplusplus } #endif void PyJPMethod_initType(PyObject* module) { // We inherit from PyFunction_Type just so we are an instance // for purposes of inspect and tab completion tools. But // we will just ignore their memory layout as we have our own. JPPyObject tuple = JPPyTuple_Pack(&PyFunction_Type); unsigned long flags = PyFunction_Type.tp_flags; PyFunction_Type.tp_flags |= Py_TPFLAGS_BASETYPE; PyJPMethod_Type = (PyTypeObject*) PyType_FromSpecWithBases(&methodSpec, tuple.get()); PyFunction_Type.tp_flags = flags; JP_PY_CHECK(); PyModule_AddObject(module, "_JMethod", (PyObject*) PyJPMethod_Type); JP_PY_CHECK(); } JPPyObject PyJPMethod_create(JPMethodDispatch *m, PyObject *instance) { JP_TRACE_IN("PyJPMethod_create"); auto* self = (PyJPMethod*) PyJPMethod_Type->tp_alloc(PyJPMethod_Type, 0); JP_PY_CHECK(); self->m_Method = m; self->m_Instance = instance; self->m_Doc = nullptr; self->m_Annotations = nullptr; self->m_CodeRep = nullptr; Py_XINCREF(self->m_Instance); return JPPyObject::claim((PyObject*) self); JP_TRACE_OUT; /// GCOVR_EXCL_LINE } jpype-1.6.0/native/python/pyjp_module.cpp000066400000000000000000000630411501674766700205360ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_arrayclass.h" #include "jp_primitive_accessor.h" #include "jp_gc.h" #include "jp_stringtype.h" #include "jp_classloader.h" #ifdef WIN32 #include #endif void PyJPModule_installGC(PyObject* module); bool _jp_cpp_exceptions = false; extern void PyJPArray_initType(PyObject* module); extern void PyJPBuffer_initType(PyObject* module); extern void PyJPClass_initType(PyObject* module); extern void PyJPField_initType(PyObject* module); extern void PyJPMethod_initType(PyObject* module); extern void PyJPMonitor_initType(PyObject* module); extern void PyJPProxy_initType(PyObject* module); extern void PyJPObject_initType(PyObject* module); extern void PyJPNumber_initType(PyObject* module); extern void PyJPClassHints_initType(PyObject* module); extern void PyJPPackage_initType(PyObject* module); extern void PyJPChar_initType(PyObject* module); extern void PyJPValue_initType(PyObject* module); static PyObject *PyJPModule_convertBuffer(JPPyBuffer& buffer, PyObject *dtype); // To ensure no leaks (requires C++ linkage) class JPViewWrapper { public: JPViewWrapper() { view = new Py_buffer(); } ~JPViewWrapper() { delete view; } Py_buffer *view; } ; PyObject* _JArray = nullptr; PyObject* _JChar = nullptr; PyObject* _JObject = nullptr; PyObject* _JInterface = nullptr; PyObject* _JException = nullptr; PyObject* _JClassPre = nullptr; PyObject* _JClassPost = nullptr; PyObject* _JClassDoc = nullptr; PyObject* _JMethodDoc = nullptr; PyObject* _JMethodAnnotations = nullptr; PyObject* _JMethodCode = nullptr; PyObject* _JObjectKey = nullptr; PyObject* _JVMNotRunning = nullptr; void PyJPModule_loadResources(PyObject* module) { // Note that if any resource is missing the user will get // the message: // // AttributeError: module '_jpype' has no attribute 'SomeResource' // The above exception was the direct cause of the following exception: // // Traceback (most recent call last): // File ... // RuntimeError: JPype resource is missing try { // Complete the initialization here _JObject = PyObject_GetAttrString(module, "JObject"); JP_PY_CHECK(); Py_INCREF(_JObject); _JInterface = PyObject_GetAttrString(module, "JInterface"); JP_PY_CHECK(); Py_INCREF(_JInterface); _JArray = PyObject_GetAttrString(module, "JArray"); JP_PY_CHECK(); Py_INCREF(_JArray); _JChar = PyObject_GetAttrString(module, "JChar"); JP_PY_CHECK(); Py_INCREF(_JChar); _JException = PyObject_GetAttrString(module, "JException"); JP_PY_CHECK(); Py_INCREF(_JException); _JClassPre = PyObject_GetAttrString(module, "_jclassPre"); JP_PY_CHECK(); Py_INCREF(_JClassPre); _JClassPost = PyObject_GetAttrString(module, "_jclassPost"); JP_PY_CHECK(); Py_INCREF(_JClassPost); JP_PY_CHECK(); _JClassDoc = PyObject_GetAttrString(module, "_jclassDoc"); JP_PY_CHECK(); Py_INCREF(_JClassDoc); _JMethodDoc = PyObject_GetAttrString(module, "getMethodDoc"); Py_INCREF(_JMethodDoc); _JMethodAnnotations = PyObject_GetAttrString(module, "getMethodAnnotations"); JP_PY_CHECK(); Py_INCREF(_JMethodAnnotations); _JMethodCode = PyObject_GetAttrString(module, "getMethodCode"); JP_PY_CHECK(); Py_INCREF(_JMethodCode); _JObjectKey = PyCapsule_New(module, "constructor key", nullptr); } catch (JPypeException&) // GCOVR_EXCL_LINE { // GCOVR_EXCL_START PyJP_SetStringWithCause(PyExc_RuntimeError, "JPype resource is missing"); JP_RAISE_PYTHON(); // GCOVR_EXCL_STOP } } #ifdef __cplusplus extern "C" { #endif // GCOVR_EXCL_START // This is used exclusively during startup void PyJP_SetStringWithCause(PyObject *exception, const char *str) { // See _PyErr_TrySetFromCause PyObject *exc1, *val1, *tb1; PyErr_Fetch(&exc1, &val1, &tb1); PyErr_NormalizeException(&exc1, &val1, &tb1); if (tb1 != nullptr) { PyException_SetTraceback(val1, tb1); Py_DECREF(tb1); } Py_DECREF(exc1); PyErr_SetString(exception, str); PyObject *exc2, *val2, *tb2; PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetCause(val2, val1); PyErr_Restore(exc2, val2, tb2); } // GCOVR_EXCL_STOP PyObject* PyJP_GetAttrDescriptor(PyTypeObject *type, PyObject *attr_name) { JP_PY_TRY("Py_GetAttrDescriptor"); if (type->tp_mro == nullptr) return nullptr; // GCOVR_EXCL_LINE // Grab the mro PyObject *mro = type->tp_mro; // mro should be a tuple Py_ssize_t n = PyTuple_Size(mro); // Search the tuple for the attribute for (Py_ssize_t i = 0; i < n; ++i) { auto *type2 = (PyTypeObject*) PyTuple_GetItem(mro, i); // Skip objects without a functioning dictionary if (type2->tp_dict == NULL) continue; PyObject *res = PyDict_GetItem(type2->tp_dict, attr_name); if (res) { Py_INCREF(res); return res; } } // Last check is id in the parent { PyObject *res = PyDict_GetItem(Py_TYPE(type)->tp_dict, attr_name); if (res) { Py_INCREF(res); return res; } } return nullptr; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } int PyJP_IsSubClassSingle(PyTypeObject* type, PyTypeObject* obj) { if (type == nullptr || obj == nullptr) return 0; // GCOVR_EXCL_LINE PyObject* mro1 = obj->tp_mro; Py_ssize_t n1 = PyTuple_Size(mro1); Py_ssize_t n2 = PyTuple_Size(type->tp_mro); if (n1 < n2) return 0; return PyTuple_GetItem(mro1, n1 - n2) == (PyObject*) type; } int PyJP_IsInstanceSingle(PyObject* obj, PyTypeObject* type) { if (type == nullptr || obj == nullptr) return 0; // GCOVR_EXCL_LINE return PyJP_IsSubClassSingle(type, Py_TYPE(obj)); } #ifndef ANDROID extern JNIEnv *Android_JNI_GetEnv(); static string jarTmpPath; static PyObject* PyJPModule_startup(PyObject* module, PyObject* pyargs) { JP_PY_TRY("PyJPModule_startup"); PyObject* vmOpt; PyObject* vmPath; char ignoreUnrecognized = true; char convertStrings = false; char interrupt = false; PyObject* tmp; if (!PyArg_ParseTuple(pyargs, "OO!bbbO", &vmPath, &PyTuple_Type, &vmOpt, &ignoreUnrecognized, &convertStrings, &interrupt, &tmp)) return nullptr; if (tmp != Py_None) { if (!(JPPyString::check(tmp))) { PyErr_SetString(PyExc_TypeError, "Java jar path must be a string"); return nullptr; } jarTmpPath = JPPyString::asStringUTF8(tmp); } if (!(JPPyString::check(vmPath))) { PyErr_SetString(PyExc_TypeError, "Java JVM path must be a string"); return nullptr; } string cVmPath = JPPyString::asStringUTF8(vmPath); JP_TRACE("vmpath", cVmPath); StringVector args; JPPySequence seq = JPPySequence::use(vmOpt); for (int i = 0; i < seq.size(); i++) { JPPyObject obj(seq[i]); if (JPPyString::check(obj.get())) { // TODO support unicode string v = JPPyString::asStringUTF8(obj.get()); JP_TRACE("arg", v); args.emplace_back(v); } else { PyErr_SetString(PyExc_TypeError, "VM Arguments must be strings"); return nullptr; } } // This section was moved down to make it easier to cover error cases if (JPContext_global->isRunning()) { PyErr_SetString(PyExc_OSError, "JVM is already started"); return nullptr; } // install the gc hook PyJPModule_installGC(module); PyJPModule_loadResources(module); JPContext_global->startJVM(cVmPath, args, ignoreUnrecognized != 0, convertStrings != 0, interrupt != 0); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyObject* PyJPModule_shutdown(PyObject* obj, PyObject* pyargs, PyObject* kwargs) { JP_PY_TRY("PyJPModule_shutdown"); char destroyJVM = true; char freeJVM = true; if (!PyArg_ParseTuple(pyargs, "bb", &destroyJVM, &freeJVM)) return nullptr; JPContext_global->shutdownJVM(destroyJVM, freeJVM); #ifdef WIN32 // Thus far this doesn't work on WINDOWS. The issue is a bug in the JVM // is holding the file open and there is no apparent method to close it // so that this can succeed if (jarTmpPath != "") remove(jarTmpPath.c_str()); #else if (jarTmpPath != "") unlink(jarTmpPath.c_str()); #endif Py_RETURN_NONE; JP_PY_CATCH(nullptr); } #endif static PyObject* PyJPModule_isStarted(PyObject* obj) { return PyBool_FromLong(JPContext_global->isRunning()); } #ifndef ANDROID static PyObject* PyJPModule_attachThread(PyObject* obj) { JP_PY_TRY("PyJPModule_attachThread"); PyJPModule_getContext()->attachCurrentThread(); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyObject* PyJPModule_attachThreadAsDaemon(PyObject* obj) { JP_PY_TRY("PyJPModule_attachThreadAsDaemon"); PyJPModule_getContext()->attachCurrentThreadAsDaemon(); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyObject* PyJPModule_detachThread(PyObject* obj) { JP_PY_TRY("PyJPModule_detachThread"); if (JPContext_global->isRunning()) JPContext_global->detachCurrentThread(); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } #endif static PyObject* PyJPModule_isThreadAttached(PyObject* obj) { JP_PY_TRY("PyJPModule_isThreadAttached"); if (!JPContext_global->isRunning()) return PyBool_FromLong(0); // GCOVR_EXCL_LINE return PyBool_FromLong(JPContext_global->isThreadAttached()); JP_PY_CATCH(nullptr); } // Cleanup hook for Py_buffer static void releaseView(void* view) { if (view != nullptr) { PyBuffer_Release((Py_buffer*) view); delete (Py_buffer*) view; } } static PyObject* PyJPModule_convertToDirectByteBuffer(PyObject* self, PyObject* args) { JP_PY_TRY("PyJPModule_convertToDirectByteBuffer"); JPJavaFrame frame = JPJavaFrame::outer(); PyObject *src; int ro; if (!PyArg_ParseTuple(args, "Op", &src, &ro)) return nullptr; if (PyObject_CheckBuffer(src)) { JPViewWrapper vw; if (PyObject_GetBuffer(src, vw.view, ro ? 0 : PyBUF_WRITABLE) == -1) return nullptr; // Create a byte buffer jvalue v; v.l = frame.NewDirectByteBuffer(vw.view->buf, vw.view->len); if (vw.view->readonly) v.l = frame.asReadOnlyBuffer(v.l); // Bind lifespan of the view to the java object. frame.registerRef(v.l, vw.view, &releaseView); vw.view = nullptr; JPClass *type = frame.findClassForObject(v.l); return type->convertToPythonObject(frame, v, false).keep(); } PyErr_SetString(PyExc_TypeError, "convertToDirectByteBuffer requires buffer support"); JP_PY_CATCH(nullptr); } static PyObject* PyJPModule_enableStacktraces(PyObject* self, PyObject* src) { _jp_cpp_exceptions = PyObject_IsTrue(src); Py_RETURN_TRUE; } PyObject *PyJPModule_newArrayType(PyObject *module, PyObject *args) { JP_PY_TRY("PyJPModule_newArrayType"); JPJavaFrame frame = JPJavaFrame::outer(); PyObject *type, *dims; if (!PyArg_ParseTuple(args, "OO", &type, &dims)) return nullptr; if (!PyIndex_Check(dims)) { PyErr_SetString(PyExc_TypeError, "dims must be an integer"); return nullptr; } long d = PyLong_AsLong(dims); JPClass* cls = PyJPClass_getJPClass(type); if (cls == nullptr) { PyErr_SetString(PyExc_TypeError, "Java class required"); return nullptr; } JPClass* arraycls = cls->newArrayType(frame, d); return PyJPClass_create(frame, arraycls).keep(); JP_PY_CATCH(nullptr); } PyObject *PyJPModule_getClass(PyObject* module, PyObject *obj) { JP_PY_TRY("PyJPModule_getClass"); JPJavaFrame frame = JPJavaFrame::outer(); JPContext *context = frame.getContext(); JPClass* cls; if (JPPyString::check(obj)) { // String From Python cls = frame.findClassByName(JPPyString::asStringUTF8(obj)); if (cls == nullptr) { PyErr_SetString(PyExc_ValueError, "Unable to find Java class"); return nullptr; } } else { // From an existing java.lang.Class object JPValue *value = PyJPValue_getJavaSlot(obj); if (value == nullptr || value->getClass() != context->_java_lang_Class) { PyErr_Format(PyExc_TypeError, "JClass requires str or java.lang.Class instance, not '%s'", Py_TYPE(obj)->tp_name); return nullptr; } cls = frame.findClass((jclass) value->getValue().l); if (cls == nullptr) { PyErr_SetString(PyExc_ValueError, "Unable to find class"); return nullptr; } } return PyJPClass_create(frame, cls).keep(); JP_PY_CATCH(nullptr); } PyObject *PyJPModule_hasClass(PyObject* module, PyObject *obj) { JP_PY_TRY("PyJPModule_hasClass"); if (!JPContext_global->isRunning()) Py_RETURN_FALSE; // GCOVR_EXCL_LINE JPJavaFrame frame = JPJavaFrame::outer(); JPClass* cls; if (JPPyString::check(obj)) { // String From Python cls = frame.findClassByName(JPPyString::asStringUTF8(obj)); if (cls == nullptr) { PyErr_SetString(PyExc_ValueError, "Unable to find Java class"); return nullptr; } } else { PyErr_Format(PyExc_TypeError, "str is required, not '%s'", Py_TYPE(obj)->tp_name); return nullptr; } auto *host = (PyObject*) cls->getHost(); return PyBool_FromLong(host != nullptr); JP_PY_CATCH(nullptr); } static PyObject *PyJPModule_arrayFromBuffer(PyObject *module, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPModule_arrayFromBuffer"); PyObject *source = nullptr; PyObject *dtype = nullptr; if (!PyArg_ParseTuple(args, "OO", &source, &dtype)) return nullptr; if (!PyObject_CheckBuffer(source)) { PyErr_Format(PyExc_TypeError, "'%s' does not support buffers", Py_TYPE(source)->tp_name); return nullptr; } // NUMPy does a series of probes looking for the best supported format, // we will do the same. { JPPyBuffer buffer(source, PyBUF_FULL_RO); if (buffer.valid()) return PyJPModule_convertBuffer(buffer, dtype); } { JPPyBuffer buffer(source, PyBUF_RECORDS_RO); if (buffer.valid()) return PyJPModule_convertBuffer(buffer, dtype); } { JPPyBuffer buffer(source, PyBUF_ND | PyBUF_FORMAT); if (buffer.valid()) return PyJPModule_convertBuffer(buffer, dtype); } PyErr_Format(PyExc_TypeError, "buffer protocol for '%s' not supported", Py_TYPE(source)->tp_name); return nullptr; JP_PY_CATCH(nullptr); } PyObject *PyJPModule_collect(PyObject* module, PyObject *obj) { JPContext* context = JPContext_global; if (!context->isRunning()) Py_RETURN_NONE; PyObject *a1 = PyTuple_GetItem(obj, 0); if (!PyUnicode_Check(a1)) { PyErr_SetString(PyExc_TypeError, "Bad callback argument"); return nullptr; } if (PyUnicode_ReadChar(a1, 2) == 'a') { context->m_GC->onStart(); } else { context->m_GC->onEnd(); } Py_RETURN_NONE; } // GCOVR_EXCL_START PyObject *PyJPModule_gcStats(PyObject* module, PyObject *obj) { JPContext *context = PyJPModule_getContext(); JPGCStats stats; context->m_GC->getStats(stats); PyObject *out = PyDict_New(); PyObject *res; PyDict_SetItemString(out, "current", res = PyLong_FromSsize_t((Py_ssize_t)(stats.current_rss))); Py_DECREF(res); PyDict_SetItemString(out, "java", res = PyLong_FromSsize_t((Py_ssize_t)(stats.java_rss))); Py_DECREF(res); PyDict_SetItemString(out, "python", res = PyLong_FromSsize_t((Py_ssize_t)(stats.python_rss))); Py_DECREF(res); PyDict_SetItemString(out, "max", res = PyLong_FromSsize_t((Py_ssize_t)(stats.max_rss))); Py_DECREF(res); PyDict_SetItemString(out, "min", res = PyLong_FromSsize_t((Py_ssize_t)(stats.min_rss))); Py_DECREF(res); PyDict_SetItemString(out, "triggered", res = PyLong_FromSsize_t((Py_ssize_t)(stats.python_triggered))); Py_DECREF(res); return out; } // GCOVR_EXCL_STOP static PyObject* PyJPModule_isPackage(PyObject *module, PyObject *pkg) { JP_PY_TRY("PyJPModule_isPackage"); if (!PyUnicode_Check(pkg)) { PyErr_Format(PyExc_TypeError, "isPackage required unicode"); return nullptr; } JPJavaFrame frame = JPJavaFrame::outer(); return PyBool_FromLong(frame.isPackage(JPPyString::asStringUTF8(pkg))); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } #if 1 // GCOVR_EXCL_START // This code was used in testing the Java slot memory layout. It serves no purpose outside of debugging that issue. PyObject* examine(PyObject *module, PyObject *other) { JP_PY_TRY("examine"); int ret = 0; PyTypeObject *type; if (PyType_Check(other)) type = (PyTypeObject*) other; else type = Py_TYPE(other); printf("======\n"); int offset = 0; if (!PyType_Check(other)) { offset = PyJPValue_getJavaSlotOffset(other); printf(" Object:\n"); printf(" size: %d\n", (int) Py_SIZE(other)); printf(" dictoffset: %d\n", (int) ((long long) _PyObject_GetDictPtr(other)-(long long) other)); printf(" javaoffset: %d\n", offset); } printf(" Type: %p\n", type); printf(" name: %s\n", type->tp_name); printf(" typename: %s\n", Py_TYPE(type)->tp_name); printf(" gc: %d\n", (type->tp_flags & Py_TPFLAGS_HAVE_GC) == Py_TPFLAGS_HAVE_GC); printf(" basicsize: %d\n", (int) type->tp_basicsize); printf(" itemsize: %d\n", (int) type->tp_itemsize); printf(" dictoffset: %d\n", (int) type->tp_dictoffset); printf(" weaklistoffset: %d\n", (int) type->tp_weaklistoffset); printf(" hasJavaSlot: %d\n", PyJPValue_hasJavaSlot(type)); printf(" getattro: %p\n", type->tp_getattro); printf(" setattro: %p\n", type->tp_setattro); printf(" getattr: %p\n", type->tp_getattr); printf(" setattr: %p\n", type->tp_setattr); printf(" alloc: %p\n", type->tp_alloc); printf(" free: %p\n", type->tp_free); printf(" finalize: %p\n", type->tp_finalize); long v = _PyObject_VAR_SIZE(type, 1)+(PyJPValue_hasJavaSlot(type)?sizeof (JPValue):0); printf(" size?: %ld\n",v); printf("======\n"); return PyBool_FromLong(ret); JP_PY_CATCH(nullptr); } // GCOVR_EXCL_STOP #endif // GCOVR_EXCL_START int _PyJPModule_trace = 0; static PyObject* PyJPModule_trace(PyObject *module, PyObject *args) { bool old = _PyJPModule_trace; _PyJPModule_trace = PyLong_AsLong(args); return PyLong_FromLong(old); } // GCOVR_EXCL_STOP #ifdef JP_INSTRUMENTATION uint32_t _PyJPModule_fault_code = -1; static PyObject* PyJPModule_fault(PyObject *module, PyObject *args) { if (args == Py_None) { _PyJPModule_fault_code = 0; Py_RETURN_NONE; } string code = JPPyString::asStringUTF8(args); uint32_t u = 0; for (size_t i = 0; i < code.size(); ++i) u = u * 0x1a481023 + code[i]; _PyJPModule_fault_code = u; return PyLong_FromLong(_PyJPModule_fault_code); } #endif #ifdef ANDROID static PyObject *PyJPModule_bootstrap(PyObject *module) { // After all the internals are created we can connect the API with the internal module JNIEnv * env = Android_JNI_GetEnv(); JPContext_global->attachJVM(env); PyJPModule_installGC(module); PyJPModule_loadResources(module); Py_RETURN_NONE; } #endif static PyMethodDef moduleMethods[] = { // Startup and initialization {"isStarted", (PyCFunction) PyJPModule_isStarted, METH_NOARGS, ""}, #ifdef ANDROID {"bootstrap", (PyCFunction) PyJPModule_bootstrap, METH_NOARGS, ""}, #else {"startup", (PyCFunction) PyJPModule_startup, METH_VARARGS, ""}, // {"attach", (PyCFunction) (&PyJPModule_attach), METH_VARARGS, ""}, {"shutdown", (PyCFunction) PyJPModule_shutdown, METH_VARARGS, ""}, #endif {"_getClass", (PyCFunction) PyJPModule_getClass, METH_O, ""}, {"_hasClass", (PyCFunction) PyJPModule_hasClass, METH_O, ""}, {"_newArrayType", (PyCFunction) PyJPModule_newArrayType, METH_VARARGS, ""}, {"_collect", (PyCFunction) PyJPModule_collect, METH_VARARGS, ""}, {"gcStats", (PyCFunction) PyJPModule_gcStats, METH_NOARGS, ""}, // Threading {"isThreadAttachedToJVM", (PyCFunction) PyJPModule_isThreadAttached, METH_NOARGS, ""}, #ifndef ANDROID {"attachThreadToJVM", (PyCFunction) PyJPModule_attachThread, METH_NOARGS, ""}, {"detachThreadFromJVM", (PyCFunction) PyJPModule_detachThread, METH_NOARGS, ""}, {"attachThreadAsDaemon", (PyCFunction) PyJPModule_attachThreadAsDaemon, METH_NOARGS, ""}, #endif //{"dumpJVMStats", (PyCFunction) (&PyJPModule_dumpJVMStats), METH_NOARGS, ""}, {"convertToDirectBuffer", (PyCFunction) PyJPModule_convertToDirectByteBuffer, METH_VARARGS, ""}, {"arrayFromBuffer", (PyCFunction) PyJPModule_arrayFromBuffer, METH_VARARGS, ""}, {"enableStacktraces", (PyCFunction) PyJPModule_enableStacktraces, METH_O, ""}, {"isPackage", (PyCFunction) PyJPModule_isPackage, METH_O, ""}, {"trace", (PyCFunction) PyJPModule_trace, METH_O, ""}, #ifdef JP_INSTRUMENTATION {"fault", (PyCFunction) PyJPModule_fault, METH_O, ""}, #endif {"examine", (PyCFunction) examine, METH_O, ""}, // sentinel {nullptr} }; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_jpype", "jpype module", -1, moduleMethods, }; PyObject *PyJPModule = nullptr; JPContext* JPContext_global = nullptr; PyMODINIT_FUNC PyInit__jpype() { JP_PY_TRY("PyInit__jpype"); JPContext_global = new JPContext(); // Initialize the module (depends on python version) PyObject* module = PyModule_Create(&moduledef); // PyJPModule = module; Py_INCREF(module); PyJPModule = module; #ifdef Py_GIL_DISABLED PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED); #endif PyModule_AddStringConstant(module, "__version__", "1.6.0"); // Our module will be used for PyFrame object and it is a requirement that // we have a builtins in our dictionary. PyObject *builtins = PyEval_GetBuiltins(); Py_INCREF(builtins); PyModule_AddObject(module, "__builtins__", builtins); PyJPClassMagic = PyDict_New(); // Initialize each of the python extension types PyJPValue_initType(module); PyJPClass_initType(module); PyJPObject_initType(module); PyJPArray_initType(module); PyJPBuffer_initType(module); PyJPField_initType(module); PyJPMethod_initType(module); PyJPNumber_initType(module); PyJPMonitor_initType(module); PyJPProxy_initType(module); PyJPClassHints_initType(module); PyJPPackage_initType(module); PyJPChar_initType(module); _PyJPModule_trace = true; return module; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } #ifdef __cplusplus } #endif void PyJPModule_rethrow(const JPStackInfo& info) { JP_TRACE_IN("PyJPModule_rethrow"); JP_TRACE(info.getFile(), info.getLine()); try { throw; } catch (JPypeException& ex) { ex.from(info); // this likely wont be necessary, but for now we will add the entry point. ex.toPython(); return; } catch (std::exception &ex) { PyErr_Format(PyExc_RuntimeError, "Unhandled C++ exception occurred: %s", ex.what()); return; } catch (...) { PyErr_Format(PyExc_RuntimeError, "Unhandled C++ exception occurred"); return; } JP_TRACE_OUT; // GCOVR_EXCL_LINE } static PyObject *PyJPModule_convertBuffer(JPPyBuffer& buffer, PyObject *dtype) { JPContext *context = PyJPModule_getContext(); JPJavaFrame frame = JPJavaFrame::outer(); Py_buffer& view = buffer.getView(); // Okay two possibilities here. We have a valid dtype specified, // or we need to figure it out from the buffer. JPClass *cls = nullptr; if (view.suboffsets != nullptr && view.suboffsets[view.ndim - 1] > 0) { PyErr_Format(PyExc_TypeError, "last dimension is not contiguous"); return nullptr; } // First lets find out what we are unpacking Py_ssize_t itemsize = view.itemsize; char *format = view.format; if (format == nullptr) format = "B"; // Standard size for 'l' is 4 in docs, but numpy uses format 'l' for long long if (itemsize == 8 && format[0] == 'l') format = "q"; if (itemsize == 8 && format[0] == 'L') format = "Q"; if (dtype != nullptr && dtype != Py_None ) { cls = PyJPClass_getJPClass(dtype); if (cls == nullptr || !cls->isPrimitive()) { PyErr_Format(PyExc_TypeError, "'%s' is not a Java primitive type", Py_TYPE(dtype)->tp_name); return nullptr; } } else { switch (format[0]) { case '?': cls = context->_boolean; case 'c': break; case 'b': cls = context->_byte; case 'B': break; case 'h': cls = context->_short; break; case 'H': break; case 'i': case 'l': cls = context->_int; break; case 'I': case 'L': break; case 'q': cls = context->_long; break; case 'Q': break; case 'f': cls = context->_float; break; case 'd': cls = context->_double; break; case 'n': case 'N': case 'P': default: break; } if (cls == nullptr) { PyErr_Format(PyExc_TypeError, "'%s' type code not supported without dtype specified", format); return nullptr; } } // Now we have a valid format code, so next lets get a converter for // the type. auto *pcls = dynamic_cast( cls); // Convert the shape Py_ssize_t subs = 1; Py_ssize_t base = 1; auto jdims = (jintArray) context->_int->newArrayOf(frame, view.ndim); if (view.shape != nullptr) { JPPrimitiveArrayAccessor accessor(frame, jdims, &JPJavaFrame::GetIntArrayElements, &JPJavaFrame::ReleaseIntArrayElements); jint *a = accessor.get(); for (int i = 0; i < view.ndim; ++i) { a[i] = view.shape[i]; } accessor.commit(); for (int i = 0; i < view.ndim - 1; ++i) { subs *= view.shape[i]; } base = view.shape[view.ndim - 1]; } else { if (view.ndim > 1) { PyErr_Format(PyExc_TypeError, "buffer dims inconsistent"); return nullptr; } base = view.len / view.itemsize; } return pcls->newMultiArray(frame, buffer, subs, base, (jobject) jdims); } #ifdef JP_INSTRUMENTATION int PyJPModuleFault_check(uint32_t code) { return (code == _PyJPModule_fault_code); } void PyJPModuleFault_throw(uint32_t code) { if (code == _PyJPModule_fault_code) { _PyJPModule_fault_code = -1; JP_RAISE(PyExc_SystemError, "fault"); } } #endif void PyJPModule_installGC(PyObject* module) { // Get the Python garbage collector JPPyObject gc = JPPyObject::call(PyImport_ImportModule("gc")); // Find the callbacks JPPyObject callbacks = JPPyObject::call(PyObject_GetAttrString(gc.get(), "callbacks")); // Hook up our callback JPPyObject collect = JPPyObject::call(PyObject_GetAttrString(module, "_collect")); PyList_Append(callbacks.get(), collect.get()); JP_PY_CHECK(); } jpype-1.6.0/native/python/pyjp_monitor.cpp000066400000000000000000000067231501674766700207440ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_monitor.h" #include "jp_stringtype.h" #ifdef __cplusplus extern "C" { #endif struct PyJPMonitor { PyObject_HEAD JPMonitor *m_Monitor; } ; static int PyJPMonitor_init(PyJPMonitor *self, PyObject *args) { JP_PY_TRY("PyJPMonitor_init"); self->m_Monitor = nullptr; JPContext *context = PyJPModule_getContext(); JPJavaFrame frame = JPJavaFrame::outer(); PyObject* value; if (!PyArg_ParseTuple(args, "O", &value)) return -1; JPValue *v1 = PyJPValue_getJavaSlot(value); if (v1 == nullptr) { PyErr_SetString(PyExc_TypeError, "Java object is required."); return -1; } if (v1->getClass() == context->_java_lang_String) { PyErr_SetString(PyExc_TypeError, "Java strings cannot be used to synchronize."); return -1; } if ((v1->getClass())->isPrimitive()) { PyErr_SetString(PyExc_TypeError, "Java primitives cannot be used to synchronize."); return -1; } if (v1->getValue().l == nullptr) { PyErr_SetString(PyExc_TypeError, "Java null cannot be used to synchronize."); return -1; } self->m_Monitor = new JPMonitor(v1->getValue().l); return 0; JP_PY_CATCH(-1); } static void PyJPMonitor_dealloc(PyJPMonitor *self) { JP_PY_TRY("PyJPMonitor_dealloc"); delete self->m_Monitor; Py_TYPE(self)->tp_free(self); JP_PY_CATCH(); // GCOVR_EXCL_LINE } static PyObject *PyJPMonitor_str(PyJPMonitor *self) { JP_PY_TRY("PyJPMonitor_str"); return PyUnicode_FromFormat(""); JP_PY_CATCH(nullptr); } static PyObject *PyJPMonitor_enter(PyJPMonitor *self, PyObject *args) { JP_PY_TRY("PyJPMonitor_enter"); JPJavaFrame frame = JPJavaFrame::outer(); self->m_Monitor->enter(); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyObject *PyJPMonitor_exit(PyJPMonitor *self, PyObject *args) { JP_PY_TRY("PyJPMonitor_exit"); JPJavaFrame frame = JPJavaFrame::outer(); self->m_Monitor->exit(); Py_RETURN_NONE; JP_PY_CATCH(nullptr); } static PyMethodDef monitorMethods[] = { {"__enter__", (PyCFunction) (&PyJPMonitor_enter), METH_NOARGS, ""}, {"__exit__", (PyCFunction) (&PyJPMonitor_exit), METH_VARARGS, ""}, {nullptr}, }; static PyType_Slot monitorSlots[] = { { Py_tp_init, (void*) PyJPMonitor_init}, { Py_tp_dealloc, (void*) PyJPMonitor_dealloc}, { Py_tp_str, (void*) PyJPMonitor_str}, { Py_tp_methods, (void*) &monitorMethods}, {0} }; PyType_Spec PyJPMonitorSpec = { "_jpype._JMonitor", sizeof (PyJPMonitor), 0, Py_TPFLAGS_DEFAULT, monitorSlots }; PyTypeObject* PyJPMonitor_Type = nullptr; #ifdef __cplusplus } #endif void PyJPMonitor_initType(PyObject* module) { PyJPMonitor_Type = (PyTypeObject*) PyType_FromSpec(&PyJPMonitorSpec); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JMonitor", (PyObject*) PyJPMonitor_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE } jpype-1.6.0/native/python/pyjp_number.cpp000066400000000000000000000317451501674766700205470ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_boxedtype.h" static bool isNull(PyObject *self) { JPValue *javaSlot = PyJPValue_getJavaSlot(self); if (javaSlot != nullptr && !javaSlot->getClass()->isPrimitive() && javaSlot->getValue().l == nullptr) return true; return false; } #ifdef __cplusplus extern "C" { #endif static PyObject *PyJPNumber_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPNumber_new", type); auto *cls = (JPClass*) PyJPClass_getJPClass((PyObject*) type); if (cls == nullptr) JP_RAISE(PyExc_TypeError, "Class type incorrect"); JPJavaFrame frame = JPJavaFrame::outer(); jvalue val; // One argument tries Java conversion first if (PyTuple_Size(args) == 1) { PyObject *arg = PyTuple_GetItem(args, 0); JPMatch match(&frame, arg); cls->findJavaConversion(match); if (match.type >= JPMatch::_implicit) { // Disable OverrangeError match.type = JPMatch::_exact; val = match.convert(); PyObject *obj = cls->convertToPythonObject(frame, val, true).keep(); return obj; } } if (PyObject_IsSubclass((PyObject*) type, (PyObject*) & PyLong_Type)) { JPPyObject self = JPPyObject::call(PyLong_Type.tp_new(&PyLong_Type, args, kwargs)); JPMatch match(&frame, self.get()); cls->findJavaConversion(match); match.type = JPMatch::_exact; val = match.convert(); return cls->convertToPythonObject(frame, val, true).keep(); } else if (PyObject_IsSubclass((PyObject*) type, (PyObject*) & PyFloat_Type)) { JPPyObject self = JPPyObject::call(PyFloat_Type.tp_new(&PyFloat_Type, args, kwargs)); JPMatch match(&frame, self.get()); cls->findJavaConversion(match); match.type = JPMatch::_exact; val = match.convert(); return cls->convertToPythonObject(frame, val, true).keep(); } else { PyErr_Format(PyExc_TypeError, "Type '%s' is not a number class", type->tp_name); return nullptr; } JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberLong_int(PyObject *self) { JP_PY_TRY("PyJPNumberLong_int"); JPJavaFrame frame = JPJavaFrame::outer(); if (!isNull(self)) return PyLong_Type.tp_as_number->nb_int(self); PyErr_SetString(PyExc_TypeError, "cast of null pointer would return non-int"); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberLong_float(PyObject *self) { JP_PY_TRY("PyJPNumberLong_float"); JPJavaFrame frame = JPJavaFrame::outer(); if (!isNull(self)) return PyLong_Type.tp_as_number->nb_float(self); PyErr_SetString(PyExc_TypeError, "cast of null pointer would return non-float"); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberFloat_int(PyObject *self) { JP_PY_TRY("PyJPNumberFloat_int"); JPJavaFrame frame = JPJavaFrame::outer(); if (!isNull(self)) return PyFloat_Type.tp_as_number->nb_int(self); PyErr_SetString(PyExc_TypeError, "cast of null pointer would return non-int"); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberFloat_float(PyObject *self) { JP_PY_TRY("PyJPNumberFloat_float"); JPJavaFrame frame = JPJavaFrame::outer(); if (!isNull(self)) return PyFloat_Type.tp_as_number->nb_float(self); PyErr_SetString(PyExc_TypeError, "cast of null pointer would return non-float"); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberLong_str(PyObject *self) { JP_PY_TRY("PyJPNumberLong_str"); JPJavaFrame frame = JPJavaFrame::outer(); if (isNull(self)) return Py_TYPE(Py_None)->tp_str(Py_None); return PyLong_Type.tp_str(self); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberFloat_str(PyObject *self) { JP_PY_TRY("PyJPNumberFloat_str"); JPJavaFrame frame = JPJavaFrame::outer(); if (isNull(self)) return Py_TYPE(Py_None)->tp_str(Py_None); return PyFloat_Type.tp_str(self); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberLong_repr(PyObject *self) { JP_PY_TRY("PyJPNumberLong_repr"); JPJavaFrame frame = JPJavaFrame::outer(); if (isNull(self)) return Py_TYPE(Py_None)->tp_str(Py_None); return PyLong_Type.tp_repr(self); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberFloat_repr(PyObject *self) { JP_PY_TRY("PyJPNumberFloat_repr"); JPJavaFrame frame = JPJavaFrame::outer(); if (isNull(self)) return Py_TYPE(Py_None)->tp_str(Py_None); return PyFloat_Type.tp_repr(self); JP_PY_CATCH(nullptr); } static const char* op_names[] = { "<", "<=", "==", "!=", ">", ">=" }; static PyObject *PyJPNumberLong_compare(PyObject *self, PyObject *other, int op) { JP_PY_TRY("PyJPNumberLong_compare"); JPJavaFrame frame = JPJavaFrame::outer(); if (isNull(self)) { if (op == Py_EQ) return PyBool_FromLong(other == Py_None); if (op == Py_NE) return PyBool_FromLong(other != Py_None); PyErr_Format(PyExc_TypeError, "'%s' not supported with null pointer", op_names[op]); JP_RAISE_PYTHON(); } if (!PyNumber_Check(other)) { PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; } return PyLong_Type.tp_richcompare(self, other, op); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumberFloat_compare(PyObject *self, PyObject *other, int op) { JP_PY_TRY("PyJPNumberFloat_compare"); JPJavaFrame frame = JPJavaFrame::outer(); if (isNull(self)) { if (op == Py_EQ) return PyBool_FromLong(other == Py_None); if (op == Py_NE) return PyBool_FromLong(other != Py_None); PyErr_Format(PyExc_TypeError, "'%s' not supported with null pointer", op_names[op]); JP_RAISE_PYTHON(); } if (!PyNumber_Check(other)) // || Py_TYPE(other) == (PyTypeObject*) _JChar) { PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; } return PyFloat_Type.tp_richcompare(self, other, op); JP_PY_CATCH(nullptr); } static Py_hash_t PyJPNumberLong_hash(PyObject *self) { JP_PY_TRY("PyJPNumberLong_hash"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue *javaSlot = PyJPValue_getJavaSlot(self); if (javaSlot == nullptr) return Py_TYPE(Py_None)->tp_hash(Py_None); if (!javaSlot->getClass()->isPrimitive()) { jobject o = javaSlot->getJavaObject(); if (o == nullptr) return Py_TYPE(Py_None)->tp_hash(Py_None); } return PyLong_Type.tp_hash(self); JP_PY_CATCH(0); } static Py_hash_t PyJPNumberFloat_hash(PyObject *self) { JP_PY_TRY("PyJPNumberFloat_hash"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue *javaSlot = PyJPValue_getJavaSlot(self); if (javaSlot == nullptr) return Py_TYPE(Py_None)->tp_hash(Py_None); if (!javaSlot->getClass()->isPrimitive()) { jobject o = javaSlot->getJavaObject(); if (o == nullptr) return Py_TYPE(Py_None)->tp_hash(Py_None); } return PyFloat_Type.tp_hash(self); JP_PY_CATCH(0); } static PyObject *PyJPBoolean_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPBoolean_new", type); JPPyObject self; if (PyTuple_Size(args) != 1) { PyErr_SetString(PyExc_TypeError, "Requires one argument"); return nullptr; } int i = PyObject_IsTrue(PyTuple_GetItem(args, 0)); JPPyObject args2 = JPPyTuple_Pack(PyLong_FromLong(i)); self = JPPyObject::call(PyLong_Type.tp_new(type, args2.get(), kwargs)); JPClass *cls = PyJPClass_getJPClass((PyObject*) type); if (cls == nullptr) { PyErr_SetString(PyExc_TypeError, "Class type incorrect"); return nullptr; } JPJavaFrame frame = JPJavaFrame::outer(); JPMatch match(&frame, self.get()); cls->findJavaConversion(match); jvalue val = match.convert(); PyJPValue_assignJavaSlot(frame, self.get(), JPValue(cls, val)); JP_TRACE("new", self.get()); return self.keep(); JP_PY_CATCH(nullptr); } static PyObject* PyJPBoolean_str(PyObject* self) { JP_PY_TRY("PyJPBoolean_str", self); if (isNull(self)) return Py_TYPE(Py_None)->tp_str(Py_None); if (PyLong_AsLong(self) == 0) return Py_TYPE(Py_False)->tp_str(Py_False); return Py_TYPE(Py_True)->tp_str(Py_True); JP_PY_CATCH(nullptr); } static PyObject *PyJPNumber_initSubclass(PyObject *cls, PyObject* args, PyObject *kwargs) { Py_RETURN_NONE; } static PyMethodDef numberMethods[] = { {"__init_subclass__", (PyCFunction) PyJPNumber_initSubclass, METH_CLASS | METH_VARARGS | METH_KEYWORDS, ""}, {0} }; static PyType_Slot numberLongSlots[] = { {Py_tp_new, (void*) &PyJPNumber_new}, {Py_tp_getattro, (void*) &PyJPValue_getattro}, {Py_tp_setattro, (void*) &PyJPValue_setattro}, {Py_nb_int, (void*) &PyJPNumberLong_int}, {Py_nb_float, (void*) &PyJPNumberLong_float}, {Py_tp_str, (void*) &PyJPNumberLong_str}, {Py_tp_repr, (void*) &PyJPNumberLong_repr}, {Py_tp_hash, (void*) &PyJPNumberLong_hash}, {Py_tp_richcompare, (void*) &PyJPNumberLong_compare}, {Py_tp_methods, (void*) numberMethods}, {0} }; PyTypeObject *PyJPNumberLong_Type = nullptr; PyType_Spec numberLongSpec = { "_jpype._JNumberLong", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, numberLongSlots }; static PyType_Slot numberFloatSlots[] = { {Py_tp_new, (void*) &PyJPNumber_new}, {Py_tp_getattro, (void*) &PyJPValue_getattro}, {Py_tp_setattro, (void*) &PyJPValue_setattro}, {Py_nb_int, (void*) &PyJPNumberFloat_int}, {Py_nb_float, (void*) &PyJPNumberFloat_float}, {Py_tp_str, (void*) &PyJPNumberFloat_str}, {Py_tp_repr, (void*) &PyJPNumberFloat_repr}, {Py_tp_hash, (void*) &PyJPNumberFloat_hash}, {Py_tp_richcompare, (void*) &PyJPNumberFloat_compare}, {Py_tp_methods, (void*) numberMethods}, {0} }; PyTypeObject *PyJPNumberFloat_Type = nullptr; PyType_Spec numberFloatSpec = { "_jpype._JNumberFloat", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, numberFloatSlots }; static PyType_Slot numberBooleanSlots[] = { {Py_tp_new, (void*) PyJPBoolean_new}, {Py_tp_getattro, (void*) PyJPValue_getattro}, {Py_tp_setattro, (void*) PyJPValue_setattro}, {Py_tp_str, (void*) PyJPBoolean_str}, {Py_tp_repr, (void*) PyJPBoolean_str}, {Py_nb_int, (void*) PyJPNumberLong_int}, {Py_nb_float, (void*) PyJPNumberLong_float}, {Py_tp_hash, (void*) PyJPNumberLong_hash}, {Py_tp_richcompare, (void*) PyJPNumberLong_compare}, {Py_tp_methods, (void*) numberMethods}, {0} }; PyTypeObject *PyJPNumberBool_Type = nullptr; PyType_Spec numberBooleanSpec = { "_jpype._JBoolean", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, numberBooleanSlots }; #ifdef __cplusplus } #endif void PyJPNumber_initType(PyObject* module) { JPPyObject bases = JPPyTuple_Pack(&PyLong_Type, PyJPObject_Type); PyJPNumberLong_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&numberLongSpec, bases.get()); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JNumberLong", (PyObject*) PyJPNumberLong_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE bases = JPPyTuple_Pack(&PyFloat_Type, PyJPObject_Type); PyJPNumberFloat_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&numberFloatSpec, bases.get()); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JNumberFloat", (PyObject*) PyJPNumberFloat_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE bases = JPPyTuple_Pack(&PyLong_Type, PyJPObject_Type); PyJPNumberBool_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&numberBooleanSpec, bases.get()); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JBoolean", (PyObject*) PyJPNumberBool_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE } JPPyObject PyJPNumber_create(JPJavaFrame &frame, JPPyObject& wrapper, const JPValue& value) { JPContext *context = PyJPModule_getContext(); // Bools are not numbers in Java if (value.getClass() == context->_java_lang_Boolean) { jlong l = 0; if (value.getValue().l != nullptr) l = frame.CallBooleanMethodA(value.getJavaObject(), context->_java_lang_Boolean->m_BooleanValueID, nullptr); JPPyObject args = JPPyTuple_Pack(PyLong_FromLongLong(l)); return JPPyObject::call(PyLong_Type.tp_new((PyTypeObject*) wrapper.get(), args.get(), nullptr)); } if (PyObject_IsSubclass(wrapper.get(), (PyObject*) & PyLong_Type)) { jlong l = 0; if (value.getValue().l != nullptr) { auto* jb = dynamic_cast( value.getClass()); l = frame.CallLongMethodA(value.getJavaObject(), jb->m_LongValueID, nullptr); } JPPyObject args = JPPyTuple_Pack(PyLong_FromLongLong(l)); return JPPyObject::call(PyLong_Type.tp_new((PyTypeObject*) wrapper.get(), args.get(), nullptr)); } if (PyObject_IsSubclass(wrapper.get(), (PyObject*) & PyFloat_Type)) { jdouble l = 0; if (value.getValue().l != nullptr) { auto* jb = dynamic_cast( value.getClass()); l = frame.CallDoubleMethodA(value.getJavaObject(), jb->m_DoubleValueID, nullptr); } JPPyObject args = JPPyTuple_Pack(PyFloat_FromDouble(l)); return JPPyObject::call(PyFloat_Type.tp_new((PyTypeObject*) wrapper.get(), args.get(), nullptr)); } JP_RAISE(PyExc_TypeError, "unable to convert"); //GCOVR_EXCL_LINE } jpype-1.6.0/native/python/pyjp_object.cpp000066400000000000000000000306371501674766700205240ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #ifdef __cplusplus extern "C" { #endif static PyObject *PyJPObject_new(PyTypeObject *type, PyObject *pyargs, PyObject *kwargs) { JP_PY_TRY("PyJPObject_new"); // Get the Java class from the type. JPClass *cls = PyJPClass_getJPClass((PyObject*) type); if (cls == nullptr) { PyErr_SetString(PyExc_TypeError, "Java class type is incorrect"); return nullptr; } // Create an instance (this may fail) JPJavaFrame frame = JPJavaFrame::outer(); JPPyObjectVector args(pyargs); JPValue jv = cls->newInstance(frame, args); // If it succeeded then allocate memory PyObject *self = type->tp_alloc(type, 0); JP_PY_CHECK(); JP_FAULT_RETURN("PyJPObject_init.null", self); PyJPValue_assignJavaSlot(frame, self, jv); return self; JP_PY_CATCH(nullptr); } static PyObject *PyJPObject_compare(PyObject *self, PyObject *other, int op) { JP_PY_TRY("PyJPObject_compare"); if (op == Py_NE) { PyObject *ret = PyJPObject_compare(self, other, Py_EQ); if (ret == nullptr) return nullptr; int rc = (ret == Py_False); Py_DECREF(ret); return PyBool_FromLong(rc); } if (op != Py_EQ) { PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; } JPJavaFrame frame = JPJavaFrame::outer(); JPValue *javaSlot0 = PyJPValue_getJavaSlot(self); JPValue *javaSlot1 = PyJPValue_getJavaSlot(other); // First slot is Null if (javaSlot0 == nullptr || javaSlot0->getValue().l == nullptr) { if (javaSlot1 == nullptr) return PyBool_FromLong(other == Py_None); if (javaSlot1->getClass()->isPrimitive()) Py_RETURN_FALSE; if (javaSlot1->getValue().l == nullptr) Py_RETURN_TRUE; Py_RETURN_FALSE; } // Check second slot is Null if (other == Py_None) Py_RETURN_FALSE; if (javaSlot1 == nullptr) { // This block seems like a giant waste as there are very few cases in which // a converted object would ever satisfy equals. But this was the original // logic in JPype so we will try to match it. JPMatch match(&frame, other); javaSlot0->getClass()->findJavaConversion(match); if (match.type < JPMatch::_implicit) Py_RETURN_FALSE; return PyBool_FromLong(frame.equals(javaSlot0->getValue().l, match.convert().l)); } if (javaSlot1->getClass()->isPrimitive()) Py_RETURN_FALSE; if (javaSlot1->getValue().l == nullptr) Py_RETURN_FALSE; return PyBool_FromLong(frame.equals(javaSlot0->getValue().l, javaSlot1->getValue().l)); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPComparable_compare(PyObject *self, PyObject *other, int op) { JP_PY_TRY("PyJPComparable_compare"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue *javaSlot0 = PyJPValue_getJavaSlot(self); JPValue *javaSlot1 = PyJPValue_getJavaSlot(other); bool null0 = false; bool null1 = false; // First slot is Null if (self == Py_None || javaSlot0 == nullptr || (!javaSlot0->getClass()->isPrimitive() && javaSlot0->getValue().l == nullptr)) null0 = true; if (other == Py_None || (javaSlot1 != nullptr && !javaSlot1->getClass()->isPrimitive() && javaSlot1->getValue().l == nullptr)) null1 = true; jobject obj0 = nullptr; jobject obj1 = nullptr; if (!null0) obj0 = javaSlot0->getValue().l; if (!null0 && !null1 && javaSlot1 == nullptr) { // Okay here is the hard part. We need to figure out what type // of object to create to make them comparable. We can't assume // the most derived type because some classes inherit comparable // and that would require the derived type. We have to find // the first super class that implements Comparable. Further, // because of type erasure we can't actually get. JPClass *cls2 = javaSlot0->getClass(); JPMatch match(&frame, other); while (cls2 != nullptr && !cls2->findJavaConversion(match) && !JPModifier::isComparable(cls2->getModifiers())) cls2 = cls2->getSuperClass(); // This should never happen. if (cls2 == nullptr) { PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; } if (match.type < JPMatch::Type::_implicit) { if (op == Py_EQ || op == Py_NE) return PyBool_FromLong(op == Py_NE); PyObject *out = Py_NotImplemented; Py_INCREF(out); return out; } obj1 = match.convert().l; } else if (!null1 && javaSlot1 != nullptr && !javaSlot1->getClass()->isPrimitive()) obj1 = javaSlot1->getValue().l; switch (op) { case Py_EQ: if (null0 && null1) Py_RETURN_TRUE; if (null0 || null1) Py_RETURN_FALSE; return PyBool_FromLong(frame.equals(obj0, obj1)); case Py_NE: if (null0 && null1) Py_RETURN_FALSE; if (null0 || null1) Py_RETURN_TRUE; return PyBool_FromLong(!frame.equals(obj0, obj1)); case Py_LT: if (null0 || null1) break; return PyBool_FromLong(frame.compareTo(obj0, obj1) < 0); case Py_LE: if (null0 || null1) break; return PyBool_FromLong(frame.compareTo(obj0, obj1) <= 0); case Py_GT: if (null0 || null1) break; return PyBool_FromLong(frame.compareTo(obj0, obj1) > 0); case Py_GE: if (null0 || null1) break; return PyBool_FromLong(frame.compareTo(obj0, obj1) >= 0); } PyErr_SetString(PyExc_ValueError, "can't compare null"); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static Py_hash_t PyJPObject_hash(PyObject *obj) { JP_PY_TRY("PyJPObject_hash"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue *javaSlot = PyJPValue_getJavaSlot(obj); if (javaSlot == nullptr) return Py_TYPE(Py_None)->tp_hash(Py_None); jobject o = javaSlot->getJavaObject(); if (o == nullptr) return Py_TYPE(Py_None)->tp_hash(Py_None); return frame.hashCode(o); JP_PY_CATCH(0); } static PyObject *PyJPObject_repr(PyObject *self) { JP_PY_TRY("PyJPObject_repr"); return PyUnicode_FromFormat("", Py_TYPE(self)->tp_name); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static PyObject *PyJPObject_initSubclass(PyObject *cls, PyObject* args, PyObject *kwargs) { Py_RETURN_NONE; } static PyMethodDef objectMethods[] = { {"__init_subclass__", (PyCFunction) PyJPObject_initSubclass, METH_CLASS | METH_VARARGS | METH_KEYWORDS, ""}, {0} }; static PyType_Slot objectSlots[] = { {Py_tp_alloc, (void*) &PyJPValue_alloc}, {Py_tp_finalize, (void*) &PyJPValue_finalize}, {Py_tp_new, (void*) &PyJPObject_new}, {Py_tp_free, (void*) &PyJPValue_free}, {Py_tp_getattro, (void*) &PyJPValue_getattro}, {Py_tp_setattro, (void*) &PyJPValue_setattro}, {Py_tp_str, (void*) &PyJPValue_str}, {Py_tp_repr, (void*) &PyJPObject_repr}, {Py_tp_richcompare, (void*) &PyJPObject_compare}, {Py_tp_hash, (void*) &PyJPObject_hash}, {Py_tp_methods, (void*) objectMethods}, {0} }; PyTypeObject *PyJPObject_Type = nullptr; static PyType_Spec objectSpec = { "_jpype._JObject", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, objectSlots }; static PyObject *PyJPException_new(PyTypeObject *type, PyObject *pyargs, PyObject *kwargs) { JP_PY_TRY("PyJPException_new"); // Get the Java class from the type. JPClass *cls = PyJPClass_getJPClass((PyObject*) type); if (cls == nullptr) { // GCOVR_EXCL_START PyErr_SetString(PyExc_TypeError, "Java class type is incorrect"); return nullptr; } // GCOVR_EXCL_STOP // Special constructor path for Exceptions JPJavaFrame frame = JPJavaFrame::outer(); JPPyObjectVector args(pyargs); if (args.size() == 2 && args[0] == _JObjectKey) return ((PyTypeObject*) PyExc_BaseException)->tp_new(type, args[1], kwargs); // Create an instance (this may fail) JPValue jv = cls->newInstance(frame, args); // Exception must be constructed with the BaseException_new PyObject *self = ((PyTypeObject*) PyExc_BaseException)->tp_new(type, pyargs, kwargs); JP_PY_CHECK(); JP_FAULT_RETURN("PyJPException_init.null", self); PyJPValue_assignJavaSlot(frame, self, jv); return self; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static int PyJPException_init(PyObject *self, PyObject *pyargs, PyObject *kwargs) { JP_PY_TRY("PyJPException_init"); JPPyObjectVector args(pyargs); if (args.size() == 2 && args[0] == _JObjectKey) return ((PyTypeObject*) PyExc_BaseException)->tp_init(self, args[1], kwargs); // Exception must be constructed with the BaseException_new return ((PyTypeObject*) PyExc_BaseException)->tp_init(self, pyargs, kwargs); JP_PY_CATCH(-1); // GCOVR_EXCL_LINE } static PyObject* PyJPException_expandStacktrace(PyObject* self) { JP_PY_TRY("PyJPModule_expandStackTrace"); JPJavaFrame frame = JPJavaFrame::outer(); JPValue *val = PyJPValue_getJavaSlot(self); // These two are loop invariants and must match each time auto th = (jthrowable) val->getValue().l; JPPyObject exc = JPPyObject::use(self); PyJPException_normalize(frame, exc, th, nullptr); Py_RETURN_NONE; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } PyObject *PyJPException_args(PyBaseExceptionObject *self) { if (self->args == nullptr) Py_RETURN_NONE; // GCOVR_EXCL_LINE Py_INCREF(self->args); return self->args; } static PyMethodDef exceptionMethods[] = { {"_expandStacktrace", (PyCFunction) PyJPException_expandStacktrace, METH_NOARGS, ""}, {nullptr}, }; static PyGetSetDef exceptionGetSets[] = { {"_args", (getter) PyJPException_args, nullptr, ""}, {nullptr} }; PyTypeObject *PyJPException_Type = nullptr; static PyType_Slot excSlots[] = { {Py_tp_new, (void*) &PyJPException_new}, {Py_tp_init, (void*) &PyJPException_init}, {Py_tp_str, (void*) &PyJPValue_str}, {Py_tp_getattro, (void*) &PyJPValue_getattro}, {Py_tp_setattro, (void*) &PyJPValue_setattro}, {Py_tp_methods, exceptionMethods}, {Py_tp_getset, exceptionGetSets}, {0} }; static PyType_Spec excSpec = { "_jpype._JException", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, excSlots }; static PyType_Slot comparableSlots[] = { {Py_tp_richcompare, (void*) &PyJPComparable_compare}, {Py_tp_hash, (void*) &PyJPObject_hash}, {0} }; PyTypeObject *PyJPComparable_Type = nullptr; static PyType_Spec comparableSpec = { "_jpype._JComparable", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, comparableSlots }; #ifdef __cplusplus } #endif void PyJPObject_initType(PyObject* module) { PyJPObject_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&objectSpec, nullptr); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JObject", (PyObject*) PyJPObject_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE JPPyObject bases = JPPyTuple_Pack(PyExc_Exception, PyJPObject_Type); PyJPException_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&excSpec, bases.get()); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JException", (PyObject*) PyJPException_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE bases = JPPyTuple_Pack(PyJPObject_Type); PyJPComparable_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&comparableSpec, bases.get()); JP_PY_CHECK(); // GCOVR_EXCL_LINE PyModule_AddObject(module, "_JComparable", (PyObject*) PyJPComparable_Type); JP_PY_CHECK(); // GCOVR_EXCL_LINE } /** * Attach stack frames and causes as required for a Python exception. */ void PyJPException_normalize(JPJavaFrame frame, JPPyObject exc, jthrowable th, jthrowable enclosing) { JP_TRACE_IN("PyJPException_normalize"); JPContext *context = PyJPModule_getContext(); while (th != nullptr) { // Attach the frame to first JPPyObject trace = PyTrace_FromJavaException(frame, th, enclosing); if (trace.get() != nullptr) PyException_SetTraceback(exc.get(), trace.get()); // Check for the next in the cause list enclosing = th; th = frame.getCause(th); if (th == nullptr) return; jvalue v; v.l = (jobject) th; JPPyObject next = context->_java_lang_Object->convertToPythonObject(frame, v, false); // This may already be a Python exception JPValue *val = PyJPValue_getJavaSlot(next.get()); if (val == nullptr) { PyException_SetCause(exc.get(), next.keep()); return; } next.incref(); // Set cause will steal our reference PyException_SetCause(exc.get(), next.get()); exc = next; } JP_TRACE_OUT; // GCOVR_EXCL_LINE } jpype-1.6.0/native/python/pyjp_package.cpp000066400000000000000000000234051501674766700206440ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_stringtype.h" #include #ifdef __cplusplus extern "C" { #endif PyTypeObject *PyJPPackage_Type = nullptr; static PyObject *PyJPPackage_Dict = nullptr; static PyObject *PyJPPackage_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPPackage_new"); PyObject *name = nullptr; if (!PyArg_Parse(args, "(U)", &name)) return nullptr; // Check the cache PyObject *obj = PyDict_GetItem(PyJPPackage_Dict, name); if (obj != nullptr) { Py_INCREF(obj); return obj; } // Otherwise create a new object PyObject *self = PyModule_Type.tp_new(PyJPPackage_Type, args, nullptr); int rc = PyModule_Type.tp_init(self, args, nullptr); if (rc != 0) { // If we fail clean up the mess. Py_DECREF(self); return nullptr; } // Place in cache PyDict_SetItem(PyJPPackage_Dict, name, self); return self; JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } static void dtor(PyObject *self) { // No need for exception if JVM is not running. JPContext *context = JPContext_global; if (context == nullptr || !context->isRunning()) return; auto jo = (jobject) PyCapsule_GetPointer(self, nullptr); if (jo == nullptr) return; JPJavaFrame frame = JPJavaFrame::outer(); frame.DeleteGlobalRef(jo); } static jobject getPackage(JPJavaFrame &frame, PyObject *self) { PyObject *dict = PyModule_GetDict(self); // borrowed PyObject *capsule = PyDict_GetItemString(dict, "_jpackage"); // borrowed jobject jo; if (capsule != nullptr) { jo = (jobject) PyCapsule_GetPointer(capsule, nullptr); return jo; } const char *name = PyModule_GetName(self); // Attempt to load the object. jo = frame.getPackage(name); // Found it, use it. if (jo != nullptr) { jo = frame.NewGlobalRef(jo); capsule = PyCapsule_New(jo, nullptr, dtor); PyDict_SetItemString(dict, "_jpackage", capsule); // no steal // Py_DECREF(capsule); return jo; } // Otherwise, this is a bad package. PyErr_Format(PyExc_AttributeError, "Java package '%s' is not valid", name); return nullptr; } /** * Get an attribute from the package. * * This will auto load packages and classes when encounter, * but first checks the cache. This acts like an standard Python * module otherwise. * * @param self * @param attr * @return */ static PyObject *PyJPPackage_getattro(PyObject *self, PyObject *attr) { JP_PY_TRY("PyJPPackage_getattro"); if (!PyUnicode_Check(attr)) { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%s'", Py_TYPE(attr)->tp_name); return nullptr; } PyObject *dict = PyModule_GetDict(self); if (dict != nullptr) { // Check the cache PyObject *out = PyDict_GetItem(PyModule_GetDict(self), attr); if (out != nullptr) { Py_INCREF(out); return out; } } string attrName = JPPyString::asStringUTF8(attr).c_str(); // Check for private attribute if (attrName.compare(0, 2, "__") == 0) return PyObject_GenericGetAttr((PyObject*) self, attr); // Check for JVM running first JPContext* context = JPContext_global; if (!context->isRunning()) { PyErr_Format(PyExc_RuntimeError, "Unable to import '%s.%U' without JVM", PyModule_GetName(self), attr); return nullptr; } JPJavaFrame frame = JPJavaFrame::outer(); jobject pkg = getPackage(frame, self); if (pkg == nullptr) return nullptr; JPPyObject out; jobject obj; try { obj = frame.getPackageObject(pkg, attrName); } catch (JPypeException& ex) { JPPyObject h = JPPyObject::accept(PyObject_GetAttrString(self, "_handler")); // If something fails, we need to go to a handler if (!h.isNull()) { ex.toPython(); JPPyErrFrame err; err.normalize(); err.clear(); JPPyObject tuple0 = JPPyTuple_Pack(self, attr, err.m_ExceptionValue.get()); PyObject *rc = PyObject_Call(h.get(), tuple0.get(), nullptr); if (rc == nullptr) return nullptr; Py_DECREF(rc); // GCOVR_EXCL_LINE } throw; // GCOVR_EXCL_LINE } if (obj == nullptr) { PyErr_Format(PyExc_AttributeError, "Java package '%s' has no attribute '%U'", PyModule_GetName(self), attr); return nullptr; } else if (frame.IsInstanceOf(obj, context->_java_lang_Class->getJavaClass())) out = PyJPClass_create(frame, frame.findClass((jclass) obj)); else if (frame.IsInstanceOf(obj, context->_java_lang_String->getJavaClass())) { JPPyObject u = JPPyObject::call(PyUnicode_FromFormat("%s.%U", PyModule_GetName(self), attr)); JPPyObject args = JPPyTuple_Pack(u.get()); out = JPPyObject::call(PyObject_Call((PyObject*) PyJPPackage_Type, args.get(), nullptr)); } else { // We should be able to handle Python classes, datafiles, etc, // but that will take time to implement. In principle, things // that are not packages or classes should appear as Buffers or // some other resource type. PyErr_Format(PyExc_AttributeError, "'%U' is unknown object type in Java package", attr); return nullptr; } // Cache the item for now PyDict_SetItem(dict, attr, out.get()); // no steal return out.keep(); JP_PY_CATCH(nullptr); // GCOVR_EXCL_LINE } /** * This next method is required, I have no clue why. Seems * likely that the default PyObject traverse does not agree * with modules. */ static int PyJPPackage_traverse(PyObject *m, visitproc visit, void *arg) { return PyModule_Type.tp_traverse(m, visit, arg); } static int PyJPPackage_clear(PyObject *m) { return PyModule_Type.tp_clear(m); } static PyObject *PyJPPackage_str(PyObject *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPPackage_str"); return PyModule_GetNameObject(self); JP_PY_CATCH(nullptr); } static PyObject *PyJPPackage_repr(PyObject *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPPackage_repr"); return PyUnicode_FromFormat("", PyModule_GetName(self)); JP_PY_CATCH(nullptr); } static PyObject *PyJPPackage_call(PyObject *self, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPPackage_call"); PyErr_Format(PyExc_TypeError, "Package `%s` is not callable.", PyModule_GetName(self)); JP_PY_CATCH(nullptr); } static PyObject *PyJPPackage_package(PyObject *self) { return PyUnicode_FromFormat("java"); } static PyObject *PyJPPackage_path(PyObject *self) { return PyList_New(0); } static PyObject *PyJPPackage_dir(PyObject *self) { JP_PY_TRY("PyJPPackage_dir"); JPJavaFrame frame = JPJavaFrame::outer(); jobject pkg = getPackage(frame, self); if (pkg == nullptr) return nullptr; jarray o = frame.getPackageContents(pkg); Py_ssize_t len = frame.GetArrayLength(o); JPPyObject out = JPPyObject::call(PyList_New(len)); for (Py_ssize_t i = 0; i < len; ++i) { string str = frame.toStringUTF8((jstring) frame.GetObjectArrayElement((jobjectArray) o, (jsize) i)); PyList_SetItem(out.get(), i, PyUnicode_FromFormat("%s", str.c_str())); } return out.keep(); JP_PY_CATCH(nullptr); } /** * Add redirect for matmul in package modules. * * This will be used to support "java@obj" which will be used * to force cast a Python object into Java. * * @param self * @param other * @return */ static PyObject *PyJPPackage_cast(PyObject *self, PyObject *other) { JP_PY_TRY("PyJPPackage_cast"); PyObject *dict = PyModule_GetDict(self); PyObject* matmul = PyDict_GetItemString(dict, "__matmul__"); if (matmul == nullptr) Py_RETURN_NOTIMPLEMENTED; JPPyObject args = JPPyTuple_Pack(self, other); return PyObject_Call(matmul, args.get(), nullptr); JP_PY_CATCH(nullptr); } static PyObject *PyJPPackage_castEq(PyObject *self, PyObject *other) { PyErr_Format(PyExc_TypeError, "Matmul equals not support for Java packages"); return nullptr; } static PyMethodDef packageMethods[] = { {"__dir__", (PyCFunction) PyJPPackage_dir, METH_NOARGS}, {nullptr}, }; static PyGetSetDef packageGetSets[] = { {"__all__", (getter) PyJPPackage_dir, nullptr, ""}, {"__name__", (getter) PyJPPackage_str, nullptr, ""}, {"__package__", (getter) PyJPPackage_package, nullptr, ""}, {"__path__", (getter) PyJPPackage_path, nullptr, ""}, {nullptr} }; static PyType_Slot packageSlots[] = { {Py_tp_new, (void*) PyJPPackage_new}, {Py_tp_traverse, (void*) PyJPPackage_traverse}, {Py_tp_clear, (void*) PyJPPackage_clear}, {Py_tp_getattro, (void*) PyJPPackage_getattro}, {Py_tp_str, (void*) PyJPPackage_str}, {Py_tp_repr, (void*) PyJPPackage_repr}, {Py_tp_call, (void*) PyJPPackage_call}, {Py_nb_matrix_multiply, (void*) PyJPPackage_cast}, {Py_nb_inplace_matrix_multiply, (void*) PyJPPackage_castEq}, {Py_tp_methods, (void*) packageMethods}, {Py_tp_getset, (void*) packageGetSets}, {0} }; static PyType_Spec packageSpec = { "_jpype._JPackage", -1, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, packageSlots }; #ifdef __cplusplus } #endif void PyJPPackage_initType(PyObject* module) { // Inherit from module. JPPyObject bases = JPPyTuple_Pack(&PyModule_Type); packageSpec.basicsize = PyModule_Type.tp_basicsize; PyJPPackage_Type = (PyTypeObject*) PyType_FromSpecWithBases(&packageSpec, bases.get()); JP_PY_CHECK(); PyModule_AddObject(module, "_JPackage", (PyObject*) PyJPPackage_Type); JP_PY_CHECK(); // Set up a dictionary so we can reuse packages PyJPPackage_Dict = PyDict_New(); PyModule_AddObject(module, "_packages", PyJPPackage_Dict); } jpype-1.6.0/native/python/pyjp_proxy.cpp000066400000000000000000000120051501674766700204240ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include #include #include "jpype.h" #include "pyjp.h" #include "jp_proxy.h" #ifdef __cplusplus extern "C" { #endif static PyObject *PyJPProxy_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { JP_PY_TRY("PyJPProxy_new"); auto *self = (PyJPProxy*) type->tp_alloc(type, 0); JP_PY_CHECK(); // Parse arguments PyObject *instance; PyObject *dispatch; PyObject *pyintf; int convert = 0; if (!PyArg_ParseTuple(args, "OOO|p", &instance, &dispatch, &pyintf, &convert)) return nullptr; // Pack interfaces if (!PySequence_Check(pyintf)) { PyErr_SetString(PyExc_TypeError, "third argument must be a list of interface"); return nullptr; } JPJavaFrame frame = JPJavaFrame::outer(); JPClassList interfaces; JPPySequence intf = JPPySequence::use(pyintf); jlong len = intf.size(); if (len < 1) JP_RAISE(PyExc_TypeError, "at least one interface is required"); for (jlong i = 0; i < len; i++) { JPClass *cls = PyJPClass_getJPClass(intf[i].get()); if (cls == nullptr) { PyErr_SetString(PyExc_TypeError, "interfaces must be object class instances"); return nullptr; } interfaces.push_back(cls); } if (dispatch == Py_None) self->m_Proxy = new JPProxyDirect(self, interfaces); else self->m_Proxy = new JPProxyIndirect(self, interfaces); self->m_Target = instance; self->m_Dispatch = dispatch; self->m_Convert = (convert != 0); Py_INCREF(self->m_Target); Py_INCREF(self->m_Dispatch); JP_TRACE("Proxy", self); JP_TRACE("Target", instance); JP_TRACE("Dispatch", dispatch); return (PyObject*) self; JP_PY_CATCH(nullptr); } static int PyJPProxy_traverse(PyJPProxy *self, visitproc visit, void *arg) { Py_VISIT(self->m_Target); Py_VISIT(self->m_Dispatch); return 0; } static int PyJPProxy_clear(PyJPProxy *self) { Py_CLEAR(self->m_Target); Py_CLEAR(self->m_Dispatch); return 0; } void PyJPProxy_dealloc(PyJPProxy* self) { JP_PY_TRY("PyJPProxy_dealloc"); delete self->m_Proxy; PyObject_GC_UnTrack(self); PyJPProxy_clear(self); Py_TYPE(self)->tp_free(self); JP_PY_CATCH_NONE(); } static PyObject *PyJPProxy_class(PyJPProxy *self, void *context) { JPJavaFrame frame = JPJavaFrame::outer(); JPClass* cls = self->m_Proxy->getInterfaces()[0]; return PyJPClass_create(frame, cls).keep(); } static PyObject *PyJPProxy_inst(PyJPProxy *self, void *context) { PyObject *out = self->m_Dispatch; if (out == Py_None) out = (PyObject*) self; Py_INCREF(out); return out; } static PyObject *PyJPProxy_equals(PyJPProxy *self, PyObject *other) { return PyObject_RichCompare((PyObject*) self, other, Py_EQ); } static PyObject *PyJPProxy_hash(PyJPProxy *self) { if (self->m_Target != Py_None) return PyLong_FromLong((int) PyObject_Hash(self->m_Target)); return PyLong_FromLong((int) PyObject_Hash((PyObject*) self)); } static PyObject *PyJPProxy_toString(PyJPProxy *self) { if (self->m_Target != Py_None) return PyObject_Str(self->m_Target); return PyObject_Str((PyObject*) self); } static PyMethodDef proxyMethods[] = { {"equals", (PyCFunction) (&PyJPProxy_equals), METH_O, ""}, {"hashCode", (PyCFunction) (&PyJPProxy_hash), METH_NOARGS, ""}, {"toString", (PyCFunction) (&PyJPProxy_toString), METH_NOARGS, ""}, {nullptr}, }; static PyGetSetDef proxyGetSets[] = { {"__javainst__", (getter) PyJPProxy_inst, nullptr, ""}, {"__javaclass__", (getter) PyJPProxy_class, nullptr, ""}, {nullptr} }; static PyType_Slot proxySlots[] = { { Py_tp_new, (void*) PyJPProxy_new}, { Py_tp_dealloc, (void*) PyJPProxy_dealloc}, { Py_tp_traverse, (void*) PyJPProxy_traverse}, { Py_tp_clear, (void*) PyJPProxy_clear}, { Py_tp_getset, (void*) proxyGetSets}, { Py_tp_methods, (void*) proxyMethods}, {0} }; PyTypeObject *PyJPProxy_Type = nullptr; PyType_Spec PyJPProxySpec = { "_jpype._JProxy", sizeof (PyJPProxy), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, proxySlots }; #ifdef __cplusplus } #endif void PyJPProxy_initType(PyObject* module) { JPPyObject bases = JPPyTuple_Pack(&PyBaseObject_Type); PyJPProxy_Type = (PyTypeObject*) PyType_FromSpecWithBases(&PyJPProxySpec, bases.get()); JP_PY_CHECK(); PyModule_AddObject(module, "_JProxy", (PyObject*) PyJPProxy_Type); JP_PY_CHECK(); } JPProxy *PyJPProxy_getJPProxy(PyObject* obj) { if (PyObject_IsInstance(obj, (PyObject*) PyJPProxy_Type)) return ((PyJPProxy*) obj)->m_Proxy; return nullptr; } jpype-1.6.0/native/python/pyjp_value.cpp000066400000000000000000000243371501674766700203720ustar00rootroot00000000000000/***************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. *****************************************************************************/ #include "jpype.h" #include "pyjp.h" #include "jp_stringtype.h" #include #include #ifdef __cplusplus extern "C" { #endif std::mutex mtx; // Create a dummy type which we will use only for allocation PyTypeObject* PyJPAlloc_Type = nullptr; /** * Allocate a new Python object with a slot for Java. * * We need extra space to store our values, but because there * is no way to do so without disturbing the object layout. * Fortunately, Python already handles this for dict and weakref. * Python aligns the ends of the structure and increases the * base type size to add additional slots to a standard object. * * We will use the same trick to add an additional slot for Java * after the end of the object outside of where Python is looking. * As the memory is aligned this is safe to do. We will use * the alloc and finalize slot to recognize which objects have this * extra slot appended. */ PyObject* PyJPValue_alloc(PyTypeObject* type, Py_ssize_t nitems) { JP_PY_TRY("PyJPValue_alloc"); #if PY_VERSION_HEX >= 0x030d0000 // This flag will try to place the dictionary are part of the object which // adds an unknown number of bytes to the end of the object making it impossible // to attach our needed data. If we kill the flag then we get usable behavior. if (PyType_HasFeature(type, Py_TPFLAGS_INLINE_VALUES)) { PyErr_Format(PyExc_RuntimeError, "Unhandled object layout"); return 0; } #endif PyObject* obj = nullptr; { std::lock_guard lock(mtx); // Mutate the allocator type PyJPAlloc_Type->tp_flags = type->tp_flags; PyJPAlloc_Type->tp_basicsize = type->tp_basicsize + sizeof (JPValue); PyJPAlloc_Type->tp_itemsize = type->tp_itemsize; // Create a new allocation for the dummy type obj = PyType_GenericAlloc(PyJPAlloc_Type, nitems); } // Watch for memory errors if (obj == nullptr) return nullptr; // Polymorph the type to match our desired type obj->ob_type = type; Py_INCREF(type); JP_TRACE("alloc", type->tp_name, obj); return obj; JP_PY_CATCH(nullptr); } bool PyJPValue_hasJavaSlot(PyTypeObject* type) { if (type == nullptr || type->tp_alloc != (allocfunc) PyJPValue_alloc || type->tp_finalize != (destructor) PyJPValue_finalize) return false; // GCOVR_EXCL_LINE return true; } Py_ssize_t PyJPValue_getJavaSlotOffset(PyObject* self) { PyTypeObject *type = Py_TYPE(self); if (type == nullptr || type->tp_alloc != (allocfunc) PyJPValue_alloc || type->tp_finalize != (destructor) PyJPValue_finalize) { return 0; } Py_ssize_t offset = 0; Py_ssize_t sz = 0; #if PY_VERSION_HEX>=0x030c0000 // starting in 3.12 there is no longer ob_size in PyLong if (PyType_HasFeature(self->ob_type, Py_TPFLAGS_LONG_SUBCLASS)) sz = (((PyLongObject*)self)->long_value.lv_tag) >> 3; // Private NON_SIZE_BITS else #endif if (type->tp_itemsize != 0) sz = Py_SIZE(self); // PyLong abuses ob_size with negative values prior to 3.12 if (sz < 0) sz = -sz; if (type->tp_itemsize == 0) offset = _PyObject_VAR_SIZE(type, 1); else offset = _PyObject_VAR_SIZE(type, sz + 1); return offset; } /** * Get the Java value if attached. * * The Java class is guaranteed not to be nullptr on success. * * @param obj * @return the Java value or 0 if not found. */ JPValue* PyJPValue_getJavaSlot(PyObject* self) { Py_ssize_t offset = PyJPValue_getJavaSlotOffset(self); if (offset == 0) return nullptr; auto value = (JPValue*) (((char*) self) + offset); if (value->getClass() == nullptr) return nullptr; return value; } void PyJPValue_free(void* obj) { JP_PY_TRY("PyJPValue_free", obj); // Normally finalize is not run on simple classes. PyTypeObject *type = Py_TYPE(obj); if (type->tp_finalize != nullptr) type->tp_finalize((PyObject*) obj); if (type->tp_flags & Py_TPFLAGS_HAVE_GC) PyObject_GC_Del(obj); else PyObject_Free(obj); // GCOVR_EXCL_LINE JP_PY_CATCH_NONE(); } void PyJPValue_finalize(void* obj) { JP_PY_TRY("PyJPValue_finalize", obj); JP_TRACE("type", Py_TYPE(obj)->tp_name); JPValue* value = PyJPValue_getJavaSlot((PyObject*) obj); if (value == nullptr) return; // We can skip if the JVM is stopped. No need for an exception here. JPContext *context = JPContext_global; if (context == nullptr || !context->isRunning()) return; // JVM is running so set up the frame. JPJavaFrame frame = JPJavaFrame::outer(); JPClass* cls = value->getClass(); // This one can't check for initialized because we may need to delete a stale // resource after shutdown. if (cls != nullptr && context->isRunning() && !cls->isPrimitive()) { JP_TRACE("Value", cls->getCanonicalName(), &(value->getValue())); JP_TRACE("Dereference object"); context->ReleaseGlobalRef(value->getValue().l); *value = JPValue(); } JP_PY_CATCH_NONE(); } /** This is the way to convert an object into a python string. */ PyObject* PyJPValue_str(PyObject* self) { JP_PY_TRY("PyJPValue_str", self); JPContext *context = PyJPModule_getContext(); JPJavaFrame frame = JPJavaFrame::outer(); JPValue* value = PyJPValue_getJavaSlot(self); if (value == nullptr) { PyErr_SetString(PyExc_TypeError, "Not a Java value"); return nullptr; } JPClass* cls = value->getClass(); if (cls->isPrimitive()) { PyErr_SetString(PyExc_TypeError, "toString requires a Java object"); return nullptr; } if (value->getValue().l == nullptr) return JPPyString::fromStringUTF8("null").keep(); if (cls == context->_java_lang_String) { PyObject *cache; JPPyObject dict = JPPyObject::accept(PyObject_GenericGetDict(self, nullptr)); if (!dict.isNull()) { cache = PyDict_GetItemString(dict.get(), "_jstr"); if (cache) { Py_INCREF(cache); return cache; } auto jstr = (jstring) value->getValue().l; string str; str = frame.toStringUTF8(jstr); cache = JPPyString::fromStringUTF8(str).keep(); PyDict_SetItemString(dict.get(), "_jstr", cache); return cache; } } // In general toString is not immutable, so we won't cache it. return JPPyString::fromStringUTF8(frame.toString(value->getValue().l)).keep(); JP_PY_CATCH(nullptr); } PyObject *PyJPValue_getattro(PyObject *obj, PyObject *name) { JP_PY_TRY("PyJPObject_getattro"); if (!PyUnicode_Check(name)) { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", Py_TYPE(name)->tp_name); return nullptr; } // Private members are accessed directly PyObject* pyattr = PyBaseObject_Type.tp_getattro(obj, name); if (pyattr == nullptr) return nullptr; JPPyObject attr = JPPyObject::accept(pyattr); // Private members go regardless if (PyUnicode_GetLength(name) && PyUnicode_ReadChar(name, 0) == '_') return attr.keep(); // Methods if (Py_TYPE(attr.get()) == (PyTypeObject*) PyJPMethod_Type) return attr.keep(); // Don't allow properties to be rewritten if (!PyObject_IsInstance(attr.get(), (PyObject*) & PyProperty_Type)) return attr.keep(); PyErr_Format(PyExc_AttributeError, "Field '%U' is static", name); return nullptr; JP_PY_CATCH(nullptr); } int PyJPValue_setattro(PyObject *self, PyObject *name, PyObject *value) { JP_PY_TRY("PyJPObject_setattro"); // Private members are accessed directly if (PyUnicode_GetLength(name) && PyUnicode_ReadChar(name, 0) == '_') return PyObject_GenericSetAttr(self, name, value); JPPyObject f = JPPyObject::accept(PyJP_GetAttrDescriptor(Py_TYPE(self), name)); if (f.isNull()) { PyErr_Format(PyExc_AttributeError, "Field '%U' is not found", name); return -1; } descrsetfunc desc = Py_TYPE(f.get())->tp_descr_set; if (desc != nullptr) return desc(f.get(), self, value); // Not a descriptor PyErr_Format(PyExc_AttributeError, "Field '%U' is not settable on Java '%s' object", name, Py_TYPE(self)->tp_name); return -1; JP_PY_CATCH(-1); } #ifdef __cplusplus } #endif // These are from the internal methods when we already have the jvalue void PyJPValue_assignJavaSlot(JPJavaFrame &frame, PyObject* self, const JPValue& value) { Py_ssize_t offset = PyJPValue_getJavaSlotOffset(self); // GCOVR_EXCL_START if (offset == 0) { std::stringstream ss; ss << "Missing Java slot on `" << Py_TYPE(self)->tp_name << "`"; JP_RAISE(PyExc_SystemError, ss.str()); } // GCOVR_EXCL_STOP auto* slot = (JPValue*) (((char*) self) + offset); // GCOVR_EXCL_START // This is a sanity check that should never trigger in normal operations. if (slot->getClass() != nullptr) { JP_RAISE(PyExc_SystemError, "Slot assigned twice"); } // GCOVR_EXCL_STOP JPClass* cls = value.getClass(); if (cls != nullptr && !cls->isPrimitive()) { jvalue q; q.l = frame.NewGlobalRef(value.getValue().l); *slot = JPValue(cls, q); } else *slot = value; } bool PyJPValue_isSetJavaSlot(PyObject* self) { Py_ssize_t offset = PyJPValue_getJavaSlotOffset(self); if (offset == 0) return false; // GCOVR_EXCL_LINE auto* slot = (JPValue*) (((char*) self) + offset); return slot->getClass() != nullptr; } /***************** Create a dummy type for use when allocating. ************************/ static int PyJPAlloc_traverse(PyObject *self, visitproc visit, void *arg) { return 0; } static int PyJPAlloc_clear(PyObject *self) { return 0; } static PyType_Slot allocSlots[] = { { Py_tp_traverse, (void*) PyJPAlloc_traverse}, { Py_tp_clear, (void*) PyJPAlloc_clear}, {0, NULL} // Sentinel }; static PyType_Spec allocSpec = { "_jpype._JAlloc", sizeof(PyObject), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, allocSlots }; void PyJPValue_initType(PyObject* module) { PyObject *bases = PyTuple_Pack(1, &PyBaseObject_Type); PyJPAlloc_Type = (PyTypeObject*) PyType_FromSpecWithBases(&allocSpec, bases); Py_DECREF(bases); Py_INCREF(PyJPAlloc_Type); JP_PY_CHECK(); } jpype-1.6.0/project/000077500000000000000000000000001501674766700143365ustar00rootroot00000000000000jpype-1.6.0/project/README.md000066400000000000000000000002071501674766700156140ustar00rootroot00000000000000This directory contains project files for netbeans to use for editing and refactoring. These files are not used to build the project. jpype-1.6.0/project/debug/000077500000000000000000000000001501674766700154245ustar00rootroot00000000000000jpype-1.6.0/project/debug/unix/000077500000000000000000000000001501674766700164075ustar00rootroot00000000000000jpype-1.6.0/project/debug/unix/README.rst000066400000000000000000000111641501674766700201010ustar00rootroot00000000000000JVM startup debugging ===================== So the JVM won't start under JPype. It works fine for everyone else, but just not you. Now what? The following instructions are for Linux, but apply to most unix like systems with minor modifications. JAVA_HOME --------- First make sure your JAVA_HOME is set correctly. It should point to a directory containing the JRE or JDK. It should look something like...:: bin conf COPYRIGHT include jmods legal lib release For Unix we expect to find ``$JAVA_HOME\lib\server\libjvm.so`` or ``$JAVA_HOME\lib\client\libjvm.dll``. We will assume it is server for this document. Architecture ------------ To check the type of a file, use the ``file`` command. First lets check the architecture of the ``libjvm.so`` file:: $ file $JAVA_HOME/lib/server/libjvm.so /usr/lib/jvm/java-11-openjdk-amd64/lib/server/libjvm.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=1313ccf3deaef446a6c2fec9561f2232e4202bc8, stripped We can also check the architecture of python or the test file:: $ file a.out a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=24c58f45e6aa4a41c0d69a74063243b692d51e0d, not stripped $ file /usr/bin/python3.8 /usr/bin/python3.8: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=f9e05e26d8232239158889727d8056c122a9e958, stripped The import thing is that each must have the samme architecture. In this case, 64-bit LSB x86-64. If one or more libraries or binaries has a mismatch then it likely won't work with that JVM. Dependencies ------------ The JVM has many dependencies. If any of them are missing, or not in a search path, or they are the wrong architecture then it will fail to load. To get a list of dependencies use:: ldd -v "$JAVA_HOME/lib/server/libjvm.so" It should produce an output similar to:: linux-vdso.so.1 (0x00007ffff3591000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd55b5b0000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd55b390000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fd55b000000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fd55ac60000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd55a860000) /lib64/ld-linux-x86-64.so.2 (0x00007fd55cc00000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fd55a640000) If any of these libraries are missing or the wrong architecture it will cause issues. Testing the JVM --------------- Assuming that you have the JAVA_HOME set, the architectures are correct, and all of the dependencies are in place it should load. But lets run a test. Compile using the System C++ compiler. Use:: g++ testJVM.cpp -ldl Run it with:: a.out "$JAVA_HOME\lib\server\libjvm.so" It should produce and output like:: $ ./a.out $JAVA_HOME/lib/server/libjvm.so Load library Load entry points Entry point found 0x7fad8e08f670 Pack JVM arguments Num options: 0 Create JVM Create Java resources Destroy JVM Unload library Success Testing static load ------------------- Dlopen provides very little diagnostic information. So you may want to try a static JVM load. To build a static JVM test use:: g++ staticJVM.cpp -ljvm -L $JAVA_HOME/lib/server/ The launch it with:: LD_LIBRARY_PATH=$JAVA_HOME/lib/server a.out This should produce the output:: Pack JVM arguments Num options: 0 Create JVM Create Java resources Destroy JVM Success If the library does not match it should produce something like:: ./a.out: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory Two different libstdc++ versions -------------------------------- If you are compiling the source on a test environment and pushing the compiled Jpype source to a different environment (Unix/Linux) you might see a situation where running:: ldd libstdc++.so.6 links to a different version of your production environment. You can rememdy this by copying the so file to your production system and creating a soft link to the file with:: ln -s libstdc++.so.6.0.29 libstdc++.so.6 Then override the system's (in the example below, Oracle/Solaris) 64bit dynamic linker path with:: export LD_LIBRARY_PATH_64=/path/to/linked/file/above NOTE: You will want to include the path to any other .so files you will be needing. jpype-1.6.0/project/debug/unix/staticJVM.cpp000066400000000000000000000032301501674766700207550ustar00rootroot00000000000000#if defined(_HPUX) && !defined(_IA64) #include #else #include #endif // HPUX #include #include #include #include #include //#include #include "../../../native/jni_include/jni.h" #define USE_JNI_VERSION JNI_VERSION_1_4 JavaVM *jvm; int main(int argc, char** argv) { if (argc < 1) { printf("Usage: %s [JVM_ARGS]\n", argv[0]); exit(-1); } // Pack the arguments printf("Pack JVM arguments\n"); JavaVMInitArgs jniArgs; jniArgs.options = NULL; jniArgs.version = USE_JNI_VERSION; jniArgs.ignoreUnrecognized = false; jniArgs.nOptions = (jint) argc - 1; printf(" Num options: %d\n", jniArgs.nOptions); jniArgs.options = (JavaVMOption*) malloc(sizeof (JavaVMOption) * jniArgs.nOptions); memset(jniArgs.options, 0, sizeof (JavaVMOption) * jniArgs.nOptions); for (int i = 0; i < jniArgs.nOptions; i++) { printf(" Option %s\n", argv[i + 1]); jniArgs.options[i].optionString = (char*) argv[i + 1]; } // Launch the JVM printf("Create JVM\n"); JNIEnv* env; jint rc = JNI_CreateJavaVM(&jvm, (JNIEnv**) &env, (void*) &jniArgs); free(jniArgs.options); if (rc != JNI_OK) { printf(" Create JVM failed. (%d)\n", rc); exit(-1); } // Create some resources printf("Create Java resources\n"); jclass cls = env->FindClass("java/lang/Object"); jmethodID mth = env->GetMethodID(cls, "toString", "()Ljava/lang/String;"); mth = env->GetMethodID(cls, "equals", "(Ljava/lang/Object;)Z"); mth = env->GetMethodID(cls, "hashCode", "()I"); mth = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;"); // Destroy the JVM printf("Destroy JVM\n"); jvm->DestroyJavaVM(); printf("Success\n"); } jpype-1.6.0/project/debug/unix/testJVM.cpp000066400000000000000000000056221501674766700204540ustar00rootroot00000000000000#if defined(_HPUX) && !defined(_IA64) #include #else #include #endif // HPUX #include #include #include #include #include //#include #include "../../../native/jni_include/jni.h" void* jvmLibrary; jint(JNICALL * CreateJVM_Method)(JavaVM **pvm, void **penv, void *args); #define USE_JNI_VERSION JNI_VERSION_1_4 JavaVM *jvm; void loadLibrary(const char* path) { #if defined(_HPUX) && !defined(_IA64) jvmLibrary = shl_load(path, BIND_DEFERRED | BIND_VERBOSE, 0L); #else jvmLibrary = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); #endif // HPUX if (jvmLibrary == NULL) { printf("Failed to load JVM from %s\n", path); printf("Errno: %d\n", errno); if (errno == ENOEXEC) { printf("diagnostics: %s\n", dlerror()); } exit(-1); } } void unloadLibrary() { int r = dlclose(jvmLibrary); if (r != 0) // error { printf(" Failed to unload JVM\n"); printf("Errno: %d\n", errno); printf("Dlerror: %s\n", dlerror()); exit(-1); } } void* getSymbol(const char* name) { void* res = dlsym(jvmLibrary, name); if (res == NULL) { printf(" Failed to get Symbol %s\n", name); printf("errno %d\n", errno); exit(-1); } return res; } void loadEntryPoints() { CreateJVM_Method = (jint(JNICALL *)(JavaVM **, void **, void *) )getSymbol("JNI_CreateJavaVM"); printf(" Entry point found %p\n", CreateJVM_Method); } int main(int argc, char** argv) { if (argc < 2) { printf("Usage: %s $JAVA_HOME/lib/server/libjvm.so [JVM_ARGS]\n", argv[0]); exit(-1); } printf("Load library\n"); loadLibrary(argv[1]); printf("Load entry points\n"); loadEntryPoints(); // Pack the arguments printf("Pack JVM arguments\n"); JavaVMInitArgs jniArgs; jniArgs.options = NULL; jniArgs.version = USE_JNI_VERSION; jniArgs.ignoreUnrecognized = false; jniArgs.nOptions = (jint) argc - 2; printf(" Num options: %d\n", jniArgs.nOptions); jniArgs.options = (JavaVMOption*) malloc(sizeof (JavaVMOption) * jniArgs.nOptions); memset(jniArgs.options, 0, sizeof (JavaVMOption) * jniArgs.nOptions); for (int i = 0; i < jniArgs.nOptions; i++) { printf(" Option %s\n", argv[i + 2]); jniArgs.options[i].optionString = (char*) argv[i + 2]; } // Launch the JVM printf("Create JVM\n"); JNIEnv* env; jint rc = CreateJVM_Method(&jvm, (void**) &env, (void*) &jniArgs); free(jniArgs.options); if (rc != JNI_OK) { printf(" Create JVM failed. (%d)\n", rc); exit(-1); } // Create some resources printf("Create Java resources\n"); jclass cls = env->FindClass("java/lang/Object"); jmethodID mth = env->GetMethodID(cls, "toString", "()Ljava/lang/String;"); mth = env->GetMethodID(cls, "equals", "(Ljava/lang/Object;)Z"); mth = env->GetMethodID(cls, "hashCode", "()I"); mth = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;"); // Destroy the JVM printf("Destroy JVM\n"); jvm->DestroyJavaVM(); printf("Unload library\n"); unloadLibrary(); printf("Success\n"); } jpype-1.6.0/project/debug/windows/000077500000000000000000000000001501674766700171165ustar00rootroot00000000000000jpype-1.6.0/project/debug/windows/README.rst000066400000000000000000000124551501674766700206140ustar00rootroot00000000000000JVM startup debugging ===================== So the JVM won't start under JPype. It works fine for everyone else, but just not you. Now what? JAVA_HOME --------- First make sure your JAVA_HOME is set correctly. It should point to a directory containing the JRE or JDK. It should look something like...:: bin conf COPYRIGHT include jmods legal lib release For Windows we expect to find ``%JAVA_HOME%\bin\server\jvm.dll`` or ``%JAVA_HOME%\bin\client\jvm.dll``. We will assume it is server for this document. Architecture ------------ Next make sure that Python and the JVM have the same architecture. We can do this with ``DUMPBIN``. Run:: dumpbin /headers "%JAVA_HOME%/bin/jvm.dll" It will produce a big output. The relevant section is:: File Type: DLL FILE HEADER VALUES 8664 machine (x64) <== Type is x64 7 number of sections 5CA35D83 time date stamp Tue Apr 2 06:02:59 2019 0 file pointer to symbol table 0 number of symbols F0 size of optional header 2022 characteristics Executable Application can handle large (>2GB) addresses DLL Run the same command on the Python executable:: File Type: EXECUTABLE IMAGE FILE HEADER VALUES 8664 machine (x64) <== Type is x64 6 number of sections 5C9BF5B3 time date stamp Wed Mar 27 15:14:11 2019 0 file pointer to symbol table 0 number of symbols F0 size of optional header 22 characteristics Executable Application can handle large (>2GB) addresses If they match then the issue is not an architecture problem. Dependencies ------------ The JVM has many dependencies. If any of them are missing, or not in a search path, or they are the wrong architecture then it will fail to load. To get a list of dependencies use:: dumpbin /dependents "%JAVA_HOME%/bin/server/jvm.dll" It should produce an output:: Dump of file C:\Program Files\Java\jdk-12.0.1/bin/server/jvm.dll File Type: DLL Image has the following dependencies: KERNEL32.dll USER32.dll ADVAPI32.dll WSOCK32.dll WINMM.dll VERSION.dll PSAPI.DLL VCRUNTIME140.dll api-ms-win-crt-stdio-l1-1-0.dll api-ms-win-crt-string-l1-1-0.dll api-ms-win-crt-runtime-l1-1-0.dll api-ms-win-crt-convert-l1-1-0.dll api-ms-win-crt-environment-l1-1-0.dll api-ms-win-crt-utility-l1-1-0.dll api-ms-win-crt-math-l1-1-0.dll api-ms-win-crt-filesystem-l1-1-0.dll api-ms-win-crt-time-l1-1-0.dll api-ms-win-crt-heap-l1-1-0.dll Summary DB000 .data 59000 .pdata 243000 .rdata 39000 .reloc 1000 .rsrc 7BF000 .text 3000 _RDATA So every one of these libraries needs to be found on the path. All of the required DLLs should be located in ``%JAVA_HOME%/bin``. However, Windows search order may find another copy of one of these DLLs elsewhere in the search process. If the file it finds is the wrong architechure or corrupt the JVM will fail to load. Please note the search order in https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order The order is:: 1) The directory from which the application loaded. 2) The current directory. 3) The system directory. Use the GetSystemDirectory function to get the path of this directory. 4) The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. 5) The Windows directory. Use the GetWindowsDirectory function to get the path of this directory. 6) The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path. So even if the PATH is pointing to ``%JAVA_HOME%/bin``, you may have a bad dll in any of the first 5 places. Testing the JVM --------------- Assuming that you have the JAVA_HOME set, the architectures are correct, and all of the dependencies are in place it should load. But lets run a test. Compile using the Visual Studio shell ``x64 Native Tools Command`` or ``x86 Native Tools Command`` depending on your Python architecture. Use:: CL.EXE testJVM.cpp Run it with:: testJVM.exe "%JAVA_HOME%\bin\server\jvm.dll" It should produce and output like:: Check paths SystemDirectory: C:\windows\system32 WindowsDirectory: C:\windows Load library Load entry points Entry point found 00007FFEDE4703B0 Pack JVM arguments Num options: 0 Create JVM Create Java resources Destroy JVM Unload library Success Here are some common problems: The architecture of the jvm or one of the dependencies does not match the program or is corrupted:: Failed to load JVM from C:\Program Files (x86)\Java\jre1.8.0_251\bin\client\jvm.dll LastError 193 Message: %1 is not a valid Win32 application. A module or jar file is missing from the Java distribution:: Failed to load JVM from jvm1.dll Error occurred during initialization of VM Failed setting boot class path. A dll used by the JVM is missing:: Failed to load JVM from jvm1.dll LastError 126 Message: The specified module could not be found. jpype-1.6.0/project/debug/windows/testJVM.cpp000066400000000000000000000071361501674766700211650ustar00rootroot00000000000000#include #include //#include #include "../../../native/jni_include/jni.h" HINSTANCE jvmLibrary; jint(JNICALL * CreateJVM_Method)(JavaVM **pvm, void **penv, void *args); #define USE_JNI_VERSION JNI_VERSION_1_4 JavaVM *jvm; std::string formatMessage(DWORD msgCode) { LPVOID lpMsgBuf; DWORD rc = ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, msgCode, 0, (LPTSTR) & lpMsgBuf, 0, NULL ); if (rc == 0) { printf("format message returned 0 (%d)", ::GetLastError()); exit(-1); } std::string res((LPTSTR) lpMsgBuf); ::LocalFree(lpMsgBuf); return res; } void loadLibrary(const char* path) { jvmLibrary = ::LoadLibrary(path); if (jvmLibrary == NULL) { printf("Failed to load JVM from %s\n", path); printf("LastError %d\n", GetLastError()); printf("Message: %s\n", formatMessage(GetLastError()).c_str()); exit(-1); } } void unloadLibrary() { if (::FreeLibrary(jvmLibrary) == 0) { printf(" Failed to unload JVM\n"); printf(" LastError %d\n", GetLastError()); printf(" Message: %s\n", formatMessage(GetLastError()).c_str()); exit(-1); } } void* getSymbol(const char* name) { void* res = (void*) ::GetProcAddress(jvmLibrary, name); if (res == NULL) { printf(" Failed to get Symbol %s\n", name); printf(" LastError %d\n", GetLastError()); printf(" Message: %s\n", formatMessage(GetLastError()).c_str()); exit(-1); } return res; } void loadEntryPoints() { CreateJVM_Method = (jint(JNICALL *)(JavaVM **, void **, void *) )getSymbol("JNI_CreateJavaVM"); printf(" Entry point found %p\n", CreateJVM_Method); } int main(int argc, char** argv) { if (argc < 2) { printf("Usage: %s %%JAVA_HOME%%/bin/server/jvm.dll [JVM_ARGS]", argv[0]); exit(-1); } printf("Check paths\n"); char directory[1024]; int sz = ::GetSystemDirectoryA(directory, 1024); if (sz == 0) { printf("GetSystemDirectory failed\n"); exit(-1); } printf(" SystemDirectory: %s\n", std::string(directory, sz).c_str()); sz = ::GetWindowsDirectoryA(directory, 1024); if (sz == 0) { printf("GetWindowsDirectory failed\n"); exit(-1); } printf(" WindowsDirectory: %s\n", std::string(directory, sz).c_str()); printf("Load library\n"); loadLibrary(argv[1]); printf("Load entry points\n"); loadEntryPoints(); // Pack the arguments printf("Pack JVM arguments\n"); JavaVMInitArgs jniArgs; jniArgs.options = NULL; jniArgs.version = USE_JNI_VERSION; jniArgs.ignoreUnrecognized = false; jniArgs.nOptions = (jint) argc - 2; printf(" Num options: %d\n", jniArgs.nOptions); jniArgs.options = (JavaVMOption*) malloc(sizeof (JavaVMOption) * jniArgs.nOptions); memset(jniArgs.options, 0, sizeof (JavaVMOption) * jniArgs.nOptions); for (int i = 0; i < jniArgs.nOptions; i++) { printf(" Option %s\n", argv[i + 2]); jniArgs.options[i].optionString = (char*) argv[i + 2]; } // Launch the JVM printf("Create JVM\n"); JNIEnv* env; jint rc = CreateJVM_Method(&jvm, (void**) &env, (void*) &jniArgs); free(jniArgs.options); if (rc != JNI_OK) { printf(" Create JVM failed. (%d)\n", rc); exit(-1); } // Create some resources printf("Create Java resources\n"); jclass cls = env->FindClass("java/lang/Object"); jmethodID mth = env->GetMethodID(cls, "toString", "()Ljava/lang/String;"); mth = env->GetMethodID(cls, "equals", "(Ljava/lang/Object;)Z"); mth = env->GetMethodID(cls, "hashCode", "()I"); mth = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;"); // Destroy the JVM printf("Destroy JVM\n"); jvm->DestroyJavaVM(); printf("Unload library\n"); unloadLibrary(); printf("Success\n"); } jpype-1.6.0/project/jars/000077500000000000000000000000001501674766700152755ustar00rootroot00000000000000jpype-1.6.0/project/jars/README000066400000000000000000000001251501674766700161530ustar00rootroot00000000000000This directory contains patterns for constructing special jar files used in testing. jpype-1.6.0/project/jars/late/000077500000000000000000000000001501674766700162225ustar00rootroot00000000000000jpype-1.6.0/project/jars/late/build.sh000066400000000000000000000003121501674766700176510ustar00rootroot00000000000000javac --release 7 -d classes1 src/org/jpype/late/*.java javac --release 7 -d classes2 src/org/jpype/late2/*.java jar --create --file late.jar -C classes1 . jar --create --file late2.jar -C classes2 . jpype-1.6.0/project/jars/late/src/000077500000000000000000000000001501674766700170115ustar00rootroot00000000000000jpype-1.6.0/project/jars/late/src/org/000077500000000000000000000000001501674766700176005ustar00rootroot00000000000000jpype-1.6.0/project/jars/late/src/org/jpype/000077500000000000000000000000001501674766700207275ustar00rootroot00000000000000jpype-1.6.0/project/jars/late/src/org/jpype/late/000077500000000000000000000000001501674766700216545ustar00rootroot00000000000000jpype-1.6.0/project/jars/late/src/org/jpype/late/Test.java000066400000000000000000000001651501674766700234400ustar00rootroot00000000000000package org.jpype.late; public class Test { public int field = 5; public String method() { return "Yes"; } } jpype-1.6.0/project/jars/late/src/org/jpype/late2/000077500000000000000000000000001501674766700217365ustar00rootroot00000000000000jpype-1.6.0/project/jars/late/src/org/jpype/late2/Test.java000066400000000000000000000001651501674766700235220ustar00rootroot00000000000000package org.jpype.late2; public class Test { public int field = 6; public String method() { return "No"; } } jpype-1.6.0/project/jars/missing/000077500000000000000000000000001501674766700167465ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/build.sh000066400000000000000000000002451501674766700204020ustar00rootroot00000000000000javac --release 7 -d classes src/org/jpype/missing/*.java jar --create --file missing.jar -M -C classes META-INF/MANIFEST.MF -C classes org/jpype/missing/Test.class jpype-1.6.0/project/jars/missing/classes/000077500000000000000000000000001501674766700204035ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/classes/META-INF/000077500000000000000000000000001501674766700215435ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/classes/META-INF/MANIFEST.MF000066400000000000000000000000701501674766700231720ustar00rootroot00000000000000Manifest-Version: 1.0 Created-By: 11.0.9.1 (Ubuntu) jpype-1.6.0/project/jars/missing/src/000077500000000000000000000000001501674766700175355ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/src/org/000077500000000000000000000000001501674766700203245ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/src/org/jpype/000077500000000000000000000000001501674766700214535ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/src/org/jpype/missing/000077500000000000000000000000001501674766700231245ustar00rootroot00000000000000jpype-1.6.0/project/jars/missing/src/org/jpype/missing/Test.java000066400000000000000000000000631501674766700247050ustar00rootroot00000000000000package org.jpype.missing; public class Test { } jpype-1.6.0/project/jars/mrjar/000077500000000000000000000000001501674766700164105ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/README000066400000000000000000000000751501674766700172720ustar00rootroot00000000000000This is the sources for building the multi-release jar test. jpype-1.6.0/project/jars/mrjar/build.sh000066400000000000000000000003721501674766700200450ustar00rootroot00000000000000javac --release 7 -d classes src/org/jpype/mrjar/*.java javac --release 7 -d classes src/org/jpype/mrjar/sub/*.java javac --release 9 -d classes-9 src/java9/org/jpype/mrjar/*.java jar --create --file mrjar.jar -C classes . --release 9 -C classes-9 . jpype-1.6.0/project/jars/mrjar/src/000077500000000000000000000000001501674766700171775ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/java9/000077500000000000000000000000001501674766700202115ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/java9/org/000077500000000000000000000000001501674766700210005ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/java9/org/jpype/000077500000000000000000000000001501674766700221275ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/java9/org/jpype/mrjar/000077500000000000000000000000001501674766700232425ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/java9/org/jpype/mrjar/B.java000066400000000000000000000001271501674766700242660ustar00rootroot00000000000000package org.jpype.mrjar; public class B { public String call() { return "9"; } } jpype-1.6.0/project/jars/mrjar/src/org/000077500000000000000000000000001501674766700177665ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/org/jpype/000077500000000000000000000000001501674766700211155ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/org/jpype/mrjar/000077500000000000000000000000001501674766700222305ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/org/jpype/mrjar/A.java000066400000000000000000000000551501674766700232530ustar00rootroot00000000000000package org.jpype.mrjar; public class A { } jpype-1.6.0/project/jars/mrjar/src/org/jpype/mrjar/B.java000066400000000000000000000001271501674766700232540ustar00rootroot00000000000000package org.jpype.mrjar; public class B { public String call() { return "7"; } } jpype-1.6.0/project/jars/mrjar/src/org/jpype/mrjar/sub/000077500000000000000000000000001501674766700230215ustar00rootroot00000000000000jpype-1.6.0/project/jars/mrjar/src/org/jpype/mrjar/sub/C.java000066400000000000000000000000611501674766700240430ustar00rootroot00000000000000package org.jpype.mrjar.sub; public class C { } jpype-1.6.0/project/jars/unicode_à😎/000077500000000000000000000000001501674766700211125ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/sample_package/000077500000000000000000000000001501674766700240465ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/sample_package/README000066400000000000000000000002461501674766700247300ustar00rootroot00000000000000Test to verify that JAR's with unicode characters in the path are loaded correctly. The built jar should be placed in ``test/jar/unicode_à😎/sample_package.jar``. jpype-1.6.0/project/jars/unicode_à😎/sample_package/build.sh000066400000000000000000000001651501674766700255030ustar00rootroot00000000000000javac --release 8 -d classes src/org/jpype/sample_package/*.java jar --create --file sample_package.jar -C classes . jpype-1.6.0/project/jars/unicode_à😎/sample_package/src/000077500000000000000000000000001501674766700246355ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/sample_package/src/org/000077500000000000000000000000001501674766700254245ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/sample_package/src/org/jpype/000077500000000000000000000000001501674766700265535ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/sample_package/src/org/jpype/sample_package/000077500000000000000000000000001501674766700315075ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/sample_package/src/org/jpype/sample_package/A.java000066400000000000000000000000661501674766700325340ustar00rootroot00000000000000package org.jpype.sample_package; public class A { } jpype-1.6.0/project/jars/unicode_à😎/sample_package/src/org/jpype/sample_package/B.java000066400000000000000000000000661501674766700325350ustar00rootroot00000000000000package org.jpype.sample_package; public class B { } jpype-1.6.0/project/jars/unicode_à😎/service/000077500000000000000000000000001501674766700225525ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/README000066400000000000000000000001311501674766700234250ustar00rootroot00000000000000Test to verify that service providers are found and used when placed in a non ascii path jpype-1.6.0/project/jars/unicode_à😎/service/build.gradle000066400000000000000000000005241501674766700250320ustar00rootroot00000000000000apply plugin: 'java' apply plugin: 'eclipse' sourceCompatibility = 8 targetCompatibility = 8 tasks.withType(JavaCompile) { options.release = 8 options.debug = false } jar { destinationDirectory = file('dist') from ('./src/main/java') { include 'META-INF/services/java.time.zone.ZoneRulesProvider' } } build.dependsOn(jar) jpype-1.6.0/project/jars/unicode_à😎/service/src/000077500000000000000000000000001501674766700233415ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/000077500000000000000000000000001501674766700242655ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/java/000077500000000000000000000000001501674766700252065ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/java/org/000077500000000000000000000000001501674766700257755ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/java/org/jpype/000077500000000000000000000000001501674766700271245ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/java/org/jpype/service/000077500000000000000000000000001501674766700305645ustar00rootroot00000000000000JpypeZoneRulesProvider.java000066400000000000000000000012201501674766700360140ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/java/org/jpype/servicepackage org.jpype.service; import java.time.ZoneId; import java.time.zone.ZoneRules; import java.time.zone.ZoneRulesProvider; import java.util.Collections; import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; public class JpypeZoneRulesProvider extends ZoneRulesProvider { @Override protected Set provideZoneIds() { return Collections.singleton("JpypeTest/Timezone"); } @Override protected ZoneRules provideRules(String zoneId, boolean forCaching) { return ZoneId.of("UTC").getRules(); } @Override protected NavigableMap provideVersions(String zoneId) { return new TreeMap<>(); } } jpype-1.6.0/project/jars/unicode_à😎/service/src/main/resources/000077500000000000000000000000001501674766700262775ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/resources/META-INF/000077500000000000000000000000001501674766700274375ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/resources/META-INF/services/000077500000000000000000000000001501674766700312625ustar00rootroot00000000000000java.time.zone.ZoneRulesProvider000066400000000000000000000000511501674766700374120ustar00rootroot00000000000000jpype-1.6.0/project/jars/unicode_à😎/service/src/main/resources/META-INF/servicesorg.jpype.service.JpypeZoneRulesProvider jpype-1.6.0/project/jars/unsatisfied/000077500000000000000000000000001501674766700176135ustar00rootroot00000000000000jpype-1.6.0/project/jars/unsatisfied/build.sh000066400000000000000000000002501501674766700212430ustar00rootroot00000000000000javac --release 7 -d classes src/org/jpype/unsatisfied/*.java rm classes/org/jpype/unsatisfied/UnsatisfiedClass.class jar --create --file unsatisfied.jar -C classes . jpype-1.6.0/project/jars/unsatisfied/src/000077500000000000000000000000001501674766700204025ustar00rootroot00000000000000jpype-1.6.0/project/jars/unsatisfied/src/org/000077500000000000000000000000001501674766700211715ustar00rootroot00000000000000jpype-1.6.0/project/jars/unsatisfied/src/org/jpype/000077500000000000000000000000001501674766700223205ustar00rootroot00000000000000jpype-1.6.0/project/jars/unsatisfied/src/org/jpype/unsatisfied/000077500000000000000000000000001501674766700246365ustar00rootroot00000000000000jpype-1.6.0/project/jars/unsatisfied/src/org/jpype/unsatisfied/TestClass.java000066400000000000000000000004131501674766700274040ustar00rootroot00000000000000package org.jpype.unsatisfied; public class TestClass { public static void main(String[] args) { System.out.println("Hi Freaks"); } public static UnsatisfiedClass methodWithUnsatisfiedReturnType() { return null; } } class UnsatisfiedClass { } jpype-1.6.0/project/jpype_cpython/000077500000000000000000000000001501674766700172315ustar00rootroot00000000000000jpype-1.6.0/project/jpype_cpython/nbproject/000077500000000000000000000000001501674766700212175ustar00rootroot00000000000000jpype-1.6.0/project/jpype_cpython/nbproject/configurations.xml000077500000000000000000001232131501674766700250000ustar00rootroot00000000000000 ../../native/common/include/jp_array.h ../../native/common/include/jp_arrayclass.h ../../native/common/include/jp_booleantype.h ../../native/common/include/jp_boxedtype.h ../../native/common/include/jp_buffer.h ../../native/common/include/jp_buffertype.h ../../native/common/include/jp_bytetype.h ../../native/common/include/jp_chartype.h ../../native/common/include/jp_class.h ../../native/common/include/jp_classhints.h ../../native/common/include/jp_classloader.h ../../native/common/include/jp_classtype.h ../../native/common/include/jp_context.h ../../native/common/include/jp_doubletype.h ../../native/common/include/jp_encoding.h ../../native/common/include/jp_exception.h ../../native/common/include/jp_field.h ../../native/common/include/jp_floattype.h ../../native/common/include/jp_functional.h ../../native/common/include/jp_gc.h ../../native/common/include/jp_inttype.h ../../native/common/include/jp_javaframe.h ../../native/common/include/jp_longtype.h ../../native/common/include/jp_match.h ../../native/common/include/jp_method.h ../../native/common/include/jp_methoddispatch.h ../../native/common/include/jp_modifier.h ../../native/common/include/jp_monitor.h ../../native/common/include/jp_numbertype.h ../../native/common/include/jp_objecttype.h ../../native/common/include/jp_platform.h ../../native/common/include/jp_primitive_accessor.h ../../native/common/include/jp_primitivetype.h ../../native/common/include/jp_proxy.h ../../native/common/include/jp_reference_queue.h ../../native/common/include/jp_shorttype.h ../../native/common/include/jp_stringtype.h ../../native/common/include/jp_tracer.h ../../native/common/include/jp_typemanager.h ../../native/common/include/jp_value.h ../../native/common/include/jp_voidtype.h ../../native/common/include/jpype.h ../../native/python/include/jp_pythontypes.h ../../native/python/include/pyjp.h ../../jpype/__init__.py ../../jpype/_classpath.py ../../jpype/_core.py ../../jpype/_gui.py ../../jpype/_jarray.py ../../jpype/_jclass.py ../../jpype/_jcollection.py ../../jpype/_jcustomizer.py ../../jpype/_jexception.py ../../jpype/_jinit.py ../../jpype/_jio.py ../../jpype/_jmethod.py ../../jpype/_jobject.py ../../jpype/_jpackage.py ../../jpype/_jproxy.py ../../jpype/_jstring.py ../../jpype/_jthread.py ../../jpype/_jvmfinder.py ../../jpype/_pykeywords.py ../../jpype/beans.py ../../jpype/imports.py ../../jpype/nio.py ../../jpype/pickle.py ../../jpype/protocol.py ../../jpype/types.py ../../test/jpypetest/common.py ../../test/jpypetest/conftest.py ../../test/jpypetest/subrun.py ../../test/jpypetest/test_array.py ../../test/jpypetest/test_attr.py ../../test/jpypetest/test_boxed.py ../../test/jpypetest/test_buffer.py ../../test/jpypetest/test_bytebuffer.py ../../test/jpypetest/test_caller_sensitive.py ../../test/jpypetest/test_charSequence.py ../../test/jpypetest/test_classhints.py ../../test/jpypetest/test_closeable.py ../../test/jpypetest/test_closed.py ../../test/jpypetest/test_collection.py ../../test/jpypetest/test_comparable.py ../../test/jpypetest/test_conversion.py ../../test/jpypetest/test_conversionInt.py ../../test/jpypetest/test_conversionLong.py ../../test/jpypetest/test_conversionShort.py ../../test/jpypetest/test_core.py ../../test/jpypetest/test_coverage.py ../../test/jpypetest/test_customizer.py ../../test/jpypetest/test_directbuffer.py ../../test/jpypetest/test_docstring.py ../../test/jpypetest/test_exc.py ../../test/jpypetest/test_fault.py ../../test/jpypetest/test_fields.py ../../test/jpypetest/test_forname.py ../../test/jpypetest/test_generic.py ../../test/jpypetest/test_hash.py ../../test/jpypetest/test_hints.py ../../test/jpypetest/test_imports.py ../../test/jpypetest/test_inherit.py ../../test/jpypetest/test_javacoverage.py ../../test/jpypetest/test_javadoc.py ../../test/jpypetest/test_jboolean.py ../../test/jpypetest/test_jbyte.py ../../test/jpypetest/test_jchar.py ../../test/jpypetest/test_jclass.py ../../test/jpypetest/test_jdouble.py ../../test/jpypetest/test_jedi.py ../../test/jpypetest/test_jfloat.py ../../test/jpypetest/test_jint.py ../../test/jpypetest/test_jlong.py ../../test/jpypetest/test_jmethod.py ../../test/jpypetest/test_jobject.py ../../test/jpypetest/test_jpackage.py ../../test/jpypetest/test_jshort.py ../../test/jpypetest/test_jstring.py ../../test/jpypetest/test_jvmfinder.py ../../test/jpypetest/test_keywords.py ../../test/jpypetest/test_lambdas.py ../../test/jpypetest/test_leak.py ../../test/jpypetest/test_leak2.py ../../test/jpypetest/test_legacy.py ../../test/jpypetest/test_list.py ../../test/jpypetest/test_map.py ../../test/jpypetest/test_module.py ../../test/jpypetest/test_module2.py ../../test/jpypetest/test_mro.py ../../test/jpypetest/test_number.py ../../test/jpypetest/test_numeric.py ../../test/jpypetest/test_objectwrapper.py ../../test/jpypetest/test_opts.py ../../test/jpypetest/test_overloads.py ../../test/jpypetest/test_pickle.py ../../test/jpypetest/test_properties.py ../../test/jpypetest/test_proxy.py ../../test/jpypetest/test_proxy_multithreaded.py ../../test/jpypetest/test_ref.py ../../test/jpypetest/test_reflect.py ../../test/jpypetest/test_repr.py ../../test/jpypetest/test_serial.py ../../test/jpypetest/test_shutdown.py ../../test/jpypetest/test_startup.py ../../test/jpypetest/test_synchronized.py ../../test/jpypetest/test_thread.py ../../test/jpypetest/test_utf8.py ../../test/jpypetest/test_varargs.py ../../test/jpypetest/test_virtual.py ../../test/jpypetest/test_zzz.py ../../native/common/jp_array.cpp ../../native/common/jp_arrayclass.cpp ../../native/common/jp_booleantype.cpp ../../native/common/jp_boxedtype.cpp ../../native/common/jp_buffer.cpp ../../native/common/jp_buffertype.cpp ../../native/common/jp_bytetype.cpp ../../native/common/jp_chartype.cpp ../../native/common/jp_class.cpp ../../native/common/jp_classhints.cpp ../../native/common/jp_classloader.cpp ../../native/common/jp_classtype.cpp ../../native/common/jp_context.cpp ../../native/common/jp_convert.cpp ../../native/common/jp_doubletype.cpp ../../native/common/jp_encoding.cpp ../../native/common/jp_exception.cpp ../../native/common/jp_field.cpp ../../native/common/jp_floattype.cpp ../../native/common/jp_functional.cpp ../../native/common/jp_gc.cpp ../../native/common/jp_inttype.cpp ../../native/common/jp_javaframe.cpp ../../native/common/jp_longtype.cpp ../../native/common/jp_method.cpp ../../native/common/jp_methoddispatch.cpp ../../native/common/jp_monitor.cpp ../../native/common/jp_numbertype.cpp ../../native/common/jp_objecttype.cpp ../../native/common/jp_platform.cpp ../../native/common/jp_primitivetype.cpp ../../native/common/jp_proxy.cpp ../../native/common/jp_reference_queue.cpp ../../native/common/jp_shorttype.cpp ../../native/common/jp_stringtype.cpp ../../native/common/jp_tracer.cpp ../../native/common/jp_typefactory.cpp ../../native/common/jp_typemanager.cpp ../../native/common/jp_value.cpp ../../native/common/jp_voidtype.cpp ../../native/python/jp_pythontypes.cpp ../../native/python/pyjp_array.cpp ../../native/python/pyjp_buffer.cpp ../../native/python/pyjp_char.cpp ../../native/python/pyjp_class.cpp ../../native/python/pyjp_classhints.cpp ../../native/python/pyjp_field.cpp ../../native/python/pyjp_method.cpp ../../native/python/pyjp_module.cpp ../../native/python/pyjp_monitor.cpp ../../native/python/pyjp_number.cpp ../../native/python/pyjp_object.cpp ../../native/python/pyjp_package.cpp ../../native/python/pyjp_proxy.cpp ../../native/python/pyjp_value.cpp ../../native/common ../../native/python ../../jpype ../../test/jpypetest Makefile default true false 5 5 ../../native/jni_include ../../native/python/include ../../native/common/include 5 5 ../../native/python/include ../../native/common/include jpype-1.6.0/project/jpype_cpython/nbproject/license.txt000066400000000000000000000014551501674766700234070ustar00rootroot00000000000000<#if licenseFirst??> ${licenseFirst}**************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. <#if licenseLast??> ****************************************************************************${licenseLast} jpype-1.6.0/project/jpype_cpython/nbproject/project.properties000077500000000000000000000001461501674766700250070ustar00rootroot00000000000000#Fri Jun 19 07:55:24 PDT 2020 project.license=apache20 project.licensePath=./nbproject/license.txt jpype-1.6.0/project/jpype_cpython/nbproject/project.xml000077500000000000000000000024561501674766700234210ustar00rootroot00000000000000 org.netbeans.modules.cnd.makeproject jpype_cpython cpp h UTF-8 ../../native/common ../../native/python ../../jpype ../../test/jpypetest Release 3 true ANSI|ANSI Default_0|JPype Default_1|JPype jpype-1.6.0/project/jpype_java/000077500000000000000000000000001501674766700164665ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/build.xml000077500000000000000000000005711501674766700203150ustar00rootroot00000000000000 Builds, tests, and runs the project jpype_java. jpype-1.6.0/project/jpype_java/nbproject/000077500000000000000000000000001501674766700204545ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/nbproject/license.txt000066400000000000000000000014551501674766700226440ustar00rootroot00000000000000<#if licenseFirst??> ${licenseFirst}**************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. <#if licenseLast??> ****************************************************************************${licenseLast} jpype-1.6.0/project/jpype_java/nbproject/project.properties000077500000000000000000000117211501674766700242450ustar00rootroot00000000000000annotation.processing.enabled=true annotation.processing.enabled.in.editor=false annotation.processing.processors.list= annotation.processing.run.all.processors=true annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output application.title=jpype_java application.vendor= auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=4 auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=4 auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8 auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80 auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement=NEW_LINE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.enable-indent=true auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.expand-tabs=true auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.indent-shift-width=2 auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement=NEW_LINE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.moduleDeclBracePlacement=NEW_LINE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement=NEW_LINE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantForBraces=LEAVE_ALONE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantIfBraces=LEAVE_ALONE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantWhileBraces=LEAVE_ALONE auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaces-per-tab=2 auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.tab-size=8 auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width=80 auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-line-wrap=none build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: build.dir=build build.generated.dir=${build.dir}/generated build.generated.sources.dir=${build.dir}/generated-sources # Only compile against the classpath explicitly listed here: build.sysclasspath=ignore build.test.classes.dir=${build.dir}/test/classes build.test.results.dir=${build.dir}/test/results # Uncomment to specify the preferred debugger connection transport: #debug.transport=dt_socket debug.classpath=\ ${run.classpath} debug.modulepath=\ ${run.modulepath} debug.test.classpath=\ ${run.test.classpath} debug.test.modulepath=\ ${run.test.modulepath} # Files in build.classes.dir which should be excluded from distribution jar dist.archive.excludes= # This directory is removed when the project is cleaned: dist.dir=dist dist.jar=${dist.dir}/org.jpype.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= file.reference.native-java=../../native/java file.reference.test-harness=../../test/harness includes=** jar.compress=false javac.classpath= # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false javac.external.vm=true javac.modulepath= javac.processormodulepath= javac.processorpath=\ ${javac.classpath} javac.source=1.8 javac.target=1.8 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir}:\ ${libs.testng.classpath} javac.test.modulepath=\ ${javac.modulepath} javac.test.processorpath=\ ${javac.test.classpath} javadoc.additionalparam= javadoc.author=false javadoc.encoding=${source.encoding} javadoc.html5=false javadoc.noindex=false javadoc.nonavbar=false javadoc.notree=false javadoc.private=false javadoc.splitindex=true javadoc.use=true javadoc.version=false javadoc.windowtitle= jlink.launcher=false jlink.launcher.name=jpype_java meta.inf.dir=${src.dir}/META-INF mkdist.disabled=true platform.active=default_platform project.licensePath=./nbproject/license.txt run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} # Space-separated list of JVM arguments used when running the project. # You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. # To set system properties for unit tests define test-sys-prop.name=value: run.jvmargs= run.modulepath=\ ${javac.modulepath} run.test.classpath=\ ${javac.test.classpath}:\ ${build.test.classes.dir} run.test.modulepath=\ ${javac.test.modulepath} source.encoding=UTF-8 src.java.dir=${file.reference.native-java} test.harness.dir=${file.reference.test-harness} test.src.dir=test manifest.file=../../native/java/manifest.txt jpype-1.6.0/project/jpype_java/nbproject/project.xml000077500000000000000000000011311501674766700226430ustar00rootroot00000000000000 org.netbeans.modules.java.j2seproject jpype_java jpype-1.6.0/project/jpype_java/test/000077500000000000000000000000001501674766700174455ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/test/org/000077500000000000000000000000001501674766700202345ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/test/org/jpype/000077500000000000000000000000001501674766700213635ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/test/org/jpype/manager/000077500000000000000000000000001501674766700227755ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/test/org/jpype/manager/TestDefault.java000066400000000000000000000036441501674766700260730ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Method; import java.util.EnumSet; /** * * @author nelson85 */ public class TestDefault { public interface Foo { default void fred() { } void george(); static void bob() { } } static public void main(String[] args) { System.out.println("declared:"); for (Method method : int[].class.getDeclaredMethods()) System.out.println(" " + method); System.out.println("methods:"); for (Method method : int[].class.getMethods()) { System.out.println(" " + method); } System.out.println(int[][][].class.getCanonicalName()); System.out.println(int[][][].class.getName()); System.out.println(int[][][].class.getSimpleName()); System.out.println(Object[][][].class.getCanonicalName()); System.out.println(Object[][][].class.getName()); System.out.println(Object[][][].class.getSimpleName()); System.out.println(Object.class.getSimpleName()); System.out.println(Object.class.getName()); System.out.println(Object.class.getCanonicalName()); EnumSet set = EnumSet.of(ModifierCode.PUBLIC, ModifierCode.CTOR); System.out.println(set); } } jpype-1.6.0/project/jpype_java/test/org/jpype/manager/TestMethodResolution.java000066400000000000000000000026401501674766700300060ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.util.Arrays; import java.util.List; /** * * @author Karl Einar Nelson */ public class TestMethodResolution { static class Foo { public static void bar(Object obj, int i) { } public void bar(int i) { } } static public void main(String[] args) throws NoSuchMethodException { List foo = MethodResolution .sortMethods(Arrays.asList(TestMethodResolution.Foo.class.getDeclaredMethods())); for (MethodResolution m : foo) { System.out.println(m.executable); for (MethodResolution m1 : m.children) { System.out.println(" " + m1.executable); } } } } jpype-1.6.0/project/jpype_java/test/org/jpype/manager/TestTypeManager.java000066400000000000000000000046221501674766700267200ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import org.jpype.manager.TypeFactoryHarness.DeletedResource; import org.jpype.manager.TypeFactoryHarness.Resource; /** * * @author nelson85 */ public class TestTypeManager { public interface MyFunction { Object apply(Object o); } public static class MyBase { void run() { } } static public void main(String[] args) { System.out.println("Create:"); TypeManager tm = new TypeManager(); TypeFactoryHarness tf = new TypeFactoryHarness(tm); tm.typeFactory = tf; System.out.println("Initialize:"); tm.init(); System.out.println(); System.out.println("=================================================="); tm.findClass(int[][][].class); MyFunction f = (Object o) -> o; tm.findClass(f.getClass()); MyFunction f2 = new MyFunction() { @Override public Object apply(Object t) { return t; } }; tm.findClass(f2.getClass()); MyBase f3 = new MyBase() { void run() { } }; tm.findClass(f3.getClass()); System.out.println("=================================================="); System.out.println(); System.out.println("Shutdown:"); tm.shutdown(); System.out.println("Leaked resources:"); int leaked = 0; for (Resource entry : tf.resourceMap.values()) { if (entry instanceof DeletedResource) continue; System.out.println(" " + entry.getName()); leaked++; } System.out.println("Resource created " + tf.value); // was 8803, reduced to 1149 System.out.println("Leaked total " + leaked); if (leaked > 0) throw new RuntimeException("Leaked resources"); } } jpype-1.6.0/project/jpype_java/test/org/jpype/manager/TypeFactoryHarness.java000066400000000000000000000346651501674766700274530ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.manager; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; /** * A special version of the TypeFactory for debugging and testing. *

* This harness operates like JPype C++ layer with checks for problems and * inconsistencies that may indicate a problem with the TypeManager. * * @author nelson85 */ public class TypeFactoryHarness implements TypeFactory, TypeAudit { public long value = 0; HashMap resourceMap = new HashMap<>(); private final TypeManager typeManager; TypeFactoryHarness(TypeManager tm) { this.typeManager = tm; // Register as the audit for the TypeManager tm.audit = this; } T assertResource(long id, Class cls) { Resource resource = resourceMap.get(id); if (resource == null) throw new RuntimeException("Resource not found " + id + " " + cls.getName()); if (!cls.isInstance(resource)) throw new RuntimeException("Incorrect resource type " + id + " expected: " + cls.getName() + " found: " + resource.getClass().getName()); return (T) resource; } // @Override public long defineArrayClass( long context, Class cls, String name, long superClass, long componentPtr, int modifiers) { value++; System.out.println("defineArrayClass " + value + ": " + name); System.out.println(" modifiers: " + ModifierCode.decode(modifiers)); Resource resource = new ArrayClassResource(value, "array class " + name, cls); resourceMap.put(value, resource); return value; } @Override public long defineObjectClass(long context, Class cls, String name, long superClass, long[] interfaces, int modifiers) { value++; System.out.println("defineObjectClass " + value + ": " + name); System.out.println(" modifiers: " + ModifierCode.decode(modifiers)); if (superClass != 0) { ClassResource superClassResource = assertResource(superClass, ClassResource.class); System.out.println(" super: " + superClass + " " + superClassResource.getName()); } this.dumpResourceList("interfaces", interfaces); Resource resource = new ObjectClassResource(value, "object class " + name, cls); resourceMap.put(value, resource); return value; } @Override public long definePrimitive(long context, String name, Class cls, long boxedPtr, int modifiers) { value++; System.out.println("defineObjectClass " + value + ": " + cls.toString()); System.out.println(" modifiers: " + ModifierCode.decode(modifiers)); Resource resource = new PrimitiveClassResource(value, "primitive class " + cls.getName(), cls); resourceMap.put(value, resource); return value; } // // @Override public void assignMembers( long context, long classId, long ctorMethod, long[] methodList, long[] fieldList) { // Verify resources ClassResource classResource = assertResource(classId, ClassResource.class); try { if (ctorMethod != 0) assertResource(ctorMethod, MethodDispatchResource.class); for (int i = 0; i < methodList.length; ++i) assertResource(methodList[i], MethodDispatchResource.class); for (int i = 0; i < fieldList.length; ++i) assertResource(fieldList[i], FieldResource.class); System.out.println("assignMembers " + classId + " " + classResource.getName()); } catch (Exception ex) { System.out.println("Fail in assignClass"); ClassResource r = (ClassResource) this.resourceMap.get(classId); dump(typeManager.classMap.get(r.getResource())); throw ex; } } @Override public long defineField( long context, long classId, String name, Field field, long fieldTypeId, int modifiers) { ClassResource classResource = assertResource(classId, ClassResource.class); ClassResource fieldResource = assertResource(fieldTypeId, ClassResource.class); value++; System.out.println("defineField " + value + ":" + name); System.out.println(" modifiers: " + ModifierCode.decode(modifiers)); System.out.println(" class: " + classResource.getName()); System.out.println(" fieldType: " + fieldResource.getName()); resourceMap.put(value, new FieldResource(value, "field " + field.getName(), field)); return value; } @Override public long defineMethod( long context, long classId, String name, Executable method, long[] overloadList, int modifiers) { // Verify resources assertResource(classId, ClassResource.class); for (int i = 0; i < overloadList.length; ++i) assertResource(overloadList[i], MethodResource.class); value++; System.out.println("defineMethod " + value + ": " + name); System.out.println(" modifiers: " + ModifierCode.decode(modifiers)); System.out.flush(); resourceMap.put(value, new MethodResource(value, "method " + method.toString(), method)); return value; } @Override public void populateMethod( long context, long method, long returnType, long[] argumentTypes) { assertResource(returnType, MethodResource.class); if (returnType != 0) assertResource(returnType, ClassResource.class); for (int i = 0; i < argumentTypes.length; ++i) assertResource(argumentTypes[i], ClassResource.class); } @Override public long defineMethodDispatch( long context, long classId, String name, long[] overloadList, int modifiers) { ClassResource classResource = assertResource(classId, ClassResource.class); for (int i = 0; i < overloadList.length; ++i) assertResource(overloadList[i], MethodResource.class); value++; System.out.println("defineMethodDispatch " + value + ": '" + name + "' for " + classResource.getName()); System.out.println(" modifiers: " + ModifierCode.decode(modifiers)); this.dumpResourceList("members", overloadList); resourceMap.put(value, new MethodDispatchResource(value, "dispatch " + name)); return value; } // // Resource lastDestroyed = null; @Override public void destroy(long context, long[] resources, int sz) { System.out.println("destroy resources " + sz); for (int i = 0; i < sz; ++i) { long r = resources[i]; if (this.resourceMap.containsKey(r)) { // if (r==0) // continue; System.out.println(" destroy " + r + ": " + this.resourceMap.get(r).getName()); lastDestroyed = this.resourceMap.remove(r); if (!(lastDestroyed instanceof DeletedResource)) { this.resourceMap.put(r, new DeletedResource(lastDestroyed)); continue; } } if (lastDestroyed != null) { if (lastDestroyed instanceof MethodResource) { Class cls = ((MethodResource) lastDestroyed).getResource().getDeclaringClass(); dump(this.typeManager.classMap.get(cls)); } } throw new RuntimeException("repeat delete " + r + " at index " + i + " last " + lastDestroyed); } } // // @Override public void dump(ClassDescriptor desc) { System.out.println("ClassDescriptor: " + desc.classPtr); System.out.println(" class: " + desc.cls.toString()); System.out.println(" methodCounter:" + desc.methodCounter); if (desc.constructorDispatch != 0) { System.out.println(" ctor dispatch:"); Resource resource = this.resourceMap.get(desc.constructorDispatch); if (resource == null) System.out.println(" null"); else System.out.println(" " + resource.getEntityId() + " " + resource.getName()); } dumpResourceList("ctors", desc.constructors); dumpResourceList("method dispatch", desc.methodDispatch); dumpResourceList("methods", desc.methods); dumpResourceList("fields", desc.fields); } private void dumpResourceList(String name, long[] resourceIds) { if (resourceIds == null) return; { System.out.println(" " + name + ": " + resourceIds.length); for (long l : resourceIds) { Resource resource = this.resourceMap.get(l); if (resource == null) System.out.println(" null"); else System.out.println(" " + resource.getEntityId() + " " + resource.getName()); } } } @Override public void verifyMembers(ClassDescriptor desc) { StringBuffer failures = new StringBuffer(); String nl = System.lineSeparator(); System.out.println("Verify " + desc.cls.toString()); if (desc.methodCounter != desc.methods.length) { failures.append(" Method counter inconsistency").append(nl); } if (desc.constructorDispatch == 0 && desc.constructors != null) { failures.append(" Constructor dispatch missing").append(nl); } if (desc.constructors != null) { for (long l : desc.constructors) { if (l != 0) continue; failures.append(" Constructor is null").append(nl); break; } } for (long l : desc.methodDispatch) { if (l != 0) continue; failures.append(" Method dispatch is null").append(nl); break; } HashSet methodSet = new HashSet<>( Collections.unmodifiableList( TypeManager.filterOverridden(desc.cls, desc.cls.getDeclaredMethods()))); for (long l : desc.methods) { if (l == 0) { failures.append(" Method is null").append(nl); break; } methodSet.remove(assertResource(l, MethodResource.class).getResource()); } if (!methodSet.isEmpty()) { for (Executable e : methodSet) { failures.append(" Unaccounted method ").append(e.toString()).append(nl); } } String s = failures.toString(); if (s.isEmpty()) return; System.out.println(s); dump(desc); try { Thread.sleep(200); } catch (InterruptedException ex) { throw new RuntimeException(ex); } throw new RuntimeException("Verify failed"); } @Override public void failFindMethod(ClassDescriptor desc, Method requestedMethod) { System.out.println("Failed to find method:"); System.out.println(" requested: " + requestedMethod.toString()); System.out.println(" class: " + desc.cls.getName()); System.out.println(" declaring class: " + requestedMethod.getDeclaringClass()); System.out.println(" methods: " + desc.methodIndex.length); for (int i = 0; i < desc.methodIndex.length; ++i) { if (desc.methodIndex[i] == null) System.out.println(" null"); else System.out.println(" " + desc.methodIndex[i].toString() + " " + (desc.methodIndex[i].equals(requestedMethod))); } System.out.println(" declared methods:"); for (Method dmethod : desc.cls.getDeclaredMethods()) { System.out.println(" " + dmethod.toString()); } try { Thread.sleep(200); } catch (InterruptedException ex) { throw new RuntimeException(ex); } throw new RuntimeException("method not found " + requestedMethod); } // // @Override public void newWrapper(long context, long cls) { } public interface Resource { String getName(); Object getResource(); long getEntityId(); } public static class BaseResource implements Resource { private String name; private T object; private long id; BaseResource(long id, String name, T object) { this.id = id; this.name = name; this.object = object; } @Override public String getName() { return this.name; } @Override public T getResource() { return this.object; } @Override public long getEntityId() { return this.id; } } public static class ClassResource extends BaseResource { private ClassResource(long id, String name, Class cls) { super(id, name, cls); } } public static class ArrayClassResource extends ClassResource { private ArrayClassResource(long id, String name, Class cls) { super(id, name, cls); } } public static class ObjectClassResource extends ClassResource { private ObjectClassResource(long id, String name, Class cls) { super(id, name, cls); } } public static class PrimitiveClassResource extends ClassResource { private PrimitiveClassResource(long id, String name, Class cls) { super(id, name, cls); } } public static class FieldResource extends BaseResource { private FieldResource(long id, String name, Field cls) { super(id, name, cls); } } public static class MethodResource extends BaseResource { private MethodResource(long id, String name, Executable cls) { super(id, name, cls); } } public static class MethodDispatchResource extends BaseResource { private MethodDispatchResource(long id, String name) { super(id, name, null); } } public static class DeletedResource extends BaseResource { private DeletedResource(Resource resource) { super(resource.getEntityId(), "deleted " + resource.getName(), resource); } } // } jpype-1.6.0/project/jpype_java/test/org/jpype/pkg/000077500000000000000000000000001501674766700221445ustar00rootroot00000000000000jpype-1.6.0/project/jpype_java/test/org/jpype/pkg/JPypePackageNGTest.java000066400000000000000000000050661501674766700264060ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pkg; import java.net.URI; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import static org.testng.Assert.*; import org.testng.annotations.Test; /** * * @author nelson85 */ public class JPypePackageNGTest { public JPypePackageNGTest() { } @Test public void testGetPackage() { assertTrue(JPypePackageManager.isPackage("java.lang")); JPypePackage pkg = new JPypePackage("java.lang"); for (Map.Entry e : pkg.contents.entrySet()) { Path p = JPypePackageManager.getPath(e.getValue()); JPypePackage.isPublic(p); } } @Test public void testBase() { assertTrue(JPypePackageManager.isPackage("java")); JPypePackage pkg = new JPypePackage("java"); for (Map.Entry e : pkg.contents.entrySet()) { System.out.println(e.getKey()); } } @Test public void testOrg() { assertTrue(JPypePackageManager.isPackage("org")); JPypePackage pkg = new JPypePackage("org"); for (Map.Entry e : pkg.contents.entrySet()) { System.out.println(e.getKey()); } } /** * Test of getObject method, of class JPypePackage. */ @Test public void testGetObject() { System.out.println("getObject"); JPypePackage instance = new JPypePackage("java.lang"); Object expResult = Object.class; Object result = instance.getObject("Object"); assertEquals(result, expResult); } /** * Test of getContents method, of class JPypePackage. */ @Test public void testGetContents() { System.out.println("getContents"); JPypePackage instance = new JPypePackage("java.lang"); String[] expResult = new String[] { "Enum", "ClassValue", "String" }; String[] result = Arrays.copyOfRange(instance.getContents(), 0, 3); assertEquals(result, expResult); } } jpype-1.6.0/project/jpype_java/test/org/jpype/pkg/ListPackage.java000066400000000000000000000021131501674766700251730ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.pkg; /** * * @author nelson85 */ public class ListPackage { public static void main(String[] args) { JPypePackage pkg = new JPypePackage("java.lang"); System.out.println(pkg.contents.size()); for (String s : pkg.getContents()) { System.out.println(s); } System.out.println(pkg.getObject("Class")); } } jpype-1.6.0/project/snippet/000077500000000000000000000000001501674766700160205ustar00rootroot00000000000000jpype-1.6.0/project/snippet/assertRaises.py000066400000000000000000000011131501674766700210360ustar00rootroot00000000000000 class AssertRaises(object): def __init__(self, expected): self.expected = expected def __enter__(self): return self def __exit__(self, exc_type, exc_value, tb): if exc_type is None: try: exc_name = self.expected.__name__ except AttributeError: exc_name = str(self.expected) raise AssertionError("{0} not raised".format(exc_name)) if not issubclass(exc_type, self.expected): # let unexpected exceptions pass through return False return True jpype-1.6.0/project/utility/000077500000000000000000000000001501674766700160415ustar00rootroot00000000000000jpype-1.6.0/project/utility/alter.py000066400000000000000000000022021501674766700175160ustar00rootroot00000000000000# Utility used to alter a Java jar file to date it in the future. # This was needed to intentionally trigger a version error. import jpype import jpype.imports jpype.startJVM() from java.util.jar import JarOutputStream from java.util.jar import JarInputStream from java.util.zip import CRC32 from java.io import File from java.io import FileInputStream from java.io import FileOutputStream jar = JarInputStream(FileInputStream(File("build/lib/org.jpype.jar"))) manifest = jar.getManifest() target = JarOutputStream(FileOutputStream(File("build/lib/org.jpype2.jar")), manifest) while 1: entry = jar.getNextEntry() if not entry: break out = [] l3 = 512 while 1: bt = jpype.JArray(jpype.JByte)(l3) l = jar.read(bt, 0, l3) if l == -1: break out.append((l, bt)) if out: out[0][1][7] = 57 crc = CRC32() for v in out: crc.update(v[1], 0, v[0]) entry.setCrc(crc.getValue()) entry.setCompressedSize(-1) target.putNextEntry(entry) for v in out: target.write(v[1], 0, v[0]) target.closeEntry() target.close() jpype-1.6.0/project/utility/remove.py000066400000000000000000000013531501674766700177120ustar00rootroot00000000000000# This script was used to scrub the harness of old code # First a set of sed patterns found all the harness classes # that are in used directly. They were moved out of place using, # this script, then those that were used indirectly were moved # back in place. import glob import re import os with open("keep", "r") as fd: lines = fd.readlines() lines = [i.strip() for i in lines] keep = set(lines) for fl in glob.iglob('harness/**/*.java', recursive=True): m = re.match('.*(jpype.*).java', fl) name = m.group(1).replace('/', '.') if name in keep: continue m = re.match('(.*)/[^/]*.java', fl) os.makedirs(os.path.join("old", m.group(1)), exist_ok=True) os.rename(fl, os.path.join("old", fl)) jpype-1.6.0/project/utility/testDoc.py000066400000000000000000000040011501674766700200130ustar00rootroot00000000000000import _jpype import jpype from jpype.types import * import jpype.imports jpype.startJVM(classpath=['gson-2.8.5.jar', 'gson-2.8.5-javadoc.jar', 'project/jpype_java/dist/*', 'project/jpype_java/jdk-11.0.7_doc-all.zip']) import org html = JClass("org.jpype.html.Html") hw = JClass("org.jpype.html.HtmlWriter") #jdz = JClass("org.jpype.javadoc.JavadocZip")(Paths.get("project/jpype_java/jdk-8u251-docs-all.zip")) jde = JClass("org.jpype.javadoc.JavadocExtractor") #jdf = JClass("org.jpype.javadoc.JavadocTransformer")() #jdr = JClass("org.jpype.javadoc.JavadocRenderer")() current = None def renderClass(cls): global current jd = jde.getDocumentation(cls) #jis = jdz.getInputStream(cls) if jd is None: return print("=========================================================") print("CLASS", cls) print(jd.description) print(jd.ctors) print("---------------------------------------------------------") if jd.methods is not None: for p, v in jd.methods.items(): print(v) print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - -") print("---------------------------------------------------------") if jd.fields is not None: for p, v in jd.fields.items(): print(v) print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - -") print("=========================================================") def renderPackage(pkg): for i in dir(pkg): print("Test", i) try: p = getattr(pkg, i) except Exception: continue if isinstance(p, _jpype._JPackage): renderPackage(p) continue if isinstance(p, jpype.JClass): renderClass(p) try: renderPackage(jpype.JPackage('com.google.gson')) except org.jpype.javadoc.JavadocException as ex: print("Javadoc Error: ", ex.message()) print(hw.asString(ex.node)) raise ex except Exception as ex: print("Error in") print(hw.asString(current)) raise ex jpype-1.6.0/project/utility/testMemoryExh.py000066400000000000000000000050511501674766700212310ustar00rootroot00000000000000# This is the audit script for checking the GC linker. # # It beats to memory to see if everything holds up, but not suitable for # the test suite. It is run in conjuction with the printf statement in # jp_gc.cpp to see how aggressive we are being about running the gc. # The goal is to keep the memory as low as possible while still maintaining # good speed. It has to be checked with 3 different block sizes to # verify function (<1k, 10k, >1Mb) as different behaviors occur at different # usage points. from os import path import _jpype import jpype from jpype.types import * import numpy as np import gc import time # print(gc.callbacks) # # def callHook(*args): # jpype.java.lang.System.gc() # # gc.callbacks.append(callHook) trials = 200000 tally = np.zeros((trials,), dtype=np.int8) class DestructionTracker: del_calls = 0 init_calls = 0 def __init__(self, i, obj): self.index = i self.obj = obj DestructionTracker.init_calls += 1 tally[i] = 1 super().__init__() def __del__(self): tally[self.index] += 1 DestructionTracker.del_calls += 1 def callback(self, message): pass if __name__ == '__main__': jpype.startJVM(classpath=['test/classes', 'project/jpype_java/dist/*'], convertStrings=True) print() kB = (1024 / 8) MB = (1024**2 / 8) fixture = JClass("jpype.common.Fixture")() for i in range(trials): x = np.arange(int(10 * kB), dtype=np.int64) interface = jpype.JProxy("java.io.Serializable", dict={'callback': DestructionTracker(i, x).callback}) interface_container = fixture.callObject(interface) if (i % 1000) == 0: stats = _jpype.gcStats() print("created=", DestructionTracker.init_calls, " destroyed=", DestructionTracker.del_calls, " delta=", DestructionTracker.init_calls - DestructionTracker.del_calls, " current=", stats['current'], " min=", stats['min'], " max=", stats['max'], " triggered=", stats['triggered'], ) time.sleep(1) # print(_jpype.gcStats()) del interface, interface_container # if DestructionTracker.del_calls != 0: # print(f'{i} We have deleted something: {DestructionTracker.del_calls}') # else: # print(f'{i} Still no deletion on it {i}\n', end="") print() print(np.sum(tally == 2)) jpype.shutdownJVM() print(np.sum(tally == 2)) jpype-1.6.0/pyproject.toml000066400000000000000000000026651501674766700156150ustar00rootroot00000000000000[build-system] requires = [ "setuptools", ] build-backend = "setuptools.build_meta" [project] name = "jpype1" version = '1.6.0' authors = [ {name = "Steve Menard", email = "devilwolf@users.sourceforge.net"}, ] maintainers = [ {name = "Luis Nell", email = "cooperate@originell.org"}, ] description = "A Python to Java bridge" readme = "README.rst" requires-python = ">=3.8" license = {text = "License :: OSI Approved :: Apache Software License"} classifiers = [ 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Topic :: Software Development', 'Topic :: Scientific/Engineering', ] dependencies = [ 'packaging', ] [project.optional-dependencies] docs = [ 'readthedocs-sphinx-ext', 'sphinx', 'sphinx-rtd-theme', ] tests = [ "pytest", ] [project.entry-points.pyinstaller40] "hook-dirs" = "jpype._pyinstaller.entry_points:get_hook_dirs" "tests" = "jpype._pyinstaller.entry_points:get_PyInstaller_tests" [project.urls] homepage = "https://github.com/jpype-project/jpype" [[tool.mypy.overrides]] module = [ "_jpype", "commonx", "brokenx", "java.*", "jpypex.common", "jedi", "jedi.access", ] ignore_missing_imports = true jpype-1.6.0/resolve.sh000066400000000000000000000005361501674766700147070ustar00rootroot00000000000000#!/bin/sh # This is used to pull dependencies needed for this package # drill has a bunch of dependencies, so we are going to use ivy to collect it all at once. wget -nc "https://repo1.maven.org/maven2/org/apache/ivy/ivy/2.5.0/ivy-2.5.0.jar" -P lib java -jar lib/ivy-2.5.0.jar -ivy ivy.xml -retrieve 'lib/[artifact]-[revision](-[classifier]).[ext]' jpype-1.6.0/setup.cfg000066400000000000000000000001161501674766700145070ustar00rootroot00000000000000[tool:pytest] addopts = --verbose testpaths = test jpype/_pyinstaller jpype-1.6.0/setup.py000066400000000000000000000052201501674766700144010ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys from pathlib import Path from setuptools import Extension from setuptools import setup # Add our setupext package to the path, and import it. sys.path.append(str(Path(__file__).parent)) import setupext if '--android' in sys.argv: platform = 'android' sys.argv.remove('--android') else: platform = sys.platform jpypeLib = Extension(name='_jpype', **setupext.platform.Platform( include_dirs=[Path('native', 'common', 'include'), Path('native', 'python', 'include')], sources=sorted( list(Path('native', 'common').glob('*.cpp')) + list(Path('native', 'python').glob('*.cpp')) ), platform=platform, )) p = [ i for i in Path("native", "jpype_module", "src", "main", "java").glob("**/*.java")] javaSrc = [i for i in p if not "exclude" in str(i)] jpypeJar = Extension(name="org.jpype", sources=sorted(map(str, javaSrc)), language="java", libraries=["lib/asm-8.0.1.jar"] ) setup( # Non-standard, and extension behaviour of setup() - project information # should be put in pyproject.toml wherever possible. See also: # https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration platforms=[ 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', 'Operating System :: Unix', 'Operating System :: MacOS', ], packages=['jpype', 'jpype._pyinstaller'], package_dir={'jpype': 'jpype', }, package_data={'jpype': ['*.pyi']}, cmdclass={ 'build_ext': setupext.build_ext.BuildExtCommand, 'develop': setupext.develop.Develop, 'test_java': setupext.test_java.TestJavaCommand, 'sdist': setupext.sdist.BuildSourceDistribution, }, zip_safe=False, ext_modules=[jpypeLib, jpypeJar], ) jpype-1.6.0/setupext/000077500000000000000000000000001501674766700145515ustar00rootroot00000000000000jpype-1.6.0/setupext/__init__.py000066400000000000000000000015531501674766700166660ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from . import platform from . import build_ext from . import develop from . import test_java from . import sdist jpype-1.6.0/setupext/build_ext.py000066400000000000000000000307701501674766700171110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import distutils.cmd import distutils.log import glob import os import shlex import shutil import subprocess from distutils.dir_util import copy_tree from distutils.errors import DistutilsPlatformError from setuptools.command.build_ext import build_ext # This setup option constructs a prototype Makefile suitable for compiling # the _jpype extension module. It is intended to help with development # of the extension library on unix systems. This works only on unix systems. # # To create a Makefile use # python setup.py build_ext --makefile # # Then edit with the desired options class FeatureNotice(Warning): """ indicate notices about features """ class Makefile: compiler_type = "unix" def __init__(self, actual): self.actual = actual self.compile_command = None self.compile_pre = None self.compile_post = None self.objects = [] self.sources = [] def captureCompile(self, x): command = x[0] x = x[1:] includes = [i for i in x if i.startswith("-I")] x = [i for i in x if not i.startswith("-I")] i0 = None i1 = None for i, v in enumerate(x): if v == '-c': i1 = i elif v == '-o': i0 = i pre = set(x[:i1]) post = x[i0 + 2:] self.compile_command = command self.compile_pre = pre self.compile_post = post self.includes = includes if not "bootstrap" in x[i1 + 1]: self.sources.append(x[i1 + 1]) def captureLink(self, x): self.link_command = x[0] x = x[1:] i = x.index("-o") self.library = x[i + 1] del x[i] del x[i] self.objects = [i for i in x if i.endswith(".o")] self.link_options = [i for i in x if not i.endswith(".o")] u = self.objects[0].split("/") self.build_dir = "/".join(u[:2]) def compile(self, *args, **kwargs): self.actual.spawn = self.captureCompile rc = self.actual.compile(*args, **kwargs) return rc def _need_link(self, *args): return True def link_shared_object(self, *args, **kwargs): self.actual._need_link = self._need_link self.actual.spawn = self.captureLink rc = self.actual.link_shared_object(*args, **kwargs) self.write() return rc def detect_language(self, x): return self.actual.detect_language(x) def write(self): print("Write makefile") library = os.path.basename(self.library) link_command = self.link_command compile_command = self.compile_command compile_pre = " ".join(list(self.compile_pre)) compile_post = " ".join(list(self.compile_post)) build = self.build_dir link_flags = " ".join(self.link_options) includes = " ".join(self.includes) sources = " \\\n ".join(self.sources) with open("Makefile", "w") as fd: print("LIB = %s" % library, file=fd) print("CC = %s" % compile_command, file=fd) print("LINK = %s" % link_command, file=fd) print("CFLAGS = %s %s" % (compile_pre, compile_post), file=fd) print("INCLUDES = %s" % includes, file=fd) print("BUILD = %s" % build, file=fd) print("LINKFLAGS = %s" % link_flags, file=fd) print("SRCS = %s" % sources, file=fd) print(""" all: $(LIB) rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) #build/src/jp_thunk.cpp: $(call rwildcard,native/java,*.java) # python setup.py build_thunk DEPDIR = build/deps $(DEPDIR): ; @mkdir -p $@ DEPFILES := $(SRCS:%.cpp=$(DEPDIR)/%.d) deps: $(DEPFILES) %/: echo $@ $(DEPDIR)/%.d: %.cpp mkdir -p $(dir $@) $(CC) $(INCLUDES) -MT $(patsubst $(DEPDIR)%,'$$(BUILD)%',$(patsubst %.d,%.o,$@)) -MM $< -o $@ OBJS = $(addprefix $(BUILD)/, $(SRCS:.cpp=.o)) $(BUILD)/%.o: %.cpp mkdir -p $(dir $@) $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ $(LIB): $(OBJS) $(LINK) $(OBJS) $(LINKFLAGS) -o $@ -include $(DEPFILES) """, file=fd) # Customization of the build_ext class BuildExtCommand(build_ext): """ Override some behavior in extension building: 1. handle compiler flags for different compilers via a dictionary. 2. try to disable warning -Wstrict-prototypes is valid for C/ObjC but not for C++ """ user_options = build_ext.user_options + [ ('enable-build-jar', None, 'Build the java jar portion'), ('enable-tracing', None, 'Set for tracing for debugging'), ('enable-coverage', None, 'Instrument c++ code for code coverage measuring'), ('android', None, 'configure for android'), ('makefile', None, 'Build a makefile for extensions'), ('jar', None, 'Build the jar only'), ] def initialize_options(self, *args): """omit -Wstrict-prototypes from CFLAGS since its only valid for C code.""" self.enable_tracing = False self.enable_build_jar = False self.enable_coverage = False self.android = False self.makefile = False self.jar = False import distutils.sysconfig cfg_vars = distutils.sysconfig.get_config_vars() # Arguments to remove so we set debugging and optimization level remove_args = ['-O0', '-O1', '-O2', '-O3', '-g'] for k, v in cfg_vars.items(): if not isinstance(v, str): continue if not k == "OPT" and "FLAGS" not in k: continue args = v.split() args = [arg for arg in args if arg not in remove_args] cfg_vars[k] = " ".join(args) super().initialize_options() def _set_cflags(self): # set compiler flags c = self.compiler.compiler_type jpypeLib = [i for i in self.extensions if i.name == '_jpype'][0] if c == 'unix' and self.enable_coverage: jpypeLib.extra_compile_args.extend( ['-ggdb', '--coverage', '-ftest-coverage']) jpypeLib.extra_compile_args = [ '-O0' if x == '-O2' else x for x in jpypeLib.extra_compile_args] jpypeLib.extra_link_args.extend(['--coverage']) if c == 'unix' and self.enable_tracing: jpypeLib.extra_compile_args = [ '-O0' if x == '-O2' else x for x in jpypeLib.extra_compile_args] def build_extensions(self): if self.makefile: self.compiler = Makefile(self.compiler) self.force = True jpypeLib = [i for i in self.extensions if i.name == '_jpype'][0] self._set_cflags() if self.enable_tracing: jpypeLib.define_macros.append(('JP_TRACING_ENABLE', 1)) if self.enable_coverage: jpypeLib.define_macros.append(('JP_INSTRUMENTATION', 1)) super().build_extensions() def build_extension(self, ext): if ext.language == "java": return self.build_java_ext(ext) if self.jar: return print("Call build ext") return super().build_extension(ext) def copy_extensions_to_source(self): build_py = self.get_finalized_command('build_py') for ext in self.extensions: if ext.language == "java": fullname = self.get_ext_fullname("JAVA") filename = ext.name + ".jar" else: fullname = self.get_ext_fullname(ext.name) filename = self.get_ext_filename(fullname) modpath = fullname.split('.') package = '.'.join(modpath[:-1]) package_dir = build_py.get_package_dir(package) dest_filename = os.path.join(package_dir, os.path.basename(filename)) src_filename = os.path.join(self.build_lib, filename) # Always copy, even if source is older than destination, to ensure # that the right extensions for the current Python/platform are # used. distutils.file_util.copy_file( src_filename, dest_filename, verbose=self.verbose, dry_run=self.dry_run ) if ext._needs_stub: self.write_stub(package_dir or os.curdir, ext, True) def build_java_ext(self, ext): """Run command.""" java = self.enable_build_jar javac = "javac" try: if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'javac')): javac = '"%s"' % os.path.join( os.environ['JAVA_HOME'], 'bin', 'javac') except KeyError: pass jar = "jar" try: if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar')): jar = '"%s"' % os.path.join( os.environ['JAVA_HOME'], 'bin', 'jar') except KeyError: pass # Try to use the cache if we are not requested build if not java: src = os.path.join('native', 'jars') dest = os.path.dirname(self.get_ext_fullpath("JAVA")) if os.path.exists(src): distutils.log.info("Using Jar cache") copy_tree(src, dest) return classpath = "." if ext.libraries: classpath = os.path.pathsep.join(ext.libraries) distutils.log.info( "Jar cache is missing, using --enable-build-jar to recreate it.") target_version = "11" # build the jar try: dirname = os.path.dirname(self.get_ext_fullpath("JAVA")) jarFile = os.path.join(dirname, ext.name + ".jar") build_dir = os.path.join(self.build_temp, ext.name, "classes") os.makedirs(build_dir, exist_ok=True) os.makedirs(dirname, exist_ok=True) cmd1 = shlex.split('%s -cp "%s" -d "%s" -g:none -source %s -target %s -encoding UTF-8' % (javac, classpath, build_dir, target_version, target_version)) cmd1.extend(ext.sources) self.announce(" %s" % " ".join(cmd1), level=distutils.log.INFO) subprocess.check_call(cmd1) cmd1 = shlex.split('%s -cp "%s" -d "%s" -g:none -source %s -target %s -encoding UTF-8' % (javac, build_dir, os.path.join(build_dir, "META-INF", "versions", "0"), target_version, target_version)) cmd1.extend(glob.glob(os.path.join( "native", "jpype_module","src","main","java","exclude", "**", "*.java"), recursive=True)) os.makedirs(os.path.join(build_dir, "META-INF", "versions", "0"), exist_ok=True) self.announce(" %s" % " ".join(cmd1), level=distutils.log.INFO) subprocess.check_call(cmd1) manifest = None try: for file in glob.iglob("native/jpype_module/src/main/java/**/*.*", recursive=True): if file.endswith("manifest.txt"): manifest = file continue if file.endswith(".java") or os.path.isdir(file): continue p = os.path.join( build_dir, os.path.relpath(file, "native/jpype_module/src/main/java")) print("Copy file", file, p) shutil.copyfile(file, p) except Exception as ex: print("FAIL", ex) pass cmd3 = shlex.split( '%s cvfm "%s" "%s" -C "%s" .' % (jar, jarFile, manifest, build_dir)) self.announce(" %s" % " ".join(cmd3), level=distutils.log.INFO) subprocess.check_call(cmd3) except subprocess.CalledProcessError as exc: distutils.log.error(exc.output) raise DistutilsPlatformError("Error executing {}".format(exc.cmd)) jpype-1.6.0/setupext/develop.py000066400000000000000000000034261501674766700165660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from setuptools.command.develop import develop as develop_cmd class Develop(develop_cmd): user_options = develop_cmd.user_options + [ ('enable-build-jar', None, 'Build the java jar portion'), ('enable-tracing', None, 'Set for tracing for debugging'), ('enable-coverage', None, 'Instrument c++ code for code coverage measuring'), ] def initialize_options(self, *args): self.enable_tracing = False self.enable_build_jar = False self.enable_coverage = False super().initialize_options() def reinitialize_command(self, command, reinit_subcommands=0, **kw): cmd = super().reinitialize_command( command, reinit_subcommands=reinit_subcommands, **kw) build_ext_command = self.distribution.get_command_obj("build_ext") build_ext_command.enable_tracing = self.enable_tracing build_ext_command.enable_build_jar = self.enable_build_jar build_ext_command.enable_coverage = self.enable_coverage return cmd jpype-1.6.0/setupext/platform.py000066400000000000000000000254761501674766700167650ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import setupext import os from pathlib import Path import sys import sysconfig import ctypes.util import typing # --- Logging compatibility shim --- try: import distutils.log as _log def log_info(msg, *args): _log.info(msg, *args) def log_warn(msg, *args): _log.warn(msg, *args) except ImportError: import logging logging.basicConfig(level=logging.INFO) def log_info(msg, *args): logging.info(msg, *args) def log_warn(msg, *args): logging.warning(msg, *args) # ---------------------------------- def find_python_shared_lib(): # Get Python version (e.g., '3.11') version = '{}{}'.format(sys.version_info.major, sys.version_info.minor) # Try sysconfig for the actual library name ldlib = sysconfig.get_config_var('LDLIBRARY') libdir = sysconfig.get_config_var('LIBDIR') if ldlib and libdir: candidate = os.path.join(libdir, ldlib) if os.path.exists(candidate): return candidate # Fallback: use ctypes.util.find_library if sys.platform == "win32": libname = "python" + version elif sys.platform == "darwin": libname = "python" + version else: libname = "python" + version path = ctypes.util.find_library(libname) if path: return path # Fallback: search common locations candidates = [] if sys.platform == "win32": candidates = [ os.path.join(sys.prefix, 'python{}.dll'.format(version)), os.path.join(sys.prefix, 'DLLs', 'python{}.dll'.format(version)), ] elif sys.platform == "darwin": candidates = [ os.path.join(sys.prefix, 'lib', 'libpython{}.dylib'.format(version)), os.path.join(sys.prefix, 'lib', 'python{}.dylib'.format(version)), ] else: candidates = [ os.path.join(sys.prefix, 'lib', 'libpython{}.so'.format(version)), os.path.join(sys.prefix, 'lib', 'libpython{}.so.{}'.format(version, sys.version_info.micro)), ] for candidate in candidates: if os.path.exists(candidate): return candidate return None def config_windows(platform_specific, debug=False): """ Configure build settings for the Windows platform (Python 3+ only). - Adds 'WIN32' macro for Windows-specific compilation. - Uses C++14 standard. - Adds optimization, exception handling, and runtime flags. - Optionally adds debug info if debug=True. - If Python is built without the GIL, defines 'Py_GIL_DISABLED'. Args: platform_specific (dict): Dictionary to update with platform-specific flags. debug (bool): If True, include debug info (/Zi). Default is False. Returns: str: JNI subdirectory name for Windows ('win32'). """ log_info("Add windows settings") platform_specific['define_macros'] = [('WIN32', 1)] # Compile args for release build compile_args = ['/O2', '/EHsc', '/MD', '/std:c++14'] # Add debug info if requested if debug: compile_args.append('/Zi') platform_specific['extra_compile_args'] = compile_args # If Python is built without the Global Interpreter Lock, define macro if hasattr(sys, "_is_gil_enabled") and not sys._is_gil_enabled(): platform_specific['define_macros'].append(('Py_GIL_DISABLED', '1')) return 'win32' def config_darwin(platform_specific): """ Configure build settings for macOS (Darwin). - Links against the dynamic loading library ('dl'). - Defines 'MACOSX' macro for macOS-specific compilation. - Uses C++11 standard, disables debug info, and enables optimization. Returns: str: JNI subdirectory name for macOS ('darwin'). """ log_info("Add darwin settings") platform_specific['libraries'] = ['dl'] platform_specific['define_macros'] = [('MACOSX', 1)] platform_specific['extra_compile_args'] = ['-g0', '-std=c++11', '-O2'] return 'darwin' def config_linux(platform_specific): """ Configure build settings for Linux. - Links against the dynamic loading library ('dl'). - Uses C++11 standard, disables debug info, and enables optimization. Returns: str: JNI subdirectory name for Linux ('linux'). """ log_info("Add linux settings") platform_specific['libraries'] = ['dl'] platform_specific['extra_compile_args'] = ['-g0', '-std=c++11', '-O2'] return 'linux' def config_aix7(platform_specific): """ Configure build settings for IBM AIX 7. - Links against the dynamic loading library ('dl'). - Uses C++11 standard, disables debug info, and enables optimization. Returns: str: JNI subdirectory name for AIX 7 ('aix7'). """ log_info("Add aix settings") platform_specific['libraries'] = ['dl'] platform_specific['extra_compile_args'] = ['-g0', '-std=c++11', '-O2'] return 'aix7' def config_freebsd(platform_specific): """ Configure build settings for FreeBSD. No additional macros, libraries, or compile arguments are needed. Returns: str: JNI subdirectory name for FreeBSD ('freebsd'). """ log_info("Add freebsd settings") return 'freebsd' def config_openbsd(platform_specific): """ Configure build settings for OpenBSD. No additional macros, libraries, or compile arguments are needed. Returns: str: JNI subdirectory name for OpenBSD ('openbsd'). """ log_info("Add openbsd settings") return 'openbsd' def config_android(platform_specific): """ Configure build settings for Android. - Links against 'dl', 'c++_shared', and 'SDL2' libraries. - Uses C++11 standard, disables debug info, enables optimization, and ensures exceptions and RTTI are enabled. Returns: str: JNI subdirectory name for Android ('linux'). """ log_info("Add android settings") platform_specific['libraries'] = ['dl', 'c++_shared', 'SDL2'] platform_specific['extra_compile_args'] = [ '-g0', '-std=c++11', '-O2', '-fexceptions', '-frtti' ] return 'linux' def config_zos(platform_specific): """ Configure build settings for IBM z/OS. No additional macros, libraries, or compile arguments are needed. Returns: str: JNI subdirectory name for z/OS ('zos'). """ log_info("Add zos settings") return 'zos' def config_sunos5(platform_specific): """ Configure build settings for Solaris (SunOS 5). No additional macros, libraries, or compile arguments are needed. Returns: str: JNI subdirectory name for Solaris ('solaris'). """ log_info("Add solaris settings") return 'solaris' def config_default(platform_specific): """ Default configuration for unrecognized platforms. Issues a warning that the platform is not explicitly handled. Returns: str: Empty string, since no JNI subdirectory is specified. """ log_warn("Your platform is not being handled explicitly. It may work or not!") return '' # Map platform start strings to config functions PLATFORM_CONFIGS = [ ('win32', config_windows), ('darwin', config_darwin), ('linux', config_linux), ('aix7', config_aix7), ('freebsd', config_freebsd), ('openbsd', config_openbsd), ('android', config_android), ('zos', config_zos), ('sunos5', config_sunos5), ] def get_platform_config(platform): for prefix, func in PLATFORM_CONFIGS: if platform.startswith(prefix): return func return config_default def Platform(*, include_dirs: typing.Sequence[Path], sources: typing.Sequence[Path], platform: str): # Convert Path objects in sources to strings, as required by build systems sources = [str(pth) for pth in sources] # Initialize platform-specific build configuration dictionary platform_specific = { 'include_dirs': list(include_dirs), # Start with provided include directories 'sources': sources, # Source files for compilation } # Define the fallback location for JNI headers if JAVA_HOME is not set or incomplete fallback_jni = os.path.join('native', 'jni_include') java_home = os.getenv('JAVA_HOME', '') found_jni = False # If JAVA_HOME is set and exists, try to use its JNI headers if os.path.exists(java_home): # Add the JAVA_HOME/include directory to the include path platform_specific['include_dirs'].append(os.path.join(java_home, 'include')) # Check if any of the include directories contains jni.h for d in platform_specific['include_dirs']: if os.path.exists(os.path.join(str(d), 'jni.h')): log_info("Found native jni.h at %s", d) found_jni = True break # If JAVA_HOME/include does not contain jni.h, issue a warning and fall back if not found_jni: log_warn( 'Falling back to provided JNI headers, since your provided JAVA_HOME "%s" does not provide jni.h', java_home) # If JAVA_HOME is not set or jni.h was not found, use the fallback JNI headers if not found_jni: platform_specific['include_dirs'].append(fallback_jni) # Initialize extra linker arguments (may be populated by platform-specific config) platform_specific['extra_link_args'] = [] # Select and apply the platform-specific configuration function config_func = get_platform_config(platform) jni_md_platform = config_func(platform_specific) # For POSIX platforms (not Windows), attempt to locate and link the Python shared library # This code is necessary if the module is started before Python when used with JNI if platform != 'win32': shared = find_python_shared_lib() if shared is not None: platform_specific['extra_link_args'].append(shared) # If JAVA_HOME JNI headers were found, add the platform-specific subdirectory (e.g., 'linux', 'win32') if found_jni: jni_md_dir = os.path.join(java_home, 'include', jni_md_platform) log_info("Add JNI directory %s", jni_md_dir) platform_specific['include_dirs'].append(jni_md_dir) # Return the fully populated platform-specific configuration dictionary return platform_specific jpype-1.6.0/setupext/sdist.py000066400000000000000000000065311501674766700162560ustar00rootroot00000000000000# -*- coding: utf-7 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import os import shutil from pathlib import Path from setuptools.command.sdist import sdist # --- Logging compatibility shim --- try: import distutils.log as _log LOG_INFO = _log.INFO LOG_WARN = _log.WARN LOG_ERROR = _log.ERROR except ImportError: import logging class _log: INFO = logging.INFO WARN = logging.WARNING ERROR = logging.ERROR # ---------------------------------- class BuildSourceDistribution(sdist): """ Custom sdist command to include prebuilt JPype jar files in the source distribution. This removes the need for javac/jdk at install time. """ def run(self): # Destination directory for the jar file(s) dest = Path('native') / 'jars' self.announce(f"Preparing to build and package JPype jar files at {dest}", level=LOG_INFO) # Get the build_ext command object and set the jar build flag cmd = self.distribution.get_command_obj('build_ext') # Call with jar only option cmd.jar = True # Run build_ext and test_java to ensure all Java artifacts are built try: self.run_command('build_ext') self.run_command('test_java') except Exception as e: raise RuntimeError(f"Failed during build_ext or test_java: {e}") # Determine the path to the generated jar file jar_basename = "org.jpype.jar" # The get_ext_fullpath method returns the path to the extension module, # so we use its directory as the jar location. ext_fullpath = cmd.get_ext_fullpath("JAVA") jar_dir = Path(os.path.dirname(ext_fullpath)) jar_path = jar_dir / jar_basename # Check that the jar file exists if not jar_path.exists(): self.announce(f"Jar source file is missing: {jar_path}", level=LOG_ERROR) raise RuntimeError(f"Error copying jar file: {jar_path}") # Ensure the destination directory exists dest.mkdir(parents=True, exist_ok=True) # Copy the jar file to the destination shutil.copy2(jar_path, dest / jar_basename) self.announce(f"Copied {jar_path} to {dest}", level=LOG_INFO) # Run the standard sdist process to package the source distribution super().run() # Clean up: remove the jar cache directory after sdist is complete try: shutil.rmtree(dest) self.announce(f"Cleaned up temporary jar directory: {dest}", level=LOG_INFO) except Exception as cleanup_error: self.announce(f"Failed to clean up {dest}: {cleanup_error}", level=LOG_WARN) jpype-1.6.0/setupext/test_java.py000066400000000000000000000057111501674766700171070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import os import subprocess import distutils.cmd import distutils.log import glob import re import shlex def getJavaVersion(javac): # Find Java version version_str = os.popen('"%s" -version' % javac).read() result = re.match(r'javac ([0-9]+)\.([0-9]+)\..*', version_str) if not result: return 8 if int(result.group(1)) > 1: return int(result.group(1)) return int(result.group(2)) def compileJava(): javac = "javac" try: javac0 = os.path.join(os.environ['JAVA_HOME'], 'bin', 'javac') if os.name == "nt": javac0 += ".exe" if os.path.exists(javac0): javac = javac0 except KeyError: print("No JAVA_HOME set") pass print("JAVAC =", javac) version = getJavaVersion(javac) srcs = glob.glob('test/harness/jpype/**/*.java', recursive=True) srcs.extend(glob.glob('test/harness/org/**/*.java', recursive=True)) exports = "" if version == 8: srcs.extend(glob.glob('test/harness/java8/**/*.java', recursive=True)) if version > 8: srcs.extend(glob.glob('test/harness/java9/**/*.java', recursive=True)) exports = "--add-exports java.base/jdk.internal.reflect=ALL-UNNAMED" cmd = shlex.split( '"%s" -d test/classes %s -g:lines,vars,source' % (javac, exports)) os.makedirs("test/classes", exist_ok=True) cmd.extend(srcs) return cmd class TestJavaCommand(distutils.cmd.Command): """A custom command to create jar file during test.""" description = 'run javac to make test harness' user_options = [] def initialize_options(self): """Set default values for options.""" pass def finalize_options(self): """Post-process options.""" pass def run(self): """Run command.""" if os.path.exists(os.path.join("test", "classes")): distutils.log.info("Skip building Java testbench") return cmdStr = compileJava() self.announce(" %s" % " ".join(cmdStr), level=distutils.log.INFO) subprocess.check_call(cmdStr) subprocess.check_call(shlex.split( "javadoc -Xdoclint:none test/harness/jpype/doc/Test.java -d test/classes/")) jpype-1.6.0/test-requirements.txt000066400000000000000000000000401501674766700171230ustar00rootroot00000000000000pytest pyinstaller jedi==0.18.0 jpype-1.6.0/test/000077500000000000000000000000001501674766700136475ustar00rootroot00000000000000jpype-1.6.0/test/build.xml000066400000000000000000000031401501674766700154660ustar00rootroot00000000000000 jpype-1.6.0/test/harness/000077500000000000000000000000001501674766700153125ustar00rootroot00000000000000jpype-1.6.0/test/harness/java8/000077500000000000000000000000001501674766700163235ustar00rootroot00000000000000jpype-1.6.0/test/harness/java8/jpype/000077500000000000000000000000001501674766700174525ustar00rootroot00000000000000jpype-1.6.0/test/harness/java8/jpype/lambda/000077500000000000000000000000001501674766700206725ustar00rootroot00000000000000jpype-1.6.0/test/harness/java8/jpype/lambda/Test1.java000066400000000000000000000021051501674766700225330ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.lambda; import java.util.function.Function; public class Test1 { public Function getFunction() { return new Function() { public Double apply(Double d) { return d + 1; } }; } public Function getLambda() { return (Double d) -> (d + 1); } } jpype-1.6.0/test/harness/java9/000077500000000000000000000000001501674766700163245ustar00rootroot00000000000000jpype-1.6.0/test/harness/java9/jpype/000077500000000000000000000000001501674766700174535ustar00rootroot00000000000000jpype-1.6.0/test/harness/java9/jpype/method/000077500000000000000000000000001501674766700207335ustar00rootroot00000000000000jpype-1.6.0/test/harness/java9/jpype/method/Caller.java000066400000000000000000000040021501674766700227740ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.method; import jdk.internal.reflect.CallerSensitive; /** * Caller sensitive methods need special handling. * * So we need to test each possible path (different return types and arguments). * * We will pretend our methods are also CallerSensitive. */ public class Caller { @CallerSensitive public static Object callObjectStatic() { return new Caller(); } @CallerSensitive public Object callObjectMember() { return new Caller(); } @CallerSensitive public static void callVoidStatic() { } @CallerSensitive public void callVoidMember() { } @CallerSensitive public static int callIntegerStatic() { return 123; } @CallerSensitive public int callIntegerMember() { return 123; } @CallerSensitive public Object callArgs(Object a, Object b) { return b; } @CallerSensitive public int callArg1(int a) { return a; } @CallerSensitive public Object callVarArgs(Object a, Object... b) { return b; } public Class callStackWalker1() { return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass(); } @CallerSensitive public Class callStackWalker2() { return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass(); } }; jpype-1.6.0/test/harness/jpype/000077500000000000000000000000001501674766700164415ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/array/000077500000000000000000000000001501674766700175575ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/array/Test2.java000066400000000000000000000016051501674766700214250ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.array; public class Test2 { public Object[] getValue() { return new Object[0]; } public Object test(Object o) { return null; } } jpype-1.6.0/test/harness/jpype/array/TestArray.java000066400000000000000000000026571501674766700223520ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.array; public class TestArray { public TestArray() { } public int[] i = { 12234, 1234, 234, 1324, 424, 234, 234, 142, 5, 251, 242, 35, 235, 62, 1235, 46, 245132, 51, 2, 3, 4 }; public Object[] getSubClassArray() { return new Integer[] { 1, 2 }; } public Object getArrayAsObject() { return new String[] { "aaa", "bbb" }; } public char[] getCharArray() { return new char[] { 'a', 'v', 'c', 'd' }; } public byte[] getByteArray() { String s = "avcd"; return s.getBytes(); } public Object testObject(Object obj) { return obj; } public Object testInt(int[] obj) { return obj; } } jpype-1.6.0/test/harness/jpype/attr/000077500000000000000000000000001501674766700174135ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/attr/ClassWithBuffer.java000066400000000000000000000016411501674766700233130ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; import java.awt.image.BufferStrategy; public class ClassWithBuffer { public BufferStrategy bufferStrategy; public void foo() { System.out.println("foo"); } } jpype-1.6.0/test/harness/jpype/attr/Holder.java000066400000000000000000000014401501674766700214720ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class Holder { public String f; } jpype-1.6.0/test/harness/jpype/attr/SubHolder.java000066400000000000000000000014761501674766700221550ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class SubHolder extends Holder implements java.io.Serializable { } jpype-1.6.0/test/harness/jpype/attr/SyntheticMethods.java000066400000000000000000000020661501674766700235600ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; import java.util.List; public class SyntheticMethods { public static interface GenericInterface { void foo(T value); } public static class GenericImpl implements GenericInterface { public List mListValue = null; public void foo(List value) { mListValue = value; } } } jpype-1.6.0/test/harness/jpype/attr/Test1.java000066400000000000000000000047771501674766700212750ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class Test1 { private String mBigString; public Test1() { StringBuffer sb = new StringBuffer(4001); for (int i = 0; i < 4000; i++) { sb.append("A"); } mBigString = sb.toString(); } public String getBigString() { return mBigString; } public String toString() { return "aaa"; } public static String[] testStaticString(String s1, String s2) { return new String[] { s1, s2 }; } public static String testStaticHolder(Holder h) { return h.f; } public String[] testString(String s1, String s2) { return new String[] { s1, s2 }; } public String[] testStringArray(String[] vals) { return vals; } public String stringValue = "Foo"; public char charValue = 'a'; public static Object objectValue = (int) 234; public static void reset() { objectValue = (int) 234; } public Object getSubClass() { return new SubHolder(); } public void callWithClass(Class c) { } public void test1Method() { } public boolean mBooleanValue = false; public void setBoolean(boolean b) { mBooleanValue = b; } public byte mByteValue; public void setByte(byte b) { mByteValue = b; } public short mShortValue = 0; public void setShort(short s) { mShortValue = s; } public int mIntValue = 0; public void setInt(int i) { mIntValue = i; } public long mLongValue = 0; public void setLong(long l) { mLongValue = l; } public String callWithSomething(Object obj) { return "Object"; } public String callWithSomething(Class obj) { return "Class"; } public Test1 delete(String arg1, String arg2) { System.out.println("Overloaded test 1 called"); return null; } } jpype-1.6.0/test/harness/jpype/attr/Test2.java000066400000000000000000000020421501674766700212550ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class Test2 extends Test1 { public Test2() { super(); } public void test2Method() { } public String toString(String foo) { return foo; } public Test1 delete(String arg1, String arg2) { System.out.println("Overloaded test 2 called"); return null; } } jpype-1.6.0/test/harness/jpype/attr/TestKeywords.java000066400000000000000000000016301501674766700227250ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class TestKeywords { public String __leading_double_underscore() { return "foo"; } public String __dunder_name__() { return "foo"; } } jpype-1.6.0/test/harness/jpype/attr/TestOverloadA.java000066400000000000000000000015131501674766700227720ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class TestOverloadA { public String foo() { return "foo() in A"; } } jpype-1.6.0/test/harness/jpype/attr/TestOverloadB.java000066400000000000000000000014511501674766700227740ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class TestOverloadB extends TestOverloadA { } jpype-1.6.0/test/harness/jpype/attr/TestOverloadC.java000066400000000000000000000015571501674766700230040ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.attr; public class TestOverloadC extends TestOverloadB { public String foo(int i) { return "foo(int) in C: " + i; } } jpype-1.6.0/test/harness/jpype/boxed/000077500000000000000000000000001501674766700175425ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/boxed/Boxed.java000066400000000000000000000044111501674766700214460ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.boxed; public class Boxed { // Call each type static public boolean callBoolean(boolean i) { return i; } static public byte callByte(byte i) { return i; } static public char callChar(char i) { return i; } static public short callShort(short i) { return i; } static public int callInteger(int i) { return i; } static public long callLong(long i) { return i; } static public float callFloat(float i) { return i; } static public double callDouble(double i) { return i; } // Create a boxed type static public Short newShort(short i) { return i; } static public Integer newInteger(int i) { return i; } static public Long newLong(long i) { return i; } static public Float newFloat(float i) { return i; } static public Double newDouble(double i) { return i; } // Check which is called static public int whichShort(short i) { return 1; } static public int whichShort(Short i) { return 2; } static public int whichInteger(int i) { return 1; } static public int whichInteger(Integer i) { return 2; } static public int whichLong(long i) { return 1; } static public int whichLong(Long i) { return 2; } static public int whichFloat(float i) { return 1; } static public int whichFloat(Float i) { return 2; } static public int whichDouble(double i) { return 1; } static public int whichDouble(Double i) { return 2; } } jpype-1.6.0/test/harness/jpype/classhints/000077500000000000000000000000001501674766700206145ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/classhints/ClassHintsTest.java000066400000000000000000000015741501674766700244010ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.classhints; public class ClassHintsTest { public static Custom input = null; public static void call(Custom c) { input = c; } } jpype-1.6.0/test/harness/jpype/classhints/Custom.java000066400000000000000000000014301501674766700227270ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.classhints; public interface Custom { } jpype-1.6.0/test/harness/jpype/closeable/000077500000000000000000000000001501674766700203725ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/closeable/CloseableTest.java000066400000000000000000000026501501674766700237710ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.closeable; class CloseableTest implements java.io.Closeable { public static boolean closed = false; public static String printed = ""; public static boolean willfail = false; public static boolean failed = false; public static void reset() { closed = false; willfail = false; failed = false; printed = ""; } public CloseableTest() { } public void print(String value) { printed = value; } public void throwException() { throw new RuntimeException("oh no!"); } public void close() throws java.io.IOException { closed = true; if (willfail) { failed = true; throw new java.io.IOException("oh my?"); } } } jpype-1.6.0/test/harness/jpype/collection/000077500000000000000000000000001501674766700205745ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/collection/CollectionTest.java000066400000000000000000000020661501674766700243760ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.collection; import java.util.Collection; import java.util.Map; public class CollectionTest { public static Object testList(Collection input) { System.out.println(input); return input; } public static Object testMap(Map input) { System.out.println(input); return input; } } jpype-1.6.0/test/harness/jpype/collection/TestEnum.java000066400000000000000000000014231501674766700232030ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.collection; enum TestEnum { A, B } jpype-1.6.0/test/harness/jpype/common/000077500000000000000000000000001501674766700177315ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/common/Fixture.java000066400000000000000000000204521501674766700222250ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.common; public class Fixture { // Tests for access private static Object static_private_object_field = new String("private static object field"); private Object private_object_field = new String("private object field"); public final static Object final_static_object_field = new String("final static object field"); public final Object final_object_field = new String("final object field"); public final static int final_static_int_field = 12345; public final int final_int_field = 67890; public static boolean static_bool_field; public static char static_char_field; public static byte static_byte_field; public static short static_short_field; public static int static_int_field; public static long static_long_field; public static float static_float_field; public static double static_double_field; public static Object static_object_field; public static boolean getStaticBool() { return static_bool_field; } public static char getStaticChar() { return static_char_field; } public static byte getStaticByte() { return static_byte_field; } public static short getStaticShort() { return static_short_field; } public static int getStaticInt() { return static_int_field; } public static long getStaticLong() { return static_long_field; } public static float getStaticFloat() { return static_float_field; } public static double getStaticDouble() { return static_double_field; } public static Object getStaticObject() { return static_object_field; } public static void setStaticBool(boolean i) { static_bool_field = i; } public static void setStaticChar(char i) { static_char_field = i; } public static void setStaticByte(byte i) { static_byte_field = i; } public static void setStaticShort(short i) { static_short_field = i; } public static void setStaticInt(int i) { static_int_field = i; } public static void setStaticLong(long i) { static_long_field = i; } public static void setStaticFloat(float i) { static_float_field = i; } public static void setStaticDouble(double i) { static_double_field = i; } public static void setStaticObject(Object i) { static_object_field = i; } public boolean bool_field; public char char_field; public byte byte_field; public short short_field; public int int_field; public long long_field; public float float_field; public double double_field; public Object object_field; public boolean getBool() { return bool_field; } public char getChar() { return char_field; } public byte getByte() { return byte_field; } public short getShort() { return short_field; } public int getInt() { return int_field; } public long getLong() { return long_field; } public float getFloat() { return float_field; } public double getDouble() { return double_field; } public Object getObject() { return object_field; } public void setBool(boolean i) { bool_field = i; } public void setChar(char i) { char_field = i; } public void setByte(byte i) { byte_field = i; } public void setShort(short i) { short_field = i; } public void setInt(int i) { int_field = i; } public void setLong(long i) { long_field = i; } public void setFloat(float i) { float_field = i; } public void setDouble(double i) { double_field = i; } public void setObject(Object i) { object_field = i; } public static boolean throwStaticBool() { throw new RuntimeException("bool"); } public static char throwStaticChar() { throw new RuntimeException("char"); } public static byte throwStaticByte() { throw new RuntimeException("byte"); } public static short throwStaticShort() { throw new RuntimeException("short"); } public static int throwStaticInt() { throw new RuntimeException("int"); } public static long throwStaticLong() { throw new RuntimeException("long"); } public static float throwStaticFloat() { throw new RuntimeException("float"); } public static double throwStaticDouble() { throw new RuntimeException("double"); } public static Object throwStaticObject() { throw new RuntimeException("object"); } public boolean throwBool() { throw new RuntimeException("bool"); } public char throwChar() { throw new RuntimeException("char"); } public byte throwByte() { throw new RuntimeException("byte"); } public short throwShort() { throw new RuntimeException("short"); } public int throwInt() { throw new RuntimeException("int"); } public long throwLong() { throw new RuntimeException("long"); } public float throwFloat() { throw new RuntimeException("float"); } public double throwDouble() { throw new RuntimeException("double"); } public Object throwObject() { throw new RuntimeException("object"); } public boolean callBoolean(boolean i) { return i; } public byte callByte(byte i) { return i; } public char callChar(char i) { return i; } public short callShort(short i) { return i; } public int callInt(int i) { return i; } public long callLong(long i) { return i; } public float callFloat(float i) { return i; } public double callDouble(double i) { return i; } public String callString(String i) { return i; } public java.lang.Boolean callBoxedBoolean(java.lang.Boolean i) { return i; } public java.lang.Byte callBoxedByte(java.lang.Byte i) { return i; } public java.lang.Character callBoxedChar(java.lang.Character i) { return i; } public java.lang.Short callBoxedShort(java.lang.Short i) { return i; } public java.lang.Integer callBoxedInt(java.lang.Integer i) { return i; } public java.lang.Long callBoxedLong(java.lang.Long i) { return i; } public java.lang.Float callBoxedFloat(java.lang.Float i) { return i; } public java.lang.Double callBoxedDouble(java.lang.Double i) { return i; } public static boolean callStaticBoolean(boolean i) { return i; } public static byte callStaticByte(byte i) { return i; } public static char callStaticChar(char i) { return i; } public static short callStaticShort(short i) { return i; } public static int callStaticInt(int i) { return i; } public static long callStaticLong(long i) { return i; } public static float callStaticFloat(float i) { return i; } public static double callStaticDouble(double i) { return i; } public static String callStaticString(String i) { return i; } public static Object callStaticObject(Object i) { return i; } private static Object callPrivateStaticObject(Object i) { return i; } public Object callObject(Object i) { return i; } private Object callPrivateObject(Object i) { return i; } protected Object callProtectedObject(Object i) { return i; } public static Number callNumber(Number n) { return n; } public Object callSupplier(java.util.function.Supplier s) { return s.get(); } public Object callCharArray(char[] c) { return c; } public Object callByteArray(byte[] c) { return c; } public Object callClass(Class c) { return c; } public Iterable callIterable(Iterable c) { return c; } } jpype-1.6.0/test/harness/jpype/common/OnShutdown.java000066400000000000000000000041431501674766700227060ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.common; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * * @author nelson85 */ public class OnShutdown { static public void addCoverageHook(Object context) { try { Method method = context.getClass().getMethod("_addPost", Runnable.class); method.invoke(context, new CoverageHook()); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { System.out.println("Fail to install post hook " + ex.getMessage()); } } private static class CoverageHook implements Runnable { @Override public void run() { // If coverage tools are being used, we need to dump last before everything // shuts down try { Class RT = Class.forName("org.jacoco.agent.rt.RT"); Method getAgent = RT.getMethod("getAgent"); Object agent = getAgent.invoke(null); Thread.sleep(100); // make sure we don't clober agent.getClass().getMethod("dump", boolean.class).invoke(agent, false); System.err.println("*** Coverage dumped"); } catch (InterruptedException | NullPointerException | ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { } } } } jpype-1.6.0/test/harness/jpype/doc/000077500000000000000000000000001501674766700172065ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/doc/Test.java000066400000000000000000000030561501674766700207740ustar00rootroot00000000000000/* * Attempt at javadoc coverage test. */ package jpype.doc; /** * A bunch of random stuff. * * @author nelson85 * @see "The Java Programming Language" * @see java.lang.Object#wait() * @see Java Spec * @version 1.0 */ public class Test { public final static int QQ = 1; /** * This does something special. *

* It is so special we won't tell you. *
* We have our reasons. See a or *

a
. So up and don't look down. Literally * {@literal AC}. Don't feel s when you can be bold * or em or italics. *

* Use the {@link #methodOne(int, Object) BangBang} method. *. *

{@code
   *    hey.method(1, SecretMap());
   * }
*

* * * * *
NorthEastSteps
YN10
NY20
*

*

*
Defines *
Something. *
*
*
    *
  1. Item 1 *
      *
    • Apples *
    • Pears *
    *
  2. Item 2 *
* * @throws java.lang.NoSuchMethodException if something bad happens. * @param i an argument a. {@value #QQ} * @param j another argument. * @return a secret code. * @since 1.5 * */ public int methodOne(int i, Object j) throws NoSuchMethodException { return 12345; // Same as your luggage combination. } } jpype-1.6.0/test/harness/jpype/exc/000077500000000000000000000000001501674766700172205ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/exc/ChildTestException.java000066400000000000000000000014631501674766700236310ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.exc; public class ChildTestException extends ParentTestException { } jpype-1.6.0/test/harness/jpype/exc/ExceptionTest.java000066400000000000000000000031621501674766700226630ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.exc; public class ExceptionTest { public static void throwRuntime() { throw new RuntimeException("Foo"); } public static void throwIOException() throws java.io.IOException { throw new java.io.IOException("Bar"); } public static boolean delegateThrow(ExceptionThrower th) { try { th.throwIOException(); } catch (java.io.IOException ex) { return true; } catch (Throwable ex) { System.out.println("Unexpected Exception during delegateThrow"); ex.printStackTrace(); } System.out.println("Failed"); return false; } public static void throwChildTestException() throws ParentTestException { throw new ChildTestException(); } public static void throwChain() { method1(); } static void method1() { method2(); } static void method2() { throw new RuntimeException("Inner"); } } jpype-1.6.0/test/harness/jpype/exc/ExceptionThrower.java000066400000000000000000000015111501674766700233720ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.exc; interface ExceptionThrower { void throwIOException() throws java.io.IOException; } jpype-1.6.0/test/harness/jpype/exc/ParentTestException.java000066400000000000000000000014641501674766700240400ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.exc; public class ParentTestException extends java.lang.Exception { } jpype-1.6.0/test/harness/jpype/exc/WierdException.java000066400000000000000000000017501501674766700230170ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.exc; public class WierdException extends Exception { public WierdException(int i, float f, Object o) { super("Got it"); } public static void testThrow() throws WierdException { throw new WierdException(1, 2, new Object()); } } jpype-1.6.0/test/harness/jpype/functional/000077500000000000000000000000001501674766700206035ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/functional/Annotated.java000066400000000000000000000015011501674766700233600ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; @FunctionalInterface public interface Annotated { int call(int i); } jpype-1.6.0/test/harness/jpype/functional/AnnotatedWithObjectMethods.java000066400000000000000000000016251501674766700266760ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; @FunctionalInterface public interface AnnotatedWithObjectMethods { int call(int i); boolean equals(Object o); int hashCode(); String toString(); } jpype-1.6.0/test/harness/jpype/functional/ExtendsFunctional.java000066400000000000000000000014661501674766700251120ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; public interface ExtendsFunctional extends NonAnnotated { } jpype-1.6.0/test/harness/jpype/functional/ExtendsNonFunctional.java000066400000000000000000000015131501674766700255560ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; public interface ExtendsNonFunctional extends NonAnnotated { boolean test(); } jpype-1.6.0/test/harness/jpype/functional/NonAnnotated.java000066400000000000000000000014571501674766700240450ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; public interface NonAnnotated { int call(int i); } jpype-1.6.0/test/harness/jpype/functional/NonAnnotatedWithObjectMethods.java000066400000000000000000000016031501674766700273450ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; public interface NonAnnotatedWithObjectMethods { int call(int i); boolean equals(Object o); int hashCode(); String toString(); } jpype-1.6.0/test/harness/jpype/functional/RedeclaresAnnotated.java000066400000000000000000000015101501674766700253520ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; public interface RedeclaresAnnotated extends Annotated { int call(int i); } jpype-1.6.0/test/harness/jpype/functional/RedeclaresNonAnnotated.java000066400000000000000000000015161501674766700260330ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.functional; public interface RedeclaresNonAnnotated extends NonAnnotated { int call(int i); } jpype-1.6.0/test/harness/jpype/mro/000077500000000000000000000000001501674766700172365ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/mro/A.java000066400000000000000000000014371501674766700202660ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; interface A extends E, F { void foo(); } jpype-1.6.0/test/harness/jpype/mro/B.java000066400000000000000000000014201501674766700202570ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; interface B extends D, A { } jpype-1.6.0/test/harness/jpype/mro/C.java000066400000000000000000000014631501674766700202670ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; public class C implements A, B { public void foo() { } } jpype-1.6.0/test/harness/jpype/mro/D.java000066400000000000000000000014031501674766700202620ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; interface D { } jpype-1.6.0/test/harness/jpype/mro/E.java000066400000000000000000000014031501674766700202630ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; interface E { } jpype-1.6.0/test/harness/jpype/mro/F.java000066400000000000000000000014031501674766700202640ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; interface F { } jpype-1.6.0/test/harness/jpype/mro/MultipleInterfaces.java000066400000000000000000000017261501674766700237060ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.mro; interface IA { } interface IB { } interface ICombined1 extends IA, IB { } interface ICombined2 extends IB, IA { } public class MultipleInterfaces implements ICombined1, ICombined2 { public MultipleInterfaces() { } } jpype-1.6.0/test/harness/jpype/numeric/000077500000000000000000000000001501674766700201035ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/numeric/NumericTest.java000066400000000000000000000015761501674766700232210ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.numeric; public class NumericTest { public static boolean doubleIsTwiceMaxFloat(double d) { return d == (Float.MAX_VALUE * 2.0); } } jpype-1.6.0/test/harness/jpype/objectwrapper/000077500000000000000000000000001501674766700213105ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/objectwrapper/Static.java000066400000000000000000000015651501674766700234110ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.objectwrapper; class StaticTest { public static int i = 1; public static double d = 1.2345; public static String s = "hello"; } jpype-1.6.0/test/harness/jpype/objectwrapper/Test1.java000066400000000000000000000021061501674766700231520ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.objectwrapper; public class Test1 { public Test1() { } public int Method1(Number n) { return 1; } public int Method1(Integer n) { return 2; } public int Method1(Object n) { return 3; } public int Method1(String n) { return 4; } public Object ReturnObject(Object o) { return o; } } jpype-1.6.0/test/harness/jpype/overloads/000077500000000000000000000000001501674766700204375ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/overloads/DerivedTest.java000066400000000000000000000024161501674766700235270ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.overloads; /** Test to see that using a derived type rather than a base class will * not alter the method resolution process. */ public class DerivedTest { public static class Base {} public static class Derived extends Base {} public static int testStatic(boolean a, Base b) { return 1; } public static int testStatic(Object a, Base b) { return 2; } public int testMember(boolean a, Base b) { return 3; } public int testMember(Object a, Base b) { return 4; } public static class Sub extends DerivedTest {} } jpype-1.6.0/test/harness/jpype/overloads/Test1.java000066400000000000000000000130531501674766700223040ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.overloads; import java.util.List; public class Test1 { public static class A { } public static class B extends A { } public static class C extends B { } public String testMostSpecific(A a) { return "A"; } public String testMostSpecific(B b) { return "B"; } public String testVarArgs(A a, A... as) { return "A,A..."; } public String testVarArgs(A a, B... bs) { return "A,B..."; } public String testVarArgs(B a, B... bs) { return "B,B..."; } public String testPrimitive(byte v) { return "byte"; } public String testPrimitive(Byte v) { return "Byte"; } public String testPrimitive(short v) { return "short"; } public String testPrimitive(Short v) { return "Short"; } public String testPrimitive(int v) { return "int"; } public String testPrimitive(Integer v) { return "Integer"; } public String testPrimitive(long v) { return "long"; } public String testPrimitive(Long v) { return "Long"; } public String testPrimitive(float v) { return "float"; } public String testPrimitive(Float v) { return "Float"; } public String testPrimitive(double v) { return "double"; } public String testPrimitive(Double v) { return "Double"; } public String testPrimitive(boolean v) { return "boolean"; } public String testPrimitive(Boolean v) { return "Boolean"; } public String testPrimitive(char v) { return "char"; } public String testPrimitive(Character v) { return "Character"; } public static String testInstanceVsClass(B b) { return "static B"; } public String testInstanceVsClass(A a) { return "instance A"; } public static String testJavaInstanceVsClass() { return new Test1().testInstanceVsClass(new C()); } /* I1 / \ I2 I3 \ / \ I4 I5 | \ | I6 I7 \ | I8 */ public static interface I1 { } public static interface I2 extends I1 { } public static interface I3 extends I1 { } public static interface I4 extends I2, I3 { } public static interface I5 extends I3 { } public static interface I6 extends I4 { } public static interface I7 extends I4, I5 { } public static interface I8 extends I6, I7 { } public static class I1Impl implements I1 { } public static class I2Impl implements I2 { } public static class I3Impl implements I3 { } public static class I4Impl implements I4 { } public static class I5Impl implements I5 { } public static class I6Impl implements I6 { } public static class I7Impl implements I7 { } public static class I8Impl implements I8 { } public String testInterfaces1(I2 v) { return "I2"; } public String testInterfaces1(I3 v) { return "I3"; } public String testInterfaces2(I2 v) { return "I2"; } public String testInterfaces2(I3 v) { return "I3"; } public String testInterfaces2(I4 v) { return "I4"; } public String testInterfaces3(I4 v) { return "I4"; } public String testInterfaces3(I5 v) { return "I5"; } public String testInterfaces4(I1 v) { return "I1"; } public String testInterfaces4(I2 v) { return "I2"; } public String testInterfaces4(I3 v) { return "I3"; } public String testInterfaces4(I4 v) { return "I4"; } public String testInterfaces4(I5 v) { return "I5"; } public String testInterfaces4(I6 v) { return "I6"; } public String testInterfaces4(I7 v) { return "I7"; } public String testInterfaces4(I8 v) { return "I8"; } public String testClassVsObject(Object v) { return "Object"; } public String testClassVsObject(Class v) { return "Class"; } public String testStringArray(Object v) { return "Object"; } public String testStringArray(String v) { return "String"; } public String testStringArray(String[] v) { return "String[]"; } public String testListVSArray(String[] v) { return "String[]"; } public String testListVSArray(List v) { return "List"; } /* tests for java 1.8 default methods, commented out for travis environment public interface IDefaultA { public default String defaultMethod() { return "A"; } } public interface IDefaultB extends IDefaultA { public default String defaultMethod() { return "B"; } } public interface IDefaultC extends IDefaultA, IDefaultB { } public static class DefaultC implements IDefaultC { } //*/ } jpype-1.6.0/test/harness/jpype/overloads/Test2.java000066400000000000000000000025531501674766700223100ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.overloads; import java.util.List; public class Test2 { @FunctionalInterface public interface NoArg { public String apply(); } @FunctionalInterface public interface SingleArg { public String apply(int a); } @FunctionalInterface public interface TwoArg { public String apply(int a, int b); } public String testFunctionalInterfaces(NoArg noArg) { return "NoArgs"; } public String testFunctionalInterfaces(SingleArg singleArg) { return "SingleArg"; } public String testFunctionalInterfaces(TwoArg twoArg) { return "TwoArg"; } }jpype-1.6.0/test/harness/jpype/override/000077500000000000000000000000001501674766700202605ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/override/A.java000066400000000000000000000015001501674766700212770ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.override; public class A { public int remove(Object o) { return 1; } } jpype-1.6.0/test/harness/jpype/override/B.java000066400000000000000000000015121501674766700213030ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.override; public class B extends A { public int remove(Object o) { return 2; } } jpype-1.6.0/test/harness/jpype/properties/000077500000000000000000000000001501674766700206355ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/properties/TestBean.java000066400000000000000000000055361501674766700232160ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.properties; public class TestBean { private String getA; private String setA; private String property1; private String property2Invisible; private String property3; public String property4; private String property5; private String property6; public String property7; public String getProperty1() { return "get" + property1; } public String getProperty2() { return "get" + property2Invisible; } public String getProperty3() { return "get" + property3; } public String abcProperty4() { return "abc" + property4; } public String getProperty6() { return "get" + property7; } public String property1() { return "method"; } protected String property2() { return "method"; } protected String property3() { return "method"; } public String returnProperty5() { return "return" + this.property5; } public void setProperty1(String property1) { this.property1 = "set" + property1; } public void setProperty2(String property2) { this.property2Invisible = "set" + property2; } public void setProperty3(String property3) { this.property3 = "set" + property3; } public void setProperty5(String property5) { this.property5 = "set" + property5; } public void setProperty6(String property6) { this.property7 = "set" + property6; } public static String m1; public String m2; public String m3; public String m4; public String m5; public String getPropertyMember() { return this.m2; } public void setPropertyMember(String value) { this.m2 = value; } public static String getPropertyStatic() { return m1; } public static void setPropertyStatic(String value) { m1 = value; } public String getReadOnly() { return this.m3; } public void setWriteOnly(String value) { this.m4 = value; } public void setWith(String value) { this.m5 = value; } public String getWith() { return this.m5; } public void setFailure1(String value, int i) { } public String getFailure2(int i) { return "fail"; } } jpype-1.6.0/test/harness/jpype/proxy/000077500000000000000000000000001501674766700176225ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/proxy/ProxyExecutor.java000066400000000000000000000067421501674766700233360ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class ProxyExecutor { private ExecutorService executor; private List> executedTasks = new ArrayList>(); private List proxies = new ArrayList(); public ProxyExecutor(int noOfThreads) { executor = Executors.newFixedThreadPool(noOfThreads); } public void shutdown() { executor.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { executor.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!executor.awaitTermination(5, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted executor.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } } public void registerProxy(TestInterface4 proxy, int noOfExecutions) { for (int i = 0; i < noOfExecutions; i++) { proxies.add(new ProxyCaller(proxy)); } } public int getExpectedTasks() { return proxies.size(); } public void runExecutor() { // Collections.shuffle(proxies); for (ProxyCaller proxy : proxies) { Future future = executor.submit(proxy); executedTasks.add(future); } } public int waitForExecutedTasks() { int returns = 0; for (Future task : executedTasks) { try { returns += task.get(); } catch (Exception ex) { System.out.println("An exception has thrown during execution"); ex.printStackTrace(); } } return returns; } public class ProxyCaller implements Callable { private TestInterface4 proxy; public ProxyCaller(TestInterface4 proxy) { this.proxy = proxy; } @Override public Integer call() throws Exception { int result = 0; try { int intValue = proxy.testMethodInt(); ReturnObject objectValue = proxy.testMethodObject(); String stringValue = proxy.testMethodString(); List listValue = proxy.testMethodList(5); } catch (Exception ex) { ex.printStackTrace(); } return 1; //result; } } } jpype-1.6.0/test/harness/jpype/proxy/ProxyTriggers.java000066400000000000000000000041521501674766700233170ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; import java.util.LinkedList; import java.util.List; public class ProxyTriggers { public static String[] testProxy(Object itf) { List methods = new LinkedList<>(); if (itf instanceof TestInterface1) { methods.add("Test Method1 = " + ((TestInterface1) itf).testMethod1()); } if (itf instanceof TestInterface2) { methods.add("Test Method2 = " + ((TestInterface2) itf).testMethod2()); } if (itf instanceof TestInterface3) { methods.add("Test Method3 = " + ((TestInterface3) itf).testMethod3()); } return methods.toArray(new String[0]); } public void testProxyWithThread(final TestThreadCallback itf) { itf.notifyValue("Waiting for thread start"); Thread t = new Thread(new Runnable() { public void run() { for (int i = 1; i <= 3; i++) { itf.notifyValue(String.valueOf(i)); } } }); t.start(); try { t.join(); itf.notifyValue("Thread finished"); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); itf.notifyValue("Thread has been interrupted"); } } public Object[] testCallbackWithParameters(TestInterface2 itf) { byte[] vals = { 1, 2, 3, 4 }; return itf.write(vals, 12, 13); } public boolean testEquals(Object o) { return o.equals(o); } } jpype-1.6.0/test/harness/jpype/proxy/ReturnObject.java000066400000000000000000000016451501674766700231010ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; public class ReturnObject { private int number; public ReturnObject(int number) { this.number = number; } public int getNumber() { return number; } } jpype-1.6.0/test/harness/jpype/proxy/TestInterface1.java000066400000000000000000000015351501674766700233120ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; public interface TestInterface1 { int testMethod1(); default int testDefault() { return 1234; } } jpype-1.6.0/test/harness/jpype/proxy/TestInterface2.java000066400000000000000000000015461501674766700233150ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; public interface TestInterface2 { int testMethod2(); Object[] write(byte[] bytes, int pos, int length); } jpype-1.6.0/test/harness/jpype/proxy/TestInterface3.java000066400000000000000000000015111501674766700233060ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; public interface TestInterface3 extends TestInterface2 { String testMethod3(); } jpype-1.6.0/test/harness/jpype/proxy/TestInterface4.java000066400000000000000000000017011501674766700233100ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; import java.util.List; public interface TestInterface4 { ReturnObject testMethodObject(); int testMethodInt(); String testMethodString(); List testMethodList(int noOfValues); } jpype-1.6.0/test/harness/jpype/proxy/TestInterface5.java000066400000000000000000000014751501674766700233210ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; public interface TestInterface5 { public boolean equals(Object o); } jpype-1.6.0/test/harness/jpype/proxy/TestThreadCallback.java000066400000000000000000000015071501674766700241540ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.proxy; public interface TestThreadCallback { public void notifyValue(String step); } jpype-1.6.0/test/harness/jpype/reflect/000077500000000000000000000000001501674766700200655ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/reflect/Annotation.java000066400000000000000000000020421501674766700230400ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.reflect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = { ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface Annotation { public String value(); } jpype-1.6.0/test/harness/jpype/reflect/ReflectionTest.java000066400000000000000000000021201501674766700236550ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.reflect; public class ReflectionTest { public String publicField = "public"; private String privateField = "private"; public ReflectionTest() { } public String publicMethod() { return "public"; } private String privateMethod() { return "private"; } @Annotation("annotation") public void annotatedMethod() { } } jpype-1.6.0/test/harness/jpype/serial/000077500000000000000000000000001501674766700177205ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/serial/SerializationTest.java000066400000000000000000000014721501674766700242440ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.serial; public class SerializationTest implements java.io.Serializable { } jpype-1.6.0/test/harness/jpype/startup/000077500000000000000000000000001501674766700201435ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/startup/TestSystemClassLoader.java000066400000000000000000000025651501674766700252570ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.startup; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Paths; public class TestSystemClassLoader extends URLClassLoader { public TestSystemClassLoader(ClassLoader parent) throws Throwable { super(new URL[0], parent); } public void addPath(String path) throws Throwable { addURL(Paths.get(path).toAbsolutePath().toUri().toURL()); } public void addPaths(String[] paths) throws Throwable { for (String path : paths) { addPath(path); } } @SuppressWarnings("unused") // needed to start with agent private void appendToClassPathForInstrumentation(String path) throws Throwable { addPath(path); } } jpype-1.6.0/test/harness/jpype/str/000077500000000000000000000000001501674766700172515ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/str/Bad.java000066400000000000000000000014751501674766700206110ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.str; public class Bad { public String toString() { return null; } } jpype-1.6.0/test/harness/jpype/str/StringFunction.java000066400000000000000000000014701501674766700230720ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.str; public interface StringFunction { public String call(String s); } jpype-1.6.0/test/harness/jpype/str/Test.java000066400000000000000000000025421501674766700210360ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.str; public class Test { public static String staticField = "staticField"; public String memberField = "memberField"; public static String staticCall() { return "staticCall"; } public String memberCall() { return "memberCall"; } public static String callWithNullBytes() { return "call\0With\0Null\0Bytes"; } public static String returnArgument(String argument) { return argument; } public static final String array[] = { "apples", "banana", "cherries", "dates", "elderberry" }; public static String callProxy(StringFunction f, String s) { return f.call(s); } } jpype-1.6.0/test/harness/jpype/types/000077500000000000000000000000001501674766700176055ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/types/InnerTest.java000066400000000000000000000015171501674766700223670ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.types; public class InnerTest { public Object test() { return new Outer.Inner(); } } jpype-1.6.0/test/harness/jpype/types/Outer.java000066400000000000000000000016111501674766700215450ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.types; public interface Outer { Object get(); public class Inner implements Outer { public Object get() { return null; } } } jpype-1.6.0/test/harness/jpype/types/VirtualTest.java000077500000000000000000000235011501674766700227420ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.types; public class VirtualTest { public interface ObjectSupplier { Object get(); } public static class ClassObjectSupplier implements ObjectSupplier { public Object get() { return "implements"; } } public static class ClassObjectSupplier2 extends ClassObjectSupplier { public Object get() { return "extends"; } } public static ObjectSupplier getObjectImplements() { return new ClassObjectSupplier(); } public static ObjectSupplier getObjectExtends() { return new ClassObjectSupplier2(); } public static ObjectSupplier getObjectAnon() { return new ObjectSupplier() { public Object get() { return "anon"; } }; } public static ObjectSupplier getObjectAnonExtends() { return new ClassObjectSupplier() { public Object get() { return "anon-override"; } }; } // This one has to wait for us to abandon Java 7 // public static ObjectSupplier getLambda() // { // return () -> "lambda"; // } // public interface IntegerSupplier { int get(); } public static class ClassIntegerSupplier implements IntegerSupplier { public int get() { return 1; } } public static class ClassIntegerSupplier2 extends ClassIntegerSupplier { public int get() { return 2; } } public static IntegerSupplier getIntegerImplements() { return new ClassIntegerSupplier(); } public static IntegerSupplier getIntegerExtends() { return new ClassIntegerSupplier2(); } public static IntegerSupplier getIntegerAnon() { return new IntegerSupplier() { public int get() { return 3; } }; } public static IntegerSupplier getIntegerAnonExtends() { return new ClassIntegerSupplier() { public int get() { return 4; } }; } // // public interface ShortSupplier { short get(); } public static class ClassShortSupplier implements ShortSupplier { public short get() { return 1; } } public static class ClassShortSupplier2 extends ClassShortSupplier { public short get() { return 2; } } public static ShortSupplier getShortImplements() { return new ClassShortSupplier(); } public static ShortSupplier getShortExtends() { return new ClassShortSupplier2(); } public static ShortSupplier getShortAnon() { return new ShortSupplier() { public short get() { return 3; } }; } public static ShortSupplier getShortAnonExtends() { return new ClassShortSupplier() { public short get() { return 4; } }; } // // public interface LongSupplier { long get(); } public static class ClassLongSupplier implements LongSupplier { public long get() { return 1; } } public static class ClassLongSupplier2 extends ClassLongSupplier { public long get() { return 2; } } public static LongSupplier getLongImplements() { return new ClassLongSupplier(); } public static LongSupplier getLongExtends() { return new ClassLongSupplier2(); } public static LongSupplier getLongAnon() { return new LongSupplier() { public long get() { return 3; } }; } public static LongSupplier getLongAnonExtends() { return new ClassLongSupplier() { public long get() { return 4; } }; } // // public interface FloatSupplier { float get(); } public static class ClassFloatSupplier implements FloatSupplier { public float get() { return 1; } } public static class ClassFloatSupplier2 extends ClassFloatSupplier { public float get() { return 2; } } public static FloatSupplier getFloatImplements() { return new ClassFloatSupplier(); } public static FloatSupplier getFloatExtends() { return new ClassFloatSupplier2(); } public static FloatSupplier getFloatAnon() { return new FloatSupplier() { public float get() { return 3; } }; } public static FloatSupplier getFloatAnonExtends() { return new ClassFloatSupplier() { public float get() { return 4; } }; } // // public interface DoubleSupplier { double get(); } public static class ClassDoubleSupplier implements DoubleSupplier { public double get() { return 1; } } public static class ClassDoubleSupplier2 extends ClassDoubleSupplier { public double get() { return 2; } } public static DoubleSupplier getDoubleImplements() { return new ClassDoubleSupplier(); } public static DoubleSupplier getDoubleExtends() { return new ClassDoubleSupplier2(); } public static DoubleSupplier getDoubleAnon() { return new DoubleSupplier() { public double get() { return 3; } }; } public static DoubleSupplier getDoubleAnonExtends() { return new ClassDoubleSupplier() { public double get() { return 4; } }; } // // public interface ByteSupplier { byte get(); } public static class ClassByteSupplier implements ByteSupplier { public byte get() { return 1; } } public static class ClassByteSupplier2 extends ClassByteSupplier { public byte get() { return 2; } } public static ByteSupplier getByteImplements() { return new ClassByteSupplier(); } public static ByteSupplier getByteExtends() { return new ClassByteSupplier2(); } public static ByteSupplier getByteAnon() { return new ByteSupplier() { public byte get() { return 3; } }; } public static ByteSupplier getByteAnonExtends() { return new ClassByteSupplier() { public byte get() { return 4; } }; } // // public interface CharSupplier { char get(); } public static class ClassCharSupplier implements CharSupplier { public char get() { return '1'; } } public static class ClassCharSupplier2 extends ClassCharSupplier { public char get() { return '2'; } } public static CharSupplier getCharImplements() { return new ClassCharSupplier(); } public static CharSupplier getCharExtends() { return new ClassCharSupplier2(); } public static CharSupplier getCharAnon() { return new CharSupplier() { public char get() { return '3'; } }; } public static CharSupplier getCharAnonExtends() { return new ClassCharSupplier() { public char get() { return '4'; } }; } // // public interface BooleanSupplier { boolean get(); } public static class ClassBooleanSupplier implements BooleanSupplier { public boolean get() { return true; } } public static class ClassBooleanSupplier2 extends ClassBooleanSupplier { public boolean get() { return false; } } public static BooleanSupplier getBooleanImplements() { return new ClassBooleanSupplier(); } public static BooleanSupplier getBooleanExtends() { return new ClassBooleanSupplier2(); } public static BooleanSupplier getBooleanAnon() { return new BooleanSupplier() { public boolean get() { return false; } }; } public static BooleanSupplier getBooleanAnonExtends() { return new ClassBooleanSupplier() { public boolean get() { return false; } }; } // // // Yes, we have to do this one as well. All paths need exercise. public interface VoidSupplier { void get(); } public static class ClassVoidSupplier implements VoidSupplier { public void get() { return; } } public static class ClassVoidSupplier2 extends ClassVoidSupplier { public void get() { return; } } public static VoidSupplier getVoidImplements() { return new ClassVoidSupplier(); } public static VoidSupplier getVoidExtends() { return new ClassVoidSupplier2(); } public static VoidSupplier getVoidAnon() { return new VoidSupplier() { public void get() { return; } }; } public static VoidSupplier getVoidAnonExtends() { return new ClassVoidSupplier() { public void get() { return; } }; } // } jpype-1.6.0/test/harness/jpype/utf8/000077500000000000000000000000001501674766700173275ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/utf8/Utf8Test.java000066400000000000000000000115111501674766700216570ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.utf8; public class Utf8Test { private final static String[] DEFAULT_STRINGS = { "I can eat glass and it doesn't hurt me.", "Je peux manger du verre, \u00E7a ne me fait pas mal.", "\u16D6\u16B4 \u16B7\u16D6\u16CF \u16D6\u16CF\u16C1 \u16A7 \u16B7\u16DA\u16D6\u16B1 \u16D8\u16BE \u16A6\u16D6\u16CB\u16CB \u16A8\u16A7 \u16A1\u16D6 \u16B1\u16A7\u16A8 \u16CB\u16A8\u16B1", "\u4EBA\u4EBA\u751F\u800C\u81EA\u7531,\u5728\u5C0A\u4E25\u548C\u6743\u5229\u4E0A\u4E00\u5F8B\u5E73\u7B49\u3002\u4ED6\u4EEC\u8D4B\u6709\u7406\u6027\u548C\u826F\u5FC3,\u5E76\u5E94\u4EE5\u5144\u5F1F\u5173\u7CFB\u7684\u7CBE\u795E\u4E92\u76F8\u5BF9\u5F85\u3002", "\u4EBA\u4EBA\u751F\u800C\u81EA\u7531\uFE50\u5728\u5C0A\u56B4\u548C\u6B0A\u5229\u4E0A\u4E00\u5F8B\u5E73\u7B49\u3002\u4ED6\u5011\u8CE6\u6709\u7406\u6027\u548C\u826F\u5FC3\uFE50\u4E26\u61C9\u4EE5\u5144\u5F1F\u95DC\u4FC2\u7684\u7CBE\u795E\u4E92\u76F8\u5C0D\u5F85\u3002", "\u0623\u0646\u0627 \u0642\u0627\u062F\u0631 \u0639\u0644\u0649 \u0623\u0643\u0644 \u0627\u0644\u0632\u062C\u0627\u062C \u0648 \u0647\u0630\u0627 \u0644\u0627 \u064A\u0624\u0644\u0645\u0646\u064A.", "\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04\uD83D\uDE05\uD83D\uDE06\uD83D\uDE20\uD83D\uDE21\uD83D\uDE22\uD83D\uDE23\uD83D\uDE24\uD83D\uDE25\uD83D\uDE28\uD83D\uDE29\uD83D\uDE2A\uD83D\uDE89\uD83D\uDE8C\uD83D\uDE8F\uD83D\uDE91\uD83D\uDE92\uD83D\uDE93\uD83D\uDE95\uD83D\uDE97\uD83D\uDE99\uD83D\uDE9A\uD83D\uDEA2\uD83D\uDEA4\uD83D\uDEA5\uD83D\uDEA7\uD83D\uDEA8\uD83D\uDEBB\uD83D\uDEBC\uD83D\uDEBD\uD83D\uDEBE\uD83D\uDEC0\uD83C\uDD95\uD83C\uDD96\uD83C\uDD97\uD83C\uDD98\uD83C\uDD99\uD83C\uDD9A\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE39\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE518\u20E39\u20E37\u20E36\u20E31\u20E30" }; private String data; /** * Dummy: just set a pure ascii string */ public Utf8Test() { this.data = "Utf8Test pure ASCII"; } /** * Instantiate the class with one of the DEFAULT strings. Use the index as * reference. * * @param indx reference to the DEFAULT_STRING */ public Utf8Test(int indx) { this.data = DEFAULT_STRINGS[Math.abs(indx) % DEFAULT_STRINGS.length]; } /** * Instantiate with a user-defined string * * @param myinput */ public Utf8Test(String myinput) { if (null == myinput) { this.data = "NULL INPUT"; } else { try { int indx = Integer.parseInt(myinput); this.data = DEFAULT_STRINGS[Math.abs(indx) % DEFAULT_STRINGS.length]; } catch (NumberFormatException nfe) { this.data = myinput; } } } public void print_system_info() { System.out.println("----------------------------------------------------------------------------------------"); System.out.println("JVM: " + System.getProperty("java.vm.name") + ", version: " + System.getProperty("java.version") + " (" + System.getProperty("java.vm.version") + ")"); System.out.println("OS: " + System.getProperty("os.name") + "-" + System.getProperty("os.arch") + ", version: " + System.getProperty("os.version")); System.out.println("----------------------------------------------------------------------------------------"); } public void print_to_stdout() { int nc = (int) this.data.codePoints().count(); int nb = this.data.getBytes().length; System.out.println(String.format("nc = %3d, nb = %3d: (%s)", nc, nb, this.data)); } /* * get the string defined by the instantiator */ public String get() { return this.data; } /* * return true if the string defined by the instantiator equals the default string with given index */ public boolean equalsTo(int indx) { return DEFAULT_STRINGS[Math.abs(indx) % DEFAULT_STRINGS.length].equals(this.data); } public static void main(String[] argv) { Utf8Test jp; new Utf8Test().print_system_info(); if (0 == argv.length) { new Utf8Test().print_to_stdout(); for (int i = 0; i < DEFAULT_STRINGS.length; i++) { new Utf8Test(i).print_to_stdout(); } } else { new Utf8Test(argv[0]).print_to_stdout(); } } } jpype-1.6.0/test/harness/jpype/varargs/000077500000000000000000000000001501674766700201065ustar00rootroot00000000000000jpype-1.6.0/test/harness/jpype/varargs/VarArgs.java000066400000000000000000000046641501674766700223300ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package jpype.varargs; import java.util.Map; class VarArgs { public Object[] rest; public static Object[] call(Object... args) { return args; } public static String[] callString(String... args) { return args; } public static int callString0(String str, String... args) { return args.length; } public int callString1(String str, String... args) { return args.length; } public static Integer callOverload(Integer i) { return i; } public static String[] callOverload(String str, String... rest) { return rest; } public VarArgs() { } public VarArgs(String s, Object... rest) { this.rest = rest; } public String[] method(String s, String... rest) { return rest; } public int conflict1(Object... j) { return 1; } public int conflict1(Map j) { return 2; } public int conflict2(Object... j) { return 1; } public int conflict2(Map j, Map k) { return 2; } public int conflict3(char... j) { return 1; } public int conflict3(String j) { return 2; } // Conflict 4 - overloaded multi-param signature, vararg of identical type public int conflict4(Object o, double... j) { return 1; } public int conflict4(Object o, double j) { return 2; } // Conflict 5 - overloaded single-param signature, vararg of identical type // NB: pre-patch, conflict4 deterministically generated an ambiguous match error. // However, conflict5 would arbitrarily pick between these two signatures when // called with a single variable, resulting in unpredictable failures. public int conflict5(double... j) { return 1; } public int conflict5(double j) { return 2; } } jpype-1.6.0/test/harness/org/000077500000000000000000000000001501674766700161015ustar00rootroot00000000000000jpype-1.6.0/test/harness/org/jpype/000077500000000000000000000000001501674766700172305ustar00rootroot00000000000000jpype-1.6.0/test/harness/org/jpype/fail/000077500000000000000000000000001501674766700201435ustar00rootroot00000000000000jpype-1.6.0/test/harness/org/jpype/fail/BadInitializer.java000066400000000000000000000015221501674766700237000ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.fail; public class BadInitializer { static { int[] i = new int[0]; i[1] = 2; } } jpype-1.6.0/test/harness/org/jpype/fail/BadInitializer2.java000066400000000000000000000017031501674766700237630ustar00rootroot00000000000000/* **************************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See NOTICE file for details. **************************************************************************** */ package org.jpype.fail; // To test this with more than one path, we need a second // copy. Java caches the result of the first attempt public class BadInitializer2 { static { int[] i = new int[0]; i[1] = 2; } } jpype-1.6.0/test/jar/000077500000000000000000000000001501674766700144235ustar00rootroot00000000000000jpype-1.6.0/test/jar/README000066400000000000000000000002601501674766700153010ustar00rootroot00000000000000This directory contains special jars that were formulated to test different aspects of the import system. They were constructed using resource in the jpype/project directory. jpype-1.6.0/test/jar/late/000077500000000000000000000000001501674766700153505ustar00rootroot00000000000000jpype-1.6.0/test/jar/late/late.jar000066400000000000000000000017541501674766700170020ustar00rootroot00000000000000PKΉ’}Q META-INF/ώΚPKPKΉ’}QMETA-INF/MANIFEST.MFσMΜΛLK-.Ρ K-*ΞΜΟ³R0Τ3ΰεr.JM,IMΡuͺ Eτ,υ 4B“JσJJ5yΉxΉPKΚΜP78PK Έ’}Qorg/PK Έ’}Q org/jpype/PK Έ’}Qorg/jpype/late/PKΈ’}Qorg/jpype/late/Test.classE?KAΕίδr=MLbee#I ―°TlAZ$–wΙxξqΉ —ΰ·B„ω~(qvm–™7ούfφλ{»pŽ£.Ί!Ϊ8 ΠσΡχ1 ΈOŠΛn ή₯ͺ”Ύ"8ΓΡ‘}]/˜Π™¨Šο6ˌ›Yš•’xKΦΟ΅„ΓΡ€H_€L«<™κFUω!šΦ›fΞ7ʘΓ―υ™qΕΖπΰΛ†G^ϊu“'ΕκuΕBМ+‘ϋΌΟ žkœΘΡ|BΞ4q©ZR MήHΊSΫΡψ4>Πz·ξ=£Ι p,c7sli2ι9ofΟJ{`—t~PKμμΫχ>PKΉ’}Q META-INF/ώΚPKΉ’}QΚΜP78=META-INF/MANIFEST.MFPK Έ’}QΆorg/PK Έ’}Q Ψorg/jpype/PK Έ’}Qorg/jpype/late/PKΈ’}QμμΫχ>-org/jpype/late/Test.classPKkkjpype-1.6.0/test/jar/late/late2.jar000066400000000000000000000017601501674766700170610ustar00rootroot00000000000000PKΉ’}Q META-INF/ώΚPKPKΉ’}QMETA-INF/MANIFEST.MFσMΜΛLK-.Ρ K-*ΞΜΟ³R0Τ3ΰεr.JM,IMΡuͺ Eτ,υ 4B“JσJJ5yΉxΉPKΚΜP78PK Ή’}Qorg/PK Ή’}Q org/jpype/PK Ή’}Qorg/jpype/late2/PKΉ’}Qorg/jpype/late2/Test.classEΟJΓ@ΖΏIšM“Fk=yςdΫƒ=*^A(υΠβ=iΧΈ!Ν–Έ|+=ˆΰΑπ‘ΔΩEτ2̟οϋΝΜΧχΗ'€3ĐFθ`―‹AˆύCBp―d½"Π A\¨F™K‚?ί:Wz% ύ©jδl».d»Θ‹š;b-ΝƒfΣp4žVωSžΥySfsΣͺ¦<'Δs½m—ςZYq΄ζΔͺt% ήL³]·eVmž7’FžfVJH‘·E%—G|tΐOπ™ΦΞ™ΗΣ8Ζ\»θMήA“TΌΑ{uςǘ‡€ο„;Ώ¦C‡γΙΐω Χ²ά]·₯PK—_Γχ?PKΉ’}Q META-INF/ώΚPKΉ’}QΚΜP78=META-INF/MANIFEST.MFPK Ή’}QΆorg/PK Ή’}Q Ψorg/jpype/PK Ή’}Qorg/jpype/late2/PKΉ’}Q—_Γχ?.org/jpype/late2/Test.classPKmmjpype-1.6.0/test/jar/missing.jar000066400000000000000000000010301501674766700165640ustar00rootroot00000000000000PK9œ‚QMETA-INF/MANIFEST.MFώΚσMΜΛLK-.Ρ K-*ΞΜΟ³R0Τ3ΰεr.JM,IMΡuͺ Eτ,υ 4B“JσJJ5yΉxΉPKΚΜP78PKž‚Qorg/jpype/missing/Test.class» Β@EοζibD+; ;΅p [±¬D ƒύ&.aC^δ!ψYV‚…ΰG‰³™;ά3w˜οού°ΑΘ‡ ίΕΠEΐΰlU‘ΪƒΉX^¬}y“ γ£*δ©Λ#Y‡"ʈψ—²«cyPΪx‘lΪu*ξ"€›aZΦ O«G%yšF Χ†‰ρL8G©Œ[ΜaΠ]ŒšIr³ήφκφμΧ.©ΣC“t@Σ€χPKρ€μ–«ΘPK9œ‚QΚΜP78META-INF/MANIFEST.MFώΚPKž‚Qρ€μ–«Θ}org/jpype/missing/Test.classPKrjpype-1.6.0/test/jar/mrjar.jar000066400000000000000000000051231501674766700162350ustar00rootroot00000000000000PKŒazQ META-INF/ώΚPKPKŒazQMETA-INF/MANIFEST.MFσMΜΛLK-.Ρ K-*ΞΜΟ³R0Τ3ΰεr.JM,IMΡuͺ Eτ,υ 4B“JσJJ5yΉ|KsJ2uƒRsR‹S­JŠJSyΉxΉPK/ͺ\ψLMPK ‹azQorg/PK ‹azQ org/jpype/PK ‹azQorg/jpype/mrjar/PK‹azQorg/jpype/mrjar/A.class;υoΧ>c^.f.vnvF6›ΜΌΜ;Ff Ν0Fηό”TF~ŸΜΌTΏά€Τ’Δ€ Wp~iQrͺ[&ˆΓζ¨—•X–ΘΓΐΒΐΚΘ ˜_”ŸUPYͺŸ[”•X€οΘΘ ’ΧΟIΜKΧχOΚJM.aPd`Ϊ Œ@Τ$Ω€l<5ρμ‘ν‘CpGΊΠ՘`α7Αž–kEhΟu‘ΫΐΏ’½+j½4~ο@υρ’έ:PK-cΦPK ‹azQorg/jpype/mrjar/sub/PK‹azQorg/jpype/mrjar/sub/C.class;υoΧ>c^.f.vnvF6›ΜΌΜ;Ff Ν0Fηό”TF~ŸΜΌTΏά€Τ’Δ€ Wp~iQrͺ[&ˆΓζ¬—•X–ΘΓΐΒΐΚΘ š_”ŸUPYͺŸ[”•X€_\š€οΜΘ R£Ÿ“˜—οŸ”•š\Β Θΐ΄¨H²y²`>«Φvƍ`iv Ιd’@š‰PK™qj«ΔPK ‹azQMETA-INF/versions/9/PK ‹azQMETA-INF/versions/9/org/PK ‹azQMETA-INF/versions/9/org/jpype/PK ‹azQ$META-INF/versions/9/org/jpype/mrjar/PK‹azQ+META-INF/versions/9/org/jpype/mrjar/B.class]޽jQ…ΟΈfc’†tba·›Β[₯ +ѐώξzYξ²?rY…ΌUR yJœApŠ33‡os<ώΌ£λΓΖSΟΪ:w€ ] V~μiΉV„φ\j±Ν#eΎd”±cΗ2Λ―A8OεNŠL‰XUFΙ'Α_•[«™Qw2¬‘Έϊ tK“ˆtσ³Q"7©4bBθάb–Qͺβ Xό\] P}ΜκρΦηNܝ·=θB“Υ½˜λό+Ϊ»’ΦKγχτX/Ω­3PKzYλΥPKŒazQ META-INF/ώΚPKŒazQ/ͺ\ψLM=META-INF/MANIFEST.MFPK ‹azQΛorg/PK ‹azQ νorg/jpype/PK ‹azQorg/jpype/mrjar/PK‹azQAθΒ§ΐCorg/jpype/mrjar/A.classPK‹azQ-cΦ/org/jpype/mrjar/B.classPK ‹azQJorg/jpype/mrjar/sub/PK‹azQ™qj«Δ|org/jpype/mrjar/sub/C.classPK ‹azQpMETA-INF/versions/9/PK ‹azQ’META-INF/versions/9/org/PK ‹azQΨMETA-INF/versions/9/org/jpype/PK ‹azQ$META-INF/versions/9/org/jpype/mrjar/PK‹azQzYλΥ+VMETA-INF/versions/9/org/jpype/mrjar/B.classPKΉ„jpype-1.6.0/test/jar/unicode_à😎/000077500000000000000000000000001501674766700202405ustar00rootroot00000000000000jpype-1.6.0/test/jar/unicode_à😎/sample_package.jar000066400000000000000000000037761501674766700237070ustar00rootroot00000000000000PK δq>Y META-INF/ώΚPKδq>YMETA-INF/MANIFEST.MFσMΜΛLK-.Ρ K-*ΞΜΟ³R0Τ3ΰεr.JM,IMΡuͺ΄R0Šθ™(h„&•ζ•”jςrρrPKVΟ076PK -n>Yorg/PK o>Y org/jpype/PK -n>Yorg/jpype/mrjar/PK£n>Yorg/jpype/mrjar/A.class;υoΧ>^.&fvV6F¬Δ²DύœΔΌt}€¬ΤδF6›ΜΌΜ;Ff Ν0vFΑό’tύ¬‚Κ‚Tύά’¬Δ"}GFηό”TF~ŸΜΌTΏά€Τ’Δ€ Wp~iQrͺ[&ˆΓ樲€A‘h/0!Πf Ι δΙ‚ω ¬ZΫ7‚₯Ή€$XHri&PK¨‚•§ΐPK£n>Yorg/jpype/mrjar/B.class]޽jQ…ΟΈfc’†tba·›ΒΫRD,¬D Cϊ»λeΉΛώΘeςVI%Xδ|(qVΑ)ΞΜ>fΞρtψπŽ,v \B'•;)2Y$b₯*ξHΊ¬ όn’I >‘[šD€›ŸΉI₯‚=-׊ОλB-Άy€Μ—Œ2vμXfα5η·«Κθ"ω$ψ«rkb5Σ5κN†5‚8\] Pυ‘·>wβξΌνA<Z¬ξΕ΄XŸπ|E{WΤzρ~ο@΅}Ήέ9PK§9WΦPK o>Yorg/jpype/sample_package/PKδq>Y org/jpype/sample_package/A.class;υoΧ>^.&fvV6F¬Δ²DύœΔΌt}€¬ΤδF6›ΜΌΜ;Ff Ν0vF©ό’tύ¬‚Κ‚TύβΔά‚œΤψ‚ΔδμΔτT}GFηό”TF~ŸΜΌTΏά€Τ’Δ€ Wp~iQrͺ[&ˆΓ樲‰A‘θ`B €$'' ζ30°jmg`ά–ζ’l`Af Ι €™xPKekkc°ΙPKδq>Y org/jpype/sample_package/B.class;υoΧ>^.&fvV6F¬Δ²DύœΔΌt}€¬ΤδF6›ΜΌΜ;Ff Ν0vF©ό’tύ¬‚Κ‚TύβΔά‚œΤψ‚ΔδμΔτT}'Fηό”TF~ŸΜΌTΏά€Τ’Δ€ Wp~iQrͺ[&ˆΓζ€²‰A‘θ`B €$'' ζ30°jmg`ά–ζ’l`Af Ι €™xPKJwΩΧ°ΙPK δq>Y META-INF/ώΚPKδq>YVΟ076+META-INF/MANIFEST.MFPK -n>Y€org/PK o>Y Ζorg/jpype/PK -n>Yξorg/jpype/mrjar/PK£n>Y¨‚•§ΐorg/jpype/mrjar/A.classPK£n>Y§9WΦorg/jpype/mrjar/B.classPK o>Y#org/jpype/sample_package/PKδq>Yekkc°Ι Zorg/jpype/sample_package/A.classPKδq>YJwΩΧ°Ι Xorg/jpype/sample_package/B.classPK ’Vjpype-1.6.0/test/jar/unicode_à😎/service.jar000066400000000000000000000027451501674766700224060ustar00rootroot00000000000000PK―“aY META-INF/PK―“aY²ξMETA-INF/MANIFEST.MFσMΜΛLK-.Ρ K-*ΞΜΟ³R0Τ3ΰεβεPK“aYorg/PK“aY org/jpype/PK“aYorg/jpype/service/PK“aY(€oνΓn.org/jpype/service/JpypeZoneRulesProvider.class•RMoΣ@œ—€±cLۘ†―h)-N…΄nrΙ T>DB%rs›ΕΪΘΨ‘½‰Ώͺ\h?€…xλ%” KήΡϊΝ›7ή?Ώ~π*¨Z¨ΉXA°= §‘ΠκƒŸDŠ/o&±Μ_gιT eF¨ͺDι‘귏mΨοωψγXφeEŸ;M£Χ,Έ.c•Π*d'ZΕ’›Ζ±<Υ*MrB#WIK&„]¨`Εa‰W'#&ν£ycOκΐΖ:ŸϋΆίuΰα†… -ά$¬ΟmΗΟ†„Jϊž°Ώ¨ΩΣŸφ[s΅Ϋ.ξΰ.ΑŽ€.&&lω‹ά?σ,lšs{ύLΚαΨΑ¦ΙσΑO³HŒL2"—ΩTJQδ΄$Z7JΒκxφeζŠ 4ύΛpd=%‘ždά°wΉ~ψχΈξqKαr.±$•Α?F%¬•Η2ΛgξΙΥΙf^†S…'±Ι„A_2ΔΥή:vpŸ/±yj syέζ]“‘WΞAŸKΒ2Ω”½²\χ¬ 4Ίϊ.Ώ '3': l = int(123) else: l = long(123) h.setByte(l) self.assertEqual(l, h.mByteValue) h.setShort(l) self.assertEqual(l, h.mShortValue) h.setInt(l) self.assertEqual(l, h.mIntValue) h.setLong(l) self.assertEqual(l, h.mLongValue) def testCallWithBigLong(self): h = JClass('jpype.attr.Test1')() if sys.version > '3': l = int(4398046511103) else: l = long(4398046511103) self.assertRaises(OverflowError, h.setByte, l) self.assertRaises(OverflowError, h.setShort, l) self.assertRaises(OverflowError, h.setInt, l) h.setLong(l) self.assertEqual(l, h.mLongValue) def testCallWithBigInt(self): h = JClass('jpype.attr.Test1')() if sys.version > '3' or sys.maxint > 2**31: l = int(4398046511103) else: l = long(4398046511103) self.assertRaises(OverflowError, h.setByte, l) self.assertRaises(OverflowError, h.setShort, l) self.assertRaises(OverflowError, h.setInt, l) h.setLong(l) self.assertEqual(l, h.mLongValue) def testSetBoolean(self): h = JClass('jpype.attr.Test1')() self.assertEqual(False, h.mBooleanValue) h.setBoolean(True) self.assertEqual(True, h.mBooleanValue) h.setBoolean(False) self.assertEqual(False, h.mBooleanValue) # just testing the status quo, not sure about if this is nice h.setBoolean(42) self.assertEqual(True, h.mBooleanValue) h.setBoolean(0) self.assertEqual(False, h.mBooleanValue) if sys.version > '3': l = int(4398046511103) else: l = long(4398046511103) h.setBoolean(l) self.assertEqual(True, h.mBooleanValue) if sys.version > '3': l = int(0) else: l = long(0) h.setBoolean(l) self.assertEqual(False, h.mBooleanValue) def testCreateDate(self): d = jpype.java.util.Date(1448799485000) self.assertEqual(1448799485000, d.getTime()) def testCharAttribute(self): h = JClass('jpype.attr.Test1')() h.charValue = u'b' self.assertEqual(h.charValue, 'b') if sys.version < '3': exp_repr = "u'b'" else: exp_repr = "'b'" self.assertEqual(repr(h.charValue), exp_repr) def testGetPrimitiveType(self): Integer = jpype.JClass("java.lang.Integer") intType = Integer.TYPE def testDifferentiateClassAndObject(self): h = JClass('jpype.attr.Test1')() self.assertEqual(h.callWithSomething( JClass('jpype.attr.Test1')), u"Class") result = h.callWithSomething(jpype.JObject(JClass('jpype.attr.Test1'), jpype.java.lang.Object)) self.assertEqual(result, u"Object") def testToString(self): h = JClass('jpype.attr.Test1')() self.assertEqual(str(h), 'aaa') def testSuperToString(self): h = JClass('jpype.attr.Test2')() self.assertEqual(str(h), 'aaa') # def testStringToConversion(self): # try: # jpype.ConversionConfig.string = False # for i in range(1): # h = JClass('jpype.attr.Test1')() # # start = time.time(); # for j in range(10): # ts = h.getBigString() # stop = time.time() # print ts.__class__, (stop-start), 'ms' # # # for comparison ... convert a string to JStrng and back # s = "".join([" "]*1024*1024*5) # start = time.time() # js = JString(s) # stop = time.time() # print "converted to JString in", (stop-start), 'ms', len(s) # # start = time.time() # cs = str(JString(s)) # print cs # print "converted to String in", (stop-start), 'ms', len(cs), cs # finally: # jpype.ConversionConfig.string = True def testComplexMethodOvlerloading(self): c = JClass('jpype.attr.TestOverloadC')() self.assertEqual(c.foo(1), "foo(int) in C: 1") self.assertEqual(c.foo(), "foo() in A") def testPassedObjectGetsCleanedUp(self): import platform if platform.python_implementation() == 'PyPy': raise common.unittest.SkipTest( 'PyPy memory model does not pass test') h = JClass('jpype.attr.Test1')() block_size = 1024 * 1024 * 10 def allocate_then_free(): byte_buffer = jpype.JClass('java.nio.ByteBuffer') inst = byte_buffer.allocate(1024 * 1024 * 100) # passing the object back to java seems to stop it being collected result = h.callWithSomething(inst) rt = jpype.java.lang.Runtime.getRuntime() free = rt.freeMemory() for x in range(0, 10 * free // block_size): allocate_then_free() def testSyntheticMethod(self): h = jpype.JClass('jpype.attr.SyntheticMethods$GenericImpl')() h.foo(jpype.java.util.ArrayList()) jpype-1.6.0/test/jpypetest/test_boxed.py000066400000000000000000000370661501674766700204240ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common from jpype.types import * from jpype import java def passThrough(item): al = JClass("java.util.ArrayList")() al.add(item) return al.get(0) class BoxedTestCase(common.JPypeTestCase): __name__ = "BoxedTestCase" def setUp(self): common.JPypeTestCase.setUp(self) self.TestBoxed = jpype.JClass('jpype.boxed.Boxed') self.Number = jpype.JClass('java.lang.Number') self.Comparable = jpype.JClass('java.lang.Comparable') def testShort(self): c1 = 12345 # Check passed from and passed to d1 = self.TestBoxed.newShort(c1) d2 = java.lang.Short(c1) self.assertEqual(d1, c1) self.assertEqual(d2, c1) self.assertEqual(c1, d1) self.assertEqual(c1, d2) self.assertEqual(d1, d2) self.assertEqual(self.TestBoxed.callShort(c1), self.TestBoxed.callShort(d2)) # Verify ops self.assertEqual(d1 + 2, d1 + 2) self.assertEqual(d1 * 2, d1 * 2) def testInteger(self): c1 = 12345 # Check passed from and passed to d1 = self.TestBoxed.newInteger(c1) d2 = java.lang.Integer(c1) self.assertEqual(d1, c1) self.assertEqual(d2, c1) self.assertEqual(c1, d1) self.assertEqual(c1, d2) self.assertEqual(d1, d2) self.assertEqual(self.TestBoxed.callInteger(c1), self.TestBoxed.callInteger(d2)) # Verify ops self.assertEqual(d1 + 2, d1 + 2) self.assertEqual(d1 * 2, d1 * 2) def testLong(self): c1 = 12345 # Check passed from and passed to d1 = self.TestBoxed.newLong(c1) d2 = java.lang.Long(c1) self.assertEqual(d1, c1) self.assertEqual(d2, c1) self.assertEqual(c1, d1) self.assertEqual(c1, d2) self.assertEqual(d1, d2) self.assertEqual(self.TestBoxed.callLong(c1), self.TestBoxed.callLong(d2)) # Verify ops self.assertEqual(d1 + 2, d1 + 2) self.assertEqual(d1 * 2, d1 * 2) def testDoubleFromFloat(self): java.lang.Double(1.0) def testFloatFromInt(self): java.lang.Float(1) def testDoubleFromInt(self): java.lang.Double(1) def testBoxed2(self): java.lang.Short(java.lang.Integer(1)) java.lang.Integer(java.lang.Integer(1)) java.lang.Long(java.lang.Integer(1)) java.lang.Float(java.lang.Integer(1)) java.lang.Float(java.lang.Long(1)) java.lang.Double(java.lang.Integer(1)) java.lang.Double(java.lang.Long(1)) java.lang.Double(java.lang.Float(1)) def testFloat(self): c1 = 123124 / 256.0 # Check passed from and passed to d1 = self.TestBoxed.newFloat(c1) d2 = java.lang.Float(c1) self.assertEqual(d1, c1) self.assertEqual(d2, c1) self.assertEqual(c1, d1) self.assertEqual(c1, d2) self.assertEqual(d1, d2) self.assertEqual(self.TestBoxed.callFloat(c1), self.TestBoxed.callFloat(d2)) # Verify ops self.assertEqual(d1 + 2, d1 + 2) self.assertEqual(d1 * 2, d1 * 2) self.assertTrue(d2 < c1 + 1) self.assertTrue(d2 > c1 - 1) def testDouble(self): c1 = 123124 / 256.0 # Check passed from and passed to d1 = self.TestBoxed.newDouble(c1) d2 = java.lang.Double(c1) self.assertEqual(d1, c1) self.assertEqual(d2, c1) self.assertEqual(c1, d1) self.assertEqual(c1, d2) self.assertEqual(d1, d2) self.assertEqual(self.TestBoxed.callDouble(c1), self.TestBoxed.callDouble(d2)) # Verify ops self.assertEqual(d1 + 2, d1 + 2) self.assertEqual(d1 * 2, d1 * 2) self.assertTrue(d2 < c1 + 1) self.assertTrue(d2 > c1 - 1) def testShortResolve(self): self.assertEqual(self.TestBoxed.whichShort(1), 1) self.assertEqual(self.TestBoxed.whichShort(java.lang.Short(1)), 2) def testIntegerResolve(self): self.assertEqual(self.TestBoxed.whichInteger(1), 1) self.assertEqual(self.TestBoxed.whichInteger(java.lang.Integer(1)), 2) def testLongResolve(self): self.assertEqual(self.TestBoxed.whichLong(1), 1) self.assertEqual(self.TestBoxed.whichLong(java.lang.Long(1)), 2) def testFloatResolve(self): self.assertEqual(self.TestBoxed.whichFloat(1.0), 1) self.assertEqual(self.TestBoxed.whichFloat(java.lang.Float(1.0)), 2) def testDoubleResolve(self): self.assertEqual(self.TestBoxed.whichDouble(1.0), 1) self.assertEqual(self.TestBoxed.whichDouble(java.lang.Double(1.0)), 2) def testPrivitiveToBoxed(self): java.lang.Boolean(JBoolean(0)) java.lang.Byte(JByte(0)) java.lang.Short(JShort(0)) java.lang.Integer(JInt(0)) java.lang.Long(JLong(0)) java.lang.Float(JFloat(0)) java.lang.Double(JDouble(0)) def testBooleanBad(self): # java.lang.Boolean(X) works like bool(X) # Explicit is a cast self.assertFalse(java.lang.Boolean(tuple())) self.assertFalse(java.lang.Boolean(list())) self.assertFalse(java.lang.Boolean(dict())) self.assertFalse(java.lang.Boolean(set())) self.assertTrue(java.lang.Boolean(tuple(['a']))) self.assertTrue(java.lang.Boolean(['a'])) self.assertTrue(java.lang.Boolean({'a': 1})) self.assertTrue(java.lang.Boolean(set(['a', 'b']))) # Implicit does not automatically cast fixture = JClass('jpype.common.Fixture')() with self.assertRaises(TypeError): fixture.callBoxedBoolean(tuple()) with self.assertRaises(TypeError): fixture.callBoxedBoolean(list()) with self.assertRaises(TypeError): fixture.callBoxedBoolean(dict()) with self.assertRaises(TypeError): fixture.callBoxedBoolean(set()) def testByteBad(self): with self.assertRaises(TypeError): java.lang.Byte(tuple()) def testCharacterBad(self): with self.assertRaises(TypeError): java.lang.Character(tuple()) def testShortBad(self): with self.assertRaises(TypeError): java.lang.Short(tuple()) def testIntegerBad(self): with self.assertRaises(TypeError): java.lang.Integer(tuple()) def testLongBad(self): with self.assertRaises(TypeError): java.lang.Long(tuple()) def testFloatBad(self): with self.assertRaises(TypeError): java.lang.Float(tuple()) def testDoubleBad(self): with self.assertRaises(TypeError): java.lang.Double(tuple()) def testBooleanBad2(self): with self.assertRaises(TypeError): java.lang.Boolean(tuple(), tuple()) def testByteBad2(self): with self.assertRaises(TypeError): java.lang.Byte(tuple(), tuple()) def testCharacterBad2(self): with self.assertRaises(TypeError): java.lang.Character(tuple(), tuple()) def testShortBad2(self): with self.assertRaises(TypeError): java.lang.Short(tuple(), tuple()) def testIntegerBad2(self): with self.assertRaises(TypeError): java.lang.Integer(tuple(), tuple()) def testLongBad2(self): with self.assertRaises(TypeError): java.lang.Long(tuple(), tuple()) def testFloatBad2(self): with self.assertRaises(TypeError): java.lang.Float(tuple(), tuple()) def testDoubleBad2(self): with self.assertRaises(TypeError): java.lang.Double(tuple(), tuple()) def compareTest(self, u, v): self.assertEqual(u, v) self.assertNotEqual(u, v - 1) self.assertTrue(u > v - 1) self.assertFalse(u > v + 1) self.assertTrue(u >= v) self.assertTrue(u <= v) self.assertFalse(u < v) self.assertFalse(u > v) self.assertTrue(u < v + 1) self.assertTrue(u > v - 1) def testByteBoxOps(self): u = JObject(81, JByte) self.assertIsInstance(u, jpype.java.lang.Byte) self.compareTest(u, 81) def testCharBoxOps(self): u = JObject('Q', JChar) self.assertIsInstance(u, jpype.java.lang.Character) self.compareTest(u, 81) def testShortBoxOps(self): u = JObject(81, JShort) self.assertIsInstance(u, jpype.java.lang.Short) self.compareTest(u, 81) def testIntBoxOps(self): u = JObject(81, JInt) self.assertIsInstance(u, jpype.java.lang.Integer) self.compareTest(u, 81) def testLongBoxOps(self): u = JObject(81, JLong) self.assertIsInstance(u, jpype.java.lang.Long) self.compareTest(u, 81) def testIntBoxOps_2(self): u = JObject(81, JFloat) self.assertIsInstance(u, jpype.java.lang.Float) self.compareTest(u, 81) def testLongBoxOps_2(self): u = JObject(81, JDouble) self.assertIsInstance(u, jpype.java.lang.Double) self.compareTest(u, 81) def testCharBox(self): u = passThrough(JChar('Q')) self.assertIsInstance(u, jpype.java.lang.Character) self.assertEqual(u, jpype.java.lang.Character('Q')) def testBooleanBox(self): u = passThrough(JBoolean(True)) self.assertIsInstance(u, jpype.java.lang.Boolean) self.assertEqual(u, jpype.java.lang.Boolean(True)) self.assertEqual(u, True) u = passThrough(JBoolean(False)) self.assertIsInstance(u, jpype.java.lang.Boolean) self.assertEqual(u, jpype.java.lang.Boolean(False)) self.assertEqual(u, False) def testByteBox(self): u = passThrough(JByte(5)) self.assertIsInstance(u, java.lang.Byte) self.assertEqual(u, java.lang.Byte(5)) def testShortBox(self): u = passThrough(JShort(5)) self.assertIsInstance(u, java.lang.Short) self.assertEqual(u, java.lang.Short(5)) def testIntBox(self): u = passThrough(JInt(5)) self.assertIsInstance(u, java.lang.Integer) self.assertEqual(u, java.lang.Integer(5)) def testLongBox(self): u = passThrough(JLong(5)) self.assertIsInstance(u, java.lang.Long) self.assertEqual(u, java.lang.Long(5)) def testFloatBox(self): u = passThrough(JFloat(5)) self.assertIsInstance(u, java.lang.Float) self.assertEqual(u, java.lang.Float(5)) def testDoubleBox(self): u = passThrough(JDouble(5)) self.assertIsInstance(u, java.lang.Double) self.assertEqual(u, java.lang.Double(5)) def testBooleanNull(self): n = JObject(None, JBoolean) self.assertIsInstance(n, java.lang.Boolean) self.assertEqual(n, None) self.assertNotEqual(n, True) self.assertNotEqual(n, False) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testCharNull(self): n = JObject(None, JChar) self.assertIsInstance(n, java.lang.Character) self.assertNotEqual(n, 0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testByteNull(self): n = JObject(None, JByte) self.assertIsInstance(n, java.lang.Byte) self.assertNotEqual(n, 0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testShortNull(self): n = JObject(None, JShort) self.assertIsInstance(n, java.lang.Short) self.assertNotEqual(n, 0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testIntNull(self): n = JObject(None, JInt) self.assertIsInstance(n, java.lang.Integer) self.assertNotEqual(n, 0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testLongNull(self): n = JObject(None, JLong) self.assertIsInstance(n, java.lang.Long) self.assertNotEqual(n, 0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testFloatNull(self): n = JObject(None, JFloat) self.assertIsInstance(n, java.lang.Float) self.assertNotEqual(n, 0) self.assertNotEqual(n, 0.0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testDoubleNull(self): n = JObject(None, JDouble) self.assertIsInstance(n, java.lang.Double) self.assertNotEqual(n, 0) self.assertNotEqual(n, 0.0) with self.assertRaises(TypeError): int(n) with self.assertRaises(TypeError): float(n) self.assertEqual(str(n), str(None)) self.assertEqual(repr(n), str(None)) self.assertEqual(hash(n), hash(None)) u = passThrough(n) self.assertEqual(u, None) def testAsNumber(self): self.assertIsInstance(java.lang.Byte(1), java.lang.Number) self.assertIsInstance(java.lang.Short(1), java.lang.Number) self.assertIsInstance(java.lang.Integer(1), java.lang.Number) self.assertIsInstance(java.lang.Long(1), java.lang.Number) self.assertIsInstance(java.lang.Float(1), java.lang.Number) self.assertIsInstance(java.lang.Double(1), java.lang.Number) jpype-1.6.0/test/jpypetest/test_buffer.py000066400000000000000000000366251501674766700205740ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import sys import logging import time import common from jpype.types import * try: import numpy as np gotNP = True except ImportError: gotNP = False def haveNumpy(): return gotNP class BufferTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testToMemoryview(self): data = [1, 2, 3, 4, 5] ja = JArray(JInt)(data) m1 = memoryview(ja) def testDoubleOpen(self): data = [1, 2, 3, 4, 5] ja = JArray(JInt)(data) m1 = memoryview(ja) m2 = memoryview(ja) del m1 del m2 def testBytesToBytes(self): data = [0, 1, 2, 3, 4, 5] ja = JArray(JByte)(data) self.assertEqual(len(bytes(ja)), 6) def testShortToBytes(self): data = [0, 1, 2, 3, 4, 5] ja = JArray(JShort)(data) self.assertEqual(len(bytes(ja)), 6 * 2) def testIntToBytes(self): data = [0, 1, 2, 3, 4, 5] ja = JArray(JInt)(data) self.assertEqual(len(bytes(ja)), 6 * 4) def testLongToBytes(self): data = [0, 1, 2, 3, 4, 5] ja = JArray(JLong)(data) self.assertEqual(len(bytes(ja)), 6 * 8) def testMemoryViewWriteShort(self): data = [1, 2, 3, 4, 5] ja = JArray(JShort)(data) b = memoryview(ja).cast("B") # Create a view with self.assertRaises(TypeError): b[0] = 123 # Alter the memory using the view def testMemoryViewWriteInt(self): data = [1, 2, 3, 4, 5] ja = JArray(JInt)(data) b = memoryview(ja).cast("B") # Create a view with self.assertRaises(TypeError): b[0] = 123 # Alter the memory using the view def testMemoryViewWriteLong(self): data = [1, 2, 3, 4, 5] ja = JArray(JLong)(data) b = memoryview(ja).cast("B") # Create a view with self.assertRaises(TypeError): b[0] = 123 # Alter the memory using the view @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testSlice(self): data = [0, 1, 2, 3, 4, 5] ja = JArray(JInt)(data) self.assertTrue(bytes(ja[1:3]) == bytes( np.array(data[1:3], dtype=np.int32))) self.assertTrue(bytes(ja[1:]) == bytes( np.array(data[1:], dtype=np.int32))) self.assertTrue(bytes(ja[:-1]) == bytes(np.array(data[:-1], dtype=np.int32))) self.assertTrue(bytes(ja[::2]) == bytes( np.array(data[::2], dtype=np.int32))) self.assertTrue(bytes(ja[::-1]) == bytes(np.array(data[::-1], dtype=np.int32))) self.assertTrue(bytes(ja[::-2]) == bytes(np.array(data[::-2], dtype=np.int32))) def executeConvert(self, jtype, dtype): n = 100 na = np.random.randint(0, 1, size=n).astype(np.bool_) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(-2**7, 2**7 - 1, size=n, dtype=np.int8) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(0, 2**8 - 1, size=n, dtype=np.uint8) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(-2**15, 2**15 - 1, size=n, dtype=np.int16) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(0, 2**16 - 1, size=n, dtype=np.uint16) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(-2**31, 2**31 - 1, size=n, dtype=np.int32) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(0, 2**32 - 1, size=n, dtype=np.uint32) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(-2**63, 2**63 - 1, size=n, dtype=np.int64) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(0, 2**64 - 1, size=n, dtype=np.uint64) self.assertTrue(np.allclose(np.array(jtype(na), dtype=dtype), np.array(na, dtype=dtype), atol=1e-8)) na = np.random.random(n).astype(np.float32) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.random(n).astype(np.float64) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBoolConvert(self): self.executeConvert(JArray(JBoolean), np.bool_) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testByteConvert(self): self.executeConvert(JArray(JByte), np.int8) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortConvert(self): self.executeConvert(JArray(JShort), np.int16) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntConvert(self): self.executeConvert(JArray(JInt), np.int32) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongConvert(self): self.executeConvert(JArray(JLong), np.int64) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatConvert(self): self.executeConvert(JArray(JFloat), np.float32) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testDoubleConvert(self): self.executeConvert(JArray(JDouble), np.float64) def executeIntTest(self, jtype, limits, size, dtype, code): data = np.random.randint(limits[0], limits[1], size=size, dtype=dtype) a = JArray(jtype, data.ndim)(data.tolist()) u = np.array(a) mv = memoryview(a) self.assertEqual(mv.format, code, "Type issue %s" % type(a)) self.assertEqual(mv.shape, data.shape) self.assertTrue(mv.readonly) self.assertEqual(u.dtype.type, dtype, "Problem with %s %s" % (jtype, dtype)) self.assertTrue(np.all(data == u)) def executeFloatTest(self, jtype, size, dtype, code): data = np.random.rand(*size).astype(dtype) a = JArray(jtype, data.ndim)(data) u = np.array(a) self.assertTrue(np.all(data == u)) mv = memoryview(a) self.assertEqual(mv.format, code) self.assertEqual(mv.shape, data.shape) self.assertTrue(mv.readonly) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBooleanToNP1D(self): self.executeIntTest(JBoolean, [0, 1], (100,), np.bool_, "?") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testCharToNP1D(self): self.executeIntTest(JChar, [0, 2**16], (100,), np.uint16, "H") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testByteToNP1D(self): self.executeIntTest(JByte, [-2**7, 2**7 - 1], (100,), np.int8, "b") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortToNP1D(self): self.executeIntTest(JShort, [-2**15, 2**15 - 1], (100,), np.int16, "h") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntToNP1D(self): self.executeIntTest(JInt, [-2**31, 2**31 - 1], (100,), np.int32, "=i") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP1D(self): self.executeIntTest(JLong, [-2**63, 2**63 - 1], (100,), np.int64, "=q") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP1D(self): self.executeFloatTest(JFloat, (100,), np.float32, "f") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testDoubleToNP1D(self): self.executeFloatTest(JDouble, (100,), np.float64, "d") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBooleanToNP2D(self): self.executeIntTest(JBoolean, [0, 1], (11, 10), np.bool_, "?") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testCharToNP2D(self): self.executeIntTest(JChar, [0, 2**16], (11, 10), np.uint16, "H") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testByteToNP2D(self): self.executeIntTest(JByte, [-2**7, 2**7 - 1], (11, 10), np.int8, "b") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortToNP2D(self): self.executeIntTest(JShort, [-2**15, 2**15 - 1], (11, 10), np.int16, "h") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntToNP2D(self): self.executeIntTest(JInt, [-2**31, 2**31 - 1], (11, 10), np.int32, "=i") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP2D(self): self.executeIntTest(JLong, [-2**63, 2**63 - 1], (11, 10), np.int64, "=q") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP2D(self): self.executeFloatTest(JFloat, (11, 10), np.float32, "f") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testDoubleToNP2D(self): self.executeFloatTest(JDouble, (11, 10), np.float64, "d") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBooleanToNP3D(self): self.executeIntTest(JBoolean, [0, 1], (11, 10, 9), np.bool_, "?") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testCharToNP3D(self): self.executeIntTest(JChar, [0, 2**16], (11, 10, 9), np.uint16, "H") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testByteToNP3D(self): self.executeIntTest(JByte, [-2**7, 2**7 - 1], (11, 10, 9), np.int8, "b") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortToNP3D(self): self.executeIntTest(JShort, [-2**15, 2**15 - 1], (11, 10, 9), np.int16, "h") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntToNP3D(self): self.executeIntTest(JInt, [-2**31, 2**31 - 1], (11, 10, 9), np.int32, "=i") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP3D(self): self.executeIntTest(JLong, [-2**63, 2**63 - 1], (11, 10, 9), np.int64, "=q") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP3D(self): self.executeFloatTest(JFloat, (11, 10, 9), np.float32, "f") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testDoubleToNP3D(self): self.executeFloatTest(JDouble, (11, 10, 9), np.float64, "d") def executeOrder(self, jtype, dtype): a = np.array([1, 2, 3]) ja2 = jtype[:](a) for order in ("=", "<", ">"): dt = np.dtype(dtype).newbyteorder(order) a = np.array([1, 2, 3], dtype=dt) ja = jtype[:](a) self.assertTrue(jpype.java.util.Arrays.equals(ja, ja2), "Order issue with %s %s" % (jtype, dtype)) @common.requireNumpy def testOrder(self): for i in (jpype.JBoolean, jpype.JByte, jpype.JShort, jpype.JInt, jpype.JLong, jpype.JFloat, jpype.JDouble): for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64, np.float32, np.float64): self.executeOrder(i, j) for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64): self.executeOrder(jpype.JChar, j) def testMemoryByte(self): mv = memoryview(bytes(256)) jtype = jpype.JByte[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryInt(self): mv = memoryview(bytes(256)) jtype = jpype.JInt[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": print(dtype) jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryShort(self): mv = memoryview(bytes(256)) jtype = jpype.JShort[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryLong(self): mv = memoryview(bytes(256)) jtype = jpype.JLong[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryFloat(self): mv = memoryview(bytes(256)) jtype = jpype.JFloat[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryDouble(self): mv = memoryview(bytes(256)) jtype = jpype.JDouble[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryBoolean(self): mv = memoryview(bytes(256)) jtype = jpype.JBoolean[:] # Simple checks for dtype in "c?bBhHiIlLqQfdnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) for dtype in "spP": with self.assertRaises(Exception): jtype(mv.cast(dtype)) def testMemoryChar(self): mv = memoryview(bytes(256)) jtype = jpype.JChar[:] # Simple checks for dtype in "c?bBhHiIlLqQnN": jtype(mv.cast(dtype)) jtype(mv.cast("@" + dtype)) jtype(mv.cast("P")) for dtype in ("s", "p", "f", "d", "@f", "@d"): with self.assertRaises(Exception): jtype(mv.cast(dtype)) jpype-1.6.0/test/jpypetest/test_bytebuffer.py000066400000000000000000000047121501674766700214500ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import _jpype import common class ByteBufferCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testConvertToDirectBuffer(self): a = bytearray([0, 0, 0, 0]) bb = jpype.nio.convertToDirectBuffer(a) self.assertIsInstance(bb, jpype.JClass("java.nio.DirectByteBuffer")) self.assertFalse(bb.isReadOnly()) bb.put(1) bb.put(2) bb.put(3) bb.put(4) self.assertEqual(a, bytearray([1, 2, 3, 4])) with self.assertRaises(jpype.JException): bb.put(5) def testConvertToReadonlyBuffer(self): a = bytes([1, 2, 3, 4]) bb = jpype.nio.convertToDirectBuffer(a) self.assertIsInstance(bb, jpype.JClass("java.nio.DirectByteBuffer")) self.assertTrue(bb.isReadOnly()) self.assertEqual(a, bytearray([1, 2, 3, 4])) with self.assertRaises(jpype.JException): bb.put(0, 1) def testConvertMemoryViewToReadonlyBuffer(self): a = bytearray([1, 2, 3, 4]) a = memoryview(a).toreadonly() bb = jpype.nio.convertToDirectBuffer(a) self.assertIsInstance(bb, jpype.JClass("java.nio.DirectByteBuffer")) self.assertTrue(bb.isReadOnly()) self.assertEqual(a, bytearray([1, 2, 3, 4])) with self.assertRaises(jpype.JException): bb.put(0, 1) def testRepr(self): a = bytearray([0, 0, 0, 0]) bb = jpype.nio.convertToDirectBuffer(a) self.assertIsInstance(repr(bb), str) self.assertEqual(repr(bb), "") def testMemoryView(self): self.assertEqual(memoryview(jpype.java.nio.ByteBuffer.allocateDirect(100)).nbytes, 100) jpype-1.6.0/test/jpypetest/test_caller_sensitive.py000066400000000000000000000100271501674766700226420ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class JCallerSensitiveCase(common.JPypeTestCase): """ Test for caller sensitive methods. Java uses a special pattern to deal with the security manager. It uses the stack to find the callers class and then decides if the call is permitted. But when we call from JNI there is no class. Thus, we need a special caller pattern which proxies to Java and then calls the method. This alternative method has to be tested against all of the different patterns (static, member), returning (void, primitive, object), called with (nothing, object, primitive, many, varargs) Unfortunately, the actual problematic method in Java is private, so we can't get to it directly. Thus will will perform indirect tests. For now we do not support caller sensitive constructors. """ def setUp(self): common.JPypeTestCase.setUp(self) if not jpype.getJVMVersion() > (1, 8, 0): raise common.unittest.SkipTest self.Class = jpype.JClass("jpype.method.Caller") self.obj = self.Class() def testCallStatic(self): self.assertIsInstance(self.Class.callObjectStatic(), self.Class) def testCallStaticAsMember(self): self.assertIsInstance(self.obj.callObjectStatic(), self.Class) def testCallMember(self): self.assertIsInstance(self.obj.callObjectMember(), self.Class) def testCallMemberFromClass(self): self.assertIsInstance( self.Class.callObjectMember(self.obj), self.Class) def testCallVoidStatic(self): self.assertEqual(self.Class.callVoidStatic(), None) def testCallVoidStaticAsMember(self): self.assertEqual(self.obj.callVoidStatic(), None) def testCallVoidMemberFromClass(self): self.assertEqual(self.Class.callVoidMember(self.obj), None) def testCallIntegerStatic(self): self.assertEqual(self.Class.callIntegerStatic(), 123) def testCallIntegerMember(self): self.assertEqual(self.obj.callIntegerMember(), 123) def testCallIntegerStaticAsMember(self): self.assertEqual(self.obj.callIntegerStatic(), 123) def testCallIntegerMemberFromClass(self): self.assertEqual(self.Class.callIntegerMember(self.obj), 123) def testArgs(self): self.assertEqual(self.obj.callArgs(1, 2), 2) def testArgsFromClass(self): self.assertEqual(self.Class.callArgs(self.obj, 1, 2), 2) def testPrimitive(self): self.assertEqual(self.obj.callArg1(123), 123) def testPrimitiveFromClass(self): self.assertEqual(self.Class.callArg1(self.obj, 125), 125) def testVarArgs(self): self.assertEqual(tuple(self.obj.callVarArgs(1, 2, 3)), (2, 3)) def testVarArgsFromClass(self): self.assertEqual( tuple(self.Class.callVarArgs(self.obj, 1, 2, 3)), (2, 3)) def testDeclaredMethod(self): self.assertIsInstance(jpype.java.lang.Object.class_.getDeclaredMethod( 'wait'), jpype.java.lang.reflect.Method) def testStackWalker1(self): with self.assertRaises(jpype.java.lang.IllegalCallerException): self.obj.callStackWalker1() def testStackWalker2(self): self.assertEqual(self.obj.callStackWalker2(), jpype.JClass( jpype.java.lang.Class.forName("org.jpype.Reflector0")).class_) jpype-1.6.0/test/jpypetest/test_charSequence.py000066400000000000000000000025221501674766700217160ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import sys import logging import time import common class ConversionCharSequenceTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testAutoConvert(self): Instant = jpype.JClass("java.time.Instant") now = "2019-11-12T03:20:54.710948400Z" then = Instant.parse(now) self.assertEqual(str(then), now) then = Instant.parse(jpype.JString(now)) self.assertEqual(str(then), now) then = Instant.parse(jpype.JObject(now, "java.lang.CharSequence")) self.assertEqual(str(then), now) jpype-1.6.0/test/jpypetest/test_classhints.py000066400000000000000000000100561501674766700214640ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common import sys class MyImpl(object): def blah(self): pass class ClassProxy: def __init__(self, proxy): self.proxy = proxy class ArrayProxy: def __init__(self, proxy): self.proxy = proxy class StringProxy: def __init__(self, proxy): self.proxy = proxy class ClassHintsTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.Custom = jpype.JClass("jpype.classhints.Custom") self.ClassHintsTest = jpype.JClass("jpype.classhints.ClassHintsTest") @jpype.JImplements("jpype.classhints.Custom") class MyCustom(object): def __init__(self, arg): self.arg = arg self.MyCustom = MyCustom def testCharSequence(self): Instant = jpype.JClass("java.time.Instant") s = "2019-12-21T05:26:13.223189Z" self.assertTrue(str(Instant.parse(s)), s) def testInstant(self): import datetime if sys.version_info.major == 3 and sys.version_info.minor < 12: now = datetime.datetime.utcnow() else: now = datetime.datetime.now(datetime.UTC) Instant = jpype.JClass("java.time.Instant") self.assertIsInstance(jpype.JObject(now, Instant), Instant) def testPath(self): import pathlib JPath = jpype.JClass("java.nio.file.Path") self.assertIsInstance(jpype.JObject( pathlib.Path(__file__).absolute(), JPath), JPath) def testFile(self): import pathlib JFile = jpype.JClass("java.io.File") self.assertIsInstance(jpype.JObject( pathlib.Path(__file__).absolute(), JFile), JFile) def testConvertExact(self): cht = self.ClassHintsTest with self.assertRaises(TypeError): cht.call("hello") @jpype.JConversion(self.Custom, exact=str) def StrToCustom(jcls, args): return self.MyCustom(args) cht.call("hello") self.assertIsInstance(cht.input, self.MyCustom) self.assertEqual(cht.input.arg, "hello") def testConvertAttribute(self): cht = self.ClassHintsTest with self.assertRaises(TypeError): cht.call(MyImpl()) @jpype.JConversion(self.Custom, attribute="blah") def StrToCustom(jcls, args): return self.MyCustom(args) cht.call(MyImpl()) self.assertIsInstance(cht.input, self.MyCustom) self.assertIsInstance(cht.input.arg, MyImpl) def testClassCustomizer(self): @jpype.JConversion("java.lang.Class", instanceof=ClassProxy) def ClassCustomizer(jcls, obj): return obj.proxy hints = jpype.JClass('java.lang.Class')._hints self.assertTrue(ClassProxy in hints.implicit) def testArrayCustomizer(self): @jpype.JConversion(jpype.JInt[:], instanceof=ArrayProxy) def ArrayCustomizer(jcls, obj): return obj.proxy hints = jpype.JClass(jpype.JInt[:])._hints self.assertTrue(ArrayProxy in hints.implicit) def testStringCustomizer(self): @jpype.JConversion("java.lang.String", instanceof=StringProxy) def STringCustomizer(jcls, obj): return obj.proxy hints = jpype.JClass("java.lang.String")._hints self.assertTrue(StringProxy in hints.implicit) jpype-1.6.0/test/jpypetest/test_closeable.py000066400000000000000000000115211501674766700212400ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class CloseableTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testCloseable(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() self.assertFalse(CloseableTest.closed) with CloseableTest() as myFile: myFile.print_("hello 1") self.assertEqual(CloseableTest.printed, "hello 1") self.assertTrue(CloseableTest.closed) def testCloseableFail(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() CloseableTest.willfail = True self.assertFalse(CloseableTest.closed) ex1 = None try: with CloseableTest() as myFile: myFile.print_("hello 1") try: raise Exception('foo') except Exception as ex: pass except Exception as ex: ex1 = ex self.assertTrue(ex1) self.assertEqual(CloseableTest.printed, "hello 1") self.assertTrue(CloseableTest.closed) def testCloseablePyExcept(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() self.assertFalse(CloseableTest.closed) try: with CloseableTest() as myFile: myFile.print_("hello 2") raise TypeError("Python exception") myFile.print_("there") except Exception as ex: self.assertIsInstance(ex, TypeError) self.assertEqual(CloseableTest.printed, "hello 2") self.assertTrue(CloseableTest.closed) def testCloseablePyExceptFail(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() CloseableTest.willfail = True self.assertFalse(CloseableTest.closed) try: with CloseableTest() as myFile: myFile.print_("hello 3") raise TypeError("Python exception") myFile.print_("there") except TypeError as ex: self.assertIsInstance(ex, TypeError) self.assertEqual(CloseableTest.printed, "hello 3") self.assertTrue(CloseableTest.closed) self.assertTrue(CloseableTest.failed) def testCloseableJExcept(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() self.assertFalse(CloseableTest.closed) try: with CloseableTest() as myFile: myFile.print_("hello 4") myFile.throwException() myFile.print_("there") except Exception as ex: self.assertIsInstance(ex, jpype.JException, 'type is %s' % type(ex)) self.assertEqual(ex.getMessage(), 'oh no!') self.assertEqual(CloseableTest.printed, "hello 4") self.assertTrue(CloseableTest.closed) def testCloseableJExceptFail(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() CloseableTest.willfail = True self.assertFalse(CloseableTest.closed) try: with CloseableTest() as myFile: myFile.print_("hello 5") myFile.throwException() myFile.print_("there") except Exception as ex: self.assertIsInstance(ex, jpype.JException, 'type is %s' % type(ex)) self.assertEqual(ex.getMessage(), 'oh no!') # fail if get "oh my?" self.assertEqual(CloseableTest.printed, "hello 5") self.assertTrue(CloseableTest.closed) self.assertTrue(CloseableTest.failed) def testCloseableAttr(self): cls = jpype.JClass("java.io.Closeable") self.assertTrue(hasattr(cls, '__enter__')) self.assertTrue(hasattr(cls, '__exit__')) def testAutoCloseableAttr(self): cls = jpype.JClass("java.lang.AutoCloseable") self.assertTrue(hasattr(cls, '__enter__')) self.assertTrue(hasattr(cls, '__exit__')) jpype-1.6.0/test/jpypetest/test_closed.py000066400000000000000000000033231501674766700205610ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class ClosedTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testObjects(self): from jpype import java s = java.lang.String('foo') s._allowed = 1 try: s.forbidden = 1 except AttributeError: pass else: raise AssertionError("AttributeError not raised") def testArrays(self): s = jpype.JArray(jpype.JInt)(5) # Setting private members is allowed s._allowed = 1 try: # Setting public members is prohibited s.forbidden = 1 except AttributeError: pass else: raise AssertionError("AttributeError not raised") def testStatic(self): static = jpype.JClass('jpype.objectwrapper.StaticTest') self.assertEqual(static.i, 1) self.assertEqual(static.d, 1.2345) self.assertEqual(static.s, "hello") jpype-1.6.0/test/jpypetest/test_collection.py000066400000000000000000000222121501674766700214410ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * import common import collections.abc class CollectionCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) @common.requirePythonAfter((3, 6, 0)) def testCollectionABC(self): JCollection = JClass('java.util.Collection') self.assertFalse(issubclass(JCollection, collections.abc.Sequence)) self.assertFalse(issubclass(JCollection, collections.abc.Reversible)) self.assertTrue(issubclass(JCollection, collections.abc.Collection)) self.assertTrue(issubclass(JCollection, collections.abc.Iterable)) self.assertTrue(issubclass(JCollection, collections.abc.Sized)) def testCollectionDelItem(self): ja = JClass('java.util.ArrayList')(['1', '2', '3']) jc = JObject(ja, 'java.util.Collection') with self.assertRaisesRegex(TypeError, 'remove'): del jc[1] def testListAddAll(self): l = [1, 2, 3, 4] l2 = ['a', 'b'] jlist = JClass("java.util.ArrayList")() jlist.addAll(l) jcollection = JObject(jlist, JClass("java.util.Collection")) jcollection.addAll(l2) l.extend(l2) self.assertEqual(l, list(jcollection)) class CollectionIteratorCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testIterator(self): al = JClass("java.util.ArrayList")() itr = al.iterator() self.assertEqual(itr, iter(itr)) class CollectionListCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testListAdd(self): collection = JClass("java.util.ArrayList")() collection.add(1) collection.add(2) self.assertEqual([1, 2], [i for i in collection]) def testListGet(self): jlist = JClass("java.util.ArrayList")() jlist.addAll([1, 2, 3, 4]) self.assertEqual(jlist[0], 1) self.assertEqual(jlist[3], 4) self.assertEqual(jlist[-1], 4) self.assertEqual(jlist[-4], 1) def testListSlice(self): jlist = JClass("java.util.ArrayList")() jlist.addAll([1, 2, 3, 4]) jlist[1:3] = [5, 6] self.assertEqual(jlist[1], 5) self.assertEqual(jlist[2], 6) def testListDel(self): jlist = JClass("java.util.ArrayList")() jlist.addAll([1, 2, 3, 4]) del jlist[0] self.assertEqual(len(jlist), 3) self.assertEqual(jlist[0], 2) def testListSetItemNeg(self): l = [1, 2, 3, 4] jlist = JClass("java.util.ArrayList")() jlist.addAll([1, 2, 3, 4]) jlist[-1] = 5 l[-1] = 5 self.assertEqual(l, list(jlist)) jlist[-2] = 6 l[-2] = 6 self.assertEqual(l, list(jlist)) with self.assertRaises(IndexError): jlist[-5] = 6 def testListIter(self): ls = JClass('java.util.ArrayList')([0, 1, 2, 3]) for i, j in enumerate(ls): self.assertEqual(i, j) def testUnmodifiableNext(self): ArrayList = JClass('java.util.ArrayList') Collections = JClass('java.util.Collections') a = ArrayList() a.add("first") a.add("second") a.add("third") for i in a: pass for i in Collections.unmodifiableList(a): pass @common.requirePythonAfter((3, 6, 0)) def testListABC(self): l = ['a', 'b', 'c', 'b'] JList = JClass('java.util.ArrayList') al = JList(l) for i, j in zip(reversed(al), reversed(l)): self.assertEqual(i, j) self.assertEqual(object() in al, object() in l) self.assertEqual('a' in al, 'a' in l) self.assertEqual(al.index('b'), l.index('b')) self.assertEqual(al.count('b'), l.count('b')) with self.assertRaises(ValueError): al.index(object()) self.assertEqual(al.count(object()), l.count(object())) self.assertIsInstance(al, collections.abc.Sequence) self.assertIsInstance(al, collections.abc.Reversible) self.assertIsInstance(al, collections.abc.Collection) self.assertIsInstance(al, collections.abc.Iterable) self.assertIsInstance(al, collections.abc.Sized) class CollectionMapCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testIterateMap(self): collection = JClass('java.util.HashMap')() collection.put('A', 1) collection.put('B', 2) asdict = dict() for x in collection.entrySet(): asdict[str(x.getKey())] = x.getValue().longValue() self.assertEqual(asdict, {'A': 1, 'B': 2}) def testMapPut(self): jmap = JClass("java.util.HashMap")() jmap["a"] = 1 self.assertEqual(jmap["a"], 1) def testMapPutAll(self): jmap = JClass("java.util.HashMap")() dic = {"a": "1", "b": "2", "c": "3"} jmap.putAll(dic) self.assertEqual(jmap["a"], "1") self.assertEqual(jmap["b"], "2") self.assertEqual(jmap["c"], "3") with self.assertRaises(TypeError): jmap.putAll([1, 2, 3]) def testMapKeyError(self): hm = JClass('java.util.HashMap')() with self.assertRaises(KeyError): hm['foo'] hm['foo'] = None self.assertEqual(hm['foo'], None) def testHashMapEntryIter(self): hm = JClass('java.util.HashMap')() hm['alice'] = 'alice' hm['betty'] = 'betty' hm['catty'] = 'catty' for p, v in hm.entrySet(): self.assertEqual(p, v) def testTreeMapEntryIter(self): hm = JClass('java.util.TreeMap')() hm['alice'] = 'alice' hm['betty'] = 'betty' hm['catty'] = 'catty' for p, v in hm.entrySet(): self.assertEqual(p, v) def testMapEntry(self): hm = JClass('java.util.TreeMap')() hm['alice'] = 'alice' h = hm.entrySet() self.assertEqual(len(h.iterator().next()), 2) def testEnumMap(self): enumclass = JClass('jpype.collection.TestEnum') enummap = JClass('java.util.EnumMap')(enumclass) enummap.put(enumclass.A, 'ABC') enummap.put(enumclass.B, 'DEF') asdict = dict() for x in enummap.entrySet(): asdict[str(x.getKey())] = x.getValue() self.assertEqual({'A': 'ABC', 'B': 'DEF'}, asdict) def testSetDelItem(self): hs = JClass('java.util.HashSet')() hs.add('a') hs.add('b') hs.add('c') self.assertIn('a', hs) del hs['a'] self.assertNotIn('a', hs) def testHashMapCtor(self): HashMap = JClass('java.util.HashMap') dc = dict() dc['fred'] = 1 dc['george'] = 2 dc['paul'] = 3 hm = HashMap(dc) for p, v in dc.items(): self.assertEqual(hm[p], v) def testHashMapPutAll(self): HashMap = JClass('java.util.HashMap') hm = HashMap() dc = dict() dc['fred'] = 1 dc['george'] = 2 dc['paul'] = 3 hm.putAll(dc) for p, v in dc.items(): self.assertEqual(hm[p], v) def testHashMapConvert(self): HashMap = JClass('java.util.HashMap') hm = HashMap() hm['fred'] = 1 hm['george'] = 2 hm['paul'] = 3 dc = dict(hm) for p, v in hm.items(): self.assertEqual(dc[p], v) def testMapABC(self): from collections.abc import Mapping, Sized, Iterable, Container hm = JClass('java.util.HashMap')() self.assertIsInstance(hm, Sized) self.assertIsInstance(hm, Iterable) self.assertIsInstance(hm, Container) self.assertIsInstance(hm, Mapping) def testMapContains(self): hm = JClass('java.util.HashMap')() hm['fred'] = 1 hm['george'] = 2 hm['paul'] = 3 self.assertTrue("fred" in hm) self.assertFalse("sally" in hm) def testMapNoConversion(self): hm = JClass("java.util.HashMap")() self.assertFalse(object() in hm) with self.assertRaises(KeyError): hm[object()] class CollectionEnumerationCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testEnumeration(self): st = JClass('java.util.StringTokenizer')("this is a test") out = [] for i in st: out.append(str(i)) self.assertEqual(len(i), 4) self.assertEqual(" ".join(out), "this is a test") jpype-1.6.0/test/jpypetest/test_comparable.py000066400000000000000000000077521501674766700214270ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common class ComparableTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testComparable(self): a = jpype.java.time.Instant.ofEpochSecond(10000000) b = jpype.java.time.Instant.ofEpochSecond(10000001) self.assertFalse(a < a) self.assertFalse(a > a) self.assertTrue(a >= a) self.assertTrue(a <= a) self.assertTrue(a == a) self.assertFalse(a != a) self.assertTrue(a < b) self.assertFalse(a > b) self.assertFalse(a >= b) self.assertTrue(a <= b) self.assertFalse(a == b) self.assertTrue(a != b) def testComparableHash(self): i = jpype.java.math.BigInteger("1000000000000") self.assertIsInstance(hash(i), int) def testComparableNull(self): Instant = jpype.JClass("java.time.Instant") i1 = Instant.parse("1970-01-01T00:00:00Z") i3 = jpype.JObject(None, Instant) self.assertTrue(i1 == i1) self.assertFalse(i1 == i3) self.assertFalse(i3 == i1) self.assertTrue(i1 != i3) self.assertTrue(i3 != i1) with self.assertRaises(ValueError): print(i1 < i3) with self.assertRaises(ValueError): print(i1 <= i3) with self.assertRaises(ValueError): print(i1 > i3) with self.assertRaises(ValueError): print(i1 >= i3) with self.assertRaises(ValueError): print(i3 < i1) with self.assertRaises(ValueError): print(i3 <= i1) with self.assertRaises(ValueError): print(i3 > i1) with self.assertRaises(ValueError): print(i3 >= i1) with self.assertRaises(ValueError): print(i3 < i3) with self.assertRaises(ValueError): print(i3 <= i3) with self.assertRaises(ValueError): print(i3 > i3) with self.assertRaises(ValueError): print(i3 >= i3) def testComparableObj(self): C1 = jpype.JClass("java.time.temporal.ChronoUnit") C2 = jpype.JClass("java.util.concurrent.TimeUnit") O1 = C1.SECONDS O2 = C2.SECONDS N1 = jpype.JObject(None, C1) N2 = jpype.JObject(None, C2) V = jpype.JInt(1) # Test dissimilar objects self.assertTrue(O1 != O2) self.assertFalse(O1 == O2) # test Nulls self.assertTrue(N1 == N2) self.assertFalse(N1 != N2) self.assertTrue(N1 == None) self.assertFalse(N1 != None) # test primitives self.assertFalse(O1 == V) self.assertFalse(V == O1) # test null to primitives self.assertFalse(N1 == V) self.assertFalse(V == N1) self.assertFalse(1 == O1) self.assertFalse("M" == O1) self.assertFalse(O1 == 1) self.assertFalse(O1 == "M") self.assertTrue(1 != O1) self.assertTrue("M" != O1) self.assertTrue(O1 != 1) self.assertTrue(O1 != "M") with self.assertRaises(TypeError): self.assertTrue(O1 > 1) with self.assertRaises(TypeError): self.assertTrue(1 > O1) with self.assertRaises(TypeError): self.assertTrue(O1 > O2) jpype-1.6.0/test/jpypetest/test_conversion.py000066400000000000000000000137111501674766700214770ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import common import jpype import _jpype from jpype.types import * class ConversionTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.jc1 = jpype.JClass("java.lang.String") self.jc2 = jpype.JClass("java.lang.Integer") def testList(self): cls = JClass('jpype.collection.CollectionTest') self.assertIsInstance(cls.testList( [1, 2, 3]), JClass('java.util.List')) def testMap(self): cls = JClass('jpype.collection.CollectionTest') self.assertIsInstance(cls.testMap( {'a': 1, 'b': 2}), JClass('java.util.Map')) def testCanConvertExact(self): self.assertEqual(self.jc1._canConvertToJava("a"), "exact") def testCanConvertNone(self): self.assertEqual(self.jc1._canConvertToJava(1), "none") def testCanConvertExplicit(self): self.assertEqual( self.jc2._canConvertToJava(1), "explicit") def testCanConvertImplicit(self): self.assertEqual( self.jc1._canConvertToJava(None), "implicit") def testConvertExact(self): self.assertIsInstance( self.jc1._convertToJava("a"), self.jc1) def testConvertImplicit(self): self.assertIsInstance( self.jc1._convertToJava(None), self.jc1) def testConvertExplicit(self): self.assertIsInstance( self.jc2._convertToJava(1), self.jc2) def testConvertNone(self): with self.assertRaises(TypeError): self.jc1._convertToJava(1) def testUnbox(self): jf = JClass('jpype.common.Fixture') java = jpype.java jf.static_bool_field = java.lang.Boolean(True) self.assertEqual(jf.static_bool_field, True) jf.static_char_field = java.lang.Character("a") self.assertEqual(jf.static_char_field, "a") jf.static_byte_field = java.lang.Byte(123) self.assertEqual(jf.static_byte_field, 123) jf.static_short_field = java.lang.Short(123) self.assertEqual(jf.static_short_field, 123) jf.static_int_field = java.lang.Integer(123) self.assertEqual(jf.static_int_field, 123) jf.static_long_field = java.lang.Long(123) self.assertEqual(jf.static_long_field, 123) jf.static_float_field = java.lang.Float(123) self.assertEqual(jf.static_float_field, 123) jf.static_double_field = java.lang.Double(123) self.assertEqual(jf.static_double_field, 123) def testUnboxFail(self): java = jpype.java with self.assertRaises(TypeError): JBoolean._convertToJava(java.lang.Double(1)) with self.assertRaises(TypeError): JChar._convertToJava(java.lang.Double(1)) with self.assertRaises(TypeError): JByte._convertToJava(java.lang.Boolean(1)) with self.assertRaises(TypeError): JShort._convertToJava(java.lang.Boolean(1)) with self.assertRaises(TypeError): JInt._convertToJava(java.lang.Boolean(1)) with self.assertRaises(TypeError): JLong._convertToJava(java.lang.Boolean(1)) with self.assertRaises(TypeError): JFloat._convertToJava(java.lang.Boolean(1)) with self.assertRaises(TypeError): JDouble._convertToJava(java.lang.Boolean(1)) def testBox(self): java = jpype.java self.assertEqual(java.lang.Boolean(JBoolean(True)), True) # FIXME this one fails #self.assertEqual(java.lang.Character(JChar("A")), "A") self.assertEqual(java.lang.Byte(JByte(123)), 123) self.assertEqual(java.lang.Short(JShort(123)), 123) self.assertEqual(java.lang.Integer(JInt(123)), 123) self.assertEqual(java.lang.Long(JLong(123)), 123) self.assertEqual(java.lang.Float(JFloat(123)), 123) self.assertEqual(java.lang.Double(JDouble(123)), 123) def testCharConversion(self): self.assertEqual(JChar._canConvertToJava("a"), "implicit") self.assertEqual(JChar._canConvertToJava(bytes([1])), "implicit") self.assertEqual(JChar._canConvertToJava(bytes([1, 1])), "none") # This test is wrong 'char q = (char) 1000000;' works in Java # def testCharOverflow(self): # with self.assertRaises(OverflowError): # JChar(1000000) def testCharBytes(self): # setArrayRange directly calls Char conversion so it is a good way # to test without checking if conversion is possible first ja = JArray(JChar)(2) with self.assertRaises(ValueError): ja[0:1] = [bytes([1, 1, 1])] with self.assertRaises(OverflowError): ja[0:1] = [1000000] with self.assertRaises(ValueError): ja[0:1] = ["AAA"] with self.assertRaises(ValueError): ja[0:1] = ["\U0001F600"] with self.assertRaises(TypeError): ja[0:1] = [object()] ja[0:1] = ["\u265E"] def testDerived(self): # Test Java Object any conversion self.assertEqual(jpype.JClass("java.lang.Object")._canConvertToJava(jpype.JClass("java.lang.String")("a")), "derived") # Test Java Object specific conversion self.assertEqual(jpype.JClass("java.util.List")._canConvertToJava(jpype.JClass("java.util.ArrayList")()), "derived") jpype-1.6.0/test/jpypetest/test_conversionInt.py000066400000000000000000000067331501674766700221600ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * import sys import logging import time import common def haveNumpy(): try: import numpy return True except ImportError: return False class ConversionIntTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.Test = jpype.JClass("jpype.common.Fixture")() def testIntFromInt(self): self.assertEqual(self.Test.callInt(int(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt(self): import numpy as np self.assertEqual(self.Test.callInt(np.int_(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt8(self): import numpy as np self.assertEqual(self.Test.callInt(np.int8(123)), 123) self.assertEqual(self.Test.callInt(np.uint8(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt16(self): import numpy as np self.assertEqual(self.Test.callInt(np.int16(123)), 123) self.assertEqual(self.Test.callInt(np.uint16(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt32(self): import numpy as np self.assertEqual(self.Test.callInt(np.int32(123)), 123) self.assertEqual(self.Test.callInt(np.uint32(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt64(self): import numpy as np self.assertEqual(self.Test.callInt(np.int64(123)), 123) self.assertEqual(self.Test.callInt(np.uint64(123)), 123) def testIntFromFloat(self): with self.assertRaises(TypeError): self.Test.callInt(float(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): self.Test.callInt(np.float16(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat32(self): import numpy as np with self.assertRaises(TypeError): self.Test.callInt(np.float32(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat64(self): import numpy as np with self.assertRaises(TypeError): self.Test.callInt(np.float64(2)) def testIntRange(self): with self.assertRaises(OverflowError): self.Test.callInt(int(1e10)) with self.assertRaises(OverflowError): self.Test.callInt(int(-1e10)) def testIntFromNone(self): with self.assertRaises(TypeError): self.Test.callInt(None) jpype-1.6.0/test/jpypetest/test_conversionLong.py000066400000000000000000000067711501674766700223270ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * import sys import logging import time import common def haveNumpy(): try: import numpy return True except ImportError: return False class ConversionLongTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.Test = jpype.JClass("jpype.common.Fixture")() def testLongFromInt(self): self.assertEqual(self.Test.callLong(int(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt(self): import numpy as np self.assertEqual(self.Test.callLong(np.int_(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt8(self): import numpy as np self.assertEqual(self.Test.callLong(np.int8(123)), 123) self.assertEqual(self.Test.callLong(np.uint8(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt16(self): import numpy as np self.assertEqual(self.Test.callLong(np.int16(123)), 123) self.assertEqual(self.Test.callLong(np.uint16(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt32(self): import numpy as np self.assertEqual(self.Test.callLong(np.int32(123)), 123) self.assertEqual(self.Test.callLong(np.uint32(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt64(self): import numpy as np self.assertEqual(self.Test.callLong(np.int64(123)), 123) self.assertEqual(self.Test.callLong(np.uint64(123)), 123) def testLongFromFloat(self): with self.assertRaises(TypeError): self.Test.callLong(float(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): self.Test.callLong(np.float16(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat32(self): import numpy as np with self.assertRaises(TypeError): self.Test.callLong(np.float32(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat64(self): import numpy as np with self.assertRaises(TypeError): self.Test.callLong(np.float64(2)) def testLongRange(self): with self.assertRaises(OverflowError): self.Test.callLong(int(1e30)) with self.assertRaises(OverflowError): self.Test.callLong(int(-1e30)) def testLongFromNone(self): with self.assertRaises(TypeError): self.Test.callLong(None) jpype-1.6.0/test/jpypetest/test_conversionShort.py000066400000000000000000000070271501674766700225220ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * import sys import logging import time import common def haveNumpy(): try: import numpy return True except ImportError: return False class ConversionShortTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.Test = jpype.JClass("jpype.common.Fixture")() def testShortFromInt(self): self.assertEqual(self.Test.callShort(int(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt(self): import numpy as np self.assertEqual(self.Test.callShort(np.int_(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt8(self): import numpy as np self.assertEqual(self.Test.callShort(np.int8(123)), 123) self.assertEqual(self.Test.callShort(np.uint8(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt16(self): import numpy as np self.assertEqual(self.Test.callShort(np.int16(123)), 123) self.assertEqual(self.Test.callShort(np.uint16(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt32(self): import numpy as np self.assertEqual(self.Test.callShort(np.int32(123)), 123) self.assertEqual(self.Test.callShort(np.uint32(123)), 123) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt64(self): import numpy as np self.assertEqual(self.Test.callShort(np.int64(123)), 123) self.assertEqual(self.Test.callShort(np.uint64(123)), 123) def testShortFromFloat(self): with self.assertRaises(TypeError): self.Test.callShort(float(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): self.Test.callShort(np.float16(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat32(self): import numpy as np with self.assertRaises(TypeError): self.Test.callShort(np.float32(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat64(self): import numpy as np with self.assertRaises(TypeError): self.Test.callShort(np.float64(2)) def testShortRange(self): with self.assertRaises(OverflowError): self.Test.callShort(int(1e10)) with self.assertRaises(OverflowError): self.Test.callShort(int(-1e10)) def testShortFromNone(self): with self.assertRaises(TypeError): self.Test.callShort(None) jpype-1.6.0/test/jpypetest/test_core.py000066400000000000000000000036231501674766700202430ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from unittest import mock import jpype import common from jpype.types import * class JCharTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testShutdownHook(self): Thread = JClass("java.lang.Thread") Runnable = JClass("java.lang.Runnable") Runtime = JClass("java.lang.Runtime") @jpype.JImplements(Runnable) class Run: @jpype.JOverride def run(self): pass th = Thread(Run()) Runtime.getRuntime().addShutdownHook(th) self.assertTrue(Runtime.getRuntime().removeShutdownHook(th)) def testShutdownWrongThread(self): Thread = JClass("java.lang.Thread") Runnable = JClass("java.lang.Runnable") @jpype.JImplements(Runnable) class Run: def __init__(self): self.rc = False @jpype.JOverride def run(self): try: jpype.shutdownJVM() except: self.rc = True run = Run() th = Thread(run) th.start() th.join() self.assertTrue(run.rc) jpype-1.6.0/test/jpypetest/test_coverage.py000066400000000000000000000226411501674766700211070ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import jpype.imports import common import sys import os import importlib import pytest from unittest import mock import _jpype # Tests just for coverage # These will be moved to the corresponding test file as needed class CoverageCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.platform = sys.platform def testWin32(self): if sys.platform == "win32": raise common.unittest.SkipTest("not tested on win32") try: sys.platform = "win32" importlib.reload(jpype._classpath) with self.assertRaises(jpype.JVMNotFoundException): jpype.getDefaultJVMPath() finally: sys.platform = self.platform importlib.reload(jpype._classpath) def testHandleClassPath(self): with self.assertRaises(TypeError): jpype._core._expandClassPath([1]) jpype._core._expandClassPath(["./*.jar"]) def testRestart(self): with self.assertRaises(OSError): jpype.startJVM() def testSynchronizeFail(self): with self.assertRaises(TypeError): jpype.synchronized("hello") def testJArrayFail1(self): with self.assertRaises(TypeError): jpype.JArray(jpype.JInt, 2, 2) def testJArrayFail2(self): with self.assertRaises(TypeError): jpype.JArray(jpype.JInt, 1)(1, 2, 3) def testJArrayStr(self): self.assertEqual(str(jpype.JArray(jpype.JInt)([1, 2])), str([1, 2])) def testJArrayLength(self): ja = jpype.JArray(jpype.JInt)([1, 2]) self.assertEqual(ja.length, len(ja)) def testJArrayGetItemSlice(self): ja = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) assert list(ja[0:2:-1]) == [1, 2, 3, 4][0:2:-1] def testJArraySetItemSlice(self): ja = jpype.JArray(jpype.JInt)([1, 2, 3, 4, 5, 6]) ja[0:-1:2] = [-1, -1, -1] self.assertEqual(list(ja[:]), [-1, 2, -1, 4, -1, 6]) # FIXME ja[0:-1:2] = [-1] works but should not # FIXME ja[0:-1:2] = 1 gives wierd error def testJArrayGetItemSlice_2(self): ja = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) assert list(ja[1:]) == [2, 3, 4] def testJArraySetItemSlice_2(self): ja = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) ja[1:] = [3, 4, 5] self.assertEqual(list(ja[:]), [1, 3, 4, 5]) def testJArrayEQ(self): ja1 = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) ja2 = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) ja1 == ja2 # FIXME: This test is failing, but shouldn't be. # assert ja1 == ja2 assert (ja1 == [1, 2]) is False def testJArrayNE(self): ja = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) assert ja != [1, 2] assert (ja != ja) is False def testJArrayIter(self): ja = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) for i in ja: pass def testJArrayBytes(self): ja = jpype.JArray(jpype.JByte)([65, 66]) self.assertEqual(str(ja), "AB") def testJArrayBytesFail(self): ja = jpype.JArray(jpype.JByte)([65, 66]) self.assertFalse(ja == None) def testJBooleanFail(self): with self.assertRaises(TypeError): jpype.java.lang.Boolean(1, 2) def testJStringAppend(self): js = jpype.JString("foo") self.assertEqual(js + "bar", "foobar") if not self._convertStrings: self.assertIsInstance(js + "bar", jpype.java.lang.String) def testJStringNE(self): js = jpype.JString("foo") self.assertFalse(js != "foo") self.assertFalse(js != jpype.JString("foo")) # FIXME review this for slices and other cases, we may need # to improve this one def testJStringGetItem(self): js = jpype.JString("fred") self.assertEqual(js[1], "r") def testJStringLen(self): js = jpype.JString("fred") self.assertEqual(len(js), 4) def testJStringLT(self): js = jpype.JString("b") self.assertTrue(js < 'c') self.assertFalse(js < 'b') self.assertFalse(js < 'a') def testJStringLE(self): js = jpype.JString("b") self.assertTrue(js <= 'c') self.assertTrue(js <= 'b') self.assertFalse(js <= 'a') def testJStringGT(self): js = jpype.JString("b") self.assertFalse(js > 'c') self.assertFalse(js > 'b') self.assertTrue(js > 'a') def testJStringGE(self): js = jpype.JString("b") self.assertFalse(js >= 'c') self.assertTrue(js >= 'b') self.assertTrue(js >= 'a') def testJStringContains(self): js = jpype.JString("fred") self.assertTrue("r" in js) self.assertFalse("g" in js) def testJStringRepr(self): js = jpype.JString("fred") self.assertTrue(repr(js), "fred") # def testSetResourceFail(self): # with self.assertRaises(RuntimeError): # _jpype.setResource("NotAResource", None) # FIXME this one is broken # def testJPrimitiveSetAttr(self): # ji = jpype.JInt(1) # with self.assertRaises(AttributeError): # ji.qq = "wow" # FIXME this path seems like something outdated and is not working # def testJBooleanFromBool(self): # self.assertTrue(jpype.java.lang.Boolean(jpype.JInt(40))==True) def testJArrayFail(self): class JArray2(jpype.JArray, internal=True): pass with self.assertRaises(TypeError): JArray2(jpype.JInt) @common.requireInstrumentation def testJStringFail(self): _jpype.fault("PyJPClass_new::verify") class JString2(jpype.JString, internal=True): pass with self.assertRaises(TypeError): JString2("foo") def testCustomizerLate(self): with self.assertRaises(TypeError): @jpype.JImplementationFor("java.lang.Object", base=True) class Sally(object): pass def testCustomizerBadType(self): with self.assertRaises(TypeError): @jpype.JImplementationFor({}) class Sally(object): pass def testModuleHasClass(self): self.assertTrue(_jpype._hasClass("java.lang.Object")) def testJClassBadClass(self): with self.assertRaises(Exception): jpype.JClass("not.a.class") def testJClassBadType(self): with self.assertRaises(TypeError): jpype.JClass({}) def testJClassFromClass(self): self.assertIsInstance(jpype.JClass(jpype.java.lang.Class.forName( "java.lang.StringBuilder")), jpype.JClass) def testHints(self): with self.assertRaises(AttributeError): jpype.JObject._hints = object() @pytest.mark.filterwarnings("ignore::DeprecationWarning") def testDeprecated(self): @jpype._core.deprecated def foo(): pass @jpype._core.deprecated("foo") def bar(): pass bar() foo() def testVersionPreStart(self): with mock.patch('_jpype.isStarted') as func: func.return_value = False self.assertEqual(jpype.getJVMVersion(), (0, 0, 0)) def testGui(self): def foo(): pass # this is executed in a thread which may start later magic = mock.MagicMock() with mock.patch("sys.platform", "other"), mock.patch.dict('sys.modules', {'PyObjCTools': magic}): from PyObjCTools import AppHelper # type: ignore self.assertEqual(sys.platform, "other") jpype.setupGuiEnvironment(foo) self.assertFalse(magic.AppHelper.runConsoleEventLoop.called) jpype.shutdownGuiEnvironment() self.assertFalse(magic.AppHelper.stopEventLoop.called) with mock.patch("sys.platform", "darwin"), mock.patch.dict('sys.modules', {'PyObjCTools': magic}): from PyObjCTools import AppHelper # type: ignore self.assertEqual(sys.platform, "darwin") jpype.setupGuiEnvironment(foo) self.assertTrue(magic.AppHelper.runConsoleEventLoop.called) jpype.shutdownGuiEnvironment() self.assertTrue(magic.AppHelper.stopEventLoop.called) def testImportKey(self): self.assertEqual(jpype.imports._keywordUnwrap("with_"), "with") self.assertEqual(jpype.imports._keywordUnwrap("func"), "func") self.assertEqual(jpype.imports._keywordWrap("with"), "with_") self.assertEqual(jpype.imports._keywordWrap("func"), "func") def testImportNotStarted(self): with mock.patch('_jpype.isStarted') as func: func.return_value = False with self.assertRaisesRegex(ImportError, "jvm"): import mil.spec # type: ignore jpype-1.6.0/test/jpypetest/test_customizer.py000066400000000000000000000031121501674766700215100ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * from jpype import java import common try: import numpy as np except ImportError: pass class CustomizerTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.fixture = JClass('jpype.common.Fixture')() def testSticky(self): @jpype.JImplementationFor("jpype.override.A") class _A: @jpype.JOverride(sticky=True, rename="remove_") def remove(self, obj): pass A = jpype.JClass("jpype.override.A") B = jpype.JClass("jpype.override.B") self.assertEqual(A.remove, _A.remove) self.assertEqual(B.remove, _A.remove) self.assertEqual(str(A.remove_), "jpype.override.A.remove") self.assertEqual(str(B.remove_), "jpype.override.B.remove") jpype-1.6.0/test/jpypetest/test_directbuffer.py000066400000000000000000000130501501674766700217520ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype import _jpype from jpype.types import * from jpype import java import common try: import numpy as np except ImportError: pass class JBufferTestCase(common.JPypeTestCase): """ Test for direct buffers. """ def setUp(self): common.JPypeTestCase.setUp(self) self.fixture = JClass('jpype.common.Fixture')() self.bo = JClass("java.nio.ByteOrder") self.cls = JClass("java.nio.ByteBuffer") def testIsDirect(self): obj = self.cls.allocate(10) with self.assertRaises(BufferError): memoryview(obj) obj = self.cls.allocateDirect(10) memoryview(obj) def testReadOnly(self): obj = self.cls.allocateDirect(10).asReadOnlyBuffer() mv = memoryview(obj) self.assertTrue(mv.readonly) def testOrder(self): obj = self.cls.allocateDirect(10) obj.order(self.bo.LITTLE_ENDIAN) v1 = obj.asIntBuffer() self.assertEqual(memoryview(v1).format, 'i') def testMemoryViewByte(self): obj = self.cls.allocateDirect(12) mv = memoryview(obj) self.assertEqual(mv.itemsize, 1) self.assertEqual(mv.strides, (1,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (12,)) self.assertEqual(mv.format, ">b") def testMemoryViewChar(self): obj = self.cls.allocateDirect(12).asCharBuffer() mv = memoryview(obj) self.assertEqual(mv.itemsize, 2) self.assertEqual(mv.strides, (2,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (6,)) self.assertEqual(mv.format, ">H") def testMemoryViewShort(self): obj = self.cls.allocateDirect(12).asShortBuffer() mv = memoryview(obj) self.assertEqual(mv.itemsize, 2) self.assertEqual(mv.strides, (2,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (6,)) self.assertEqual(mv.format, ">h") def testMemoryViewInt(self): obj = self.cls.allocateDirect(12).asIntBuffer() mv = memoryview(obj) self.assertEqual(mv.itemsize, 4) self.assertEqual(mv.strides, (4,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (3,)) self.assertEqual(mv.format, ">i") def testMemoryViewLong(self): obj = self.cls.allocateDirect(24).asLongBuffer() mv = memoryview(obj) self.assertEqual(mv.itemsize, 8) self.assertEqual(mv.strides, (8,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (3,)) self.assertEqual(mv.format, ">q") def testMemoryViewFloat(self): obj = self.cls.allocateDirect(24).asFloatBuffer() mv = memoryview(obj) self.assertEqual(mv.itemsize, 4) self.assertEqual(mv.strides, (4,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (6,)) self.assertEqual(mv.format, ">f") def testMemoryViewDouble(self): obj = self.cls.allocateDirect(24).asDoubleBuffer() mv = memoryview(obj) self.assertEqual(mv.itemsize, 8) self.assertEqual(mv.strides, (8,)) self.assertEqual(mv.suboffsets, tuple()) self.assertFalse(mv.readonly) self.assertEqual(mv.shape, (3,)) self.assertEqual(mv.format, ">d") def checkNP(self, method, dtype, sz): obj = self.cls.allocateDirect(sz * 5) obj.order(self.bo.BIG_ENDIAN) bf = method(obj) mv = np.asarray(memoryview(bf)) ja = JArray(dtype)(5) mv[:] = [1, 2, 3, 4, 5] bf.get(ja) self.assertEqual(list(ja), [1, 2, 3, 4, 5]) obj.order(self.bo.LITTLE_ENDIAN) bf = method(obj) mv = np.asarray(memoryview(bf)) ja = JArray(dtype)(5) mv[:] = [1, 2, 3, 4, 5] bf.get(ja) self.assertEqual(list(ja), [1, 2, 3, 4, 5]) @common.requireNumpy def testMemoryViewShortNP(self): self.checkNP(self.cls.asShortBuffer, JShort, 2) @common.requireNumpy def testMemoryViewIntNP(self): self.checkNP(self.cls.asIntBuffer, JInt, 4) @common.requireNumpy def testMemoryViewLongNP(self): self.checkNP(self.cls.asLongBuffer, JLong, 8) @common.requireNumpy def testMemoryViewFloatNP(self): self.checkNP(self.cls.asFloatBuffer, JFloat, 4) @common.requireNumpy def testMemoryViewDoubleNP(self): self.checkNP(self.cls.asDoubleBuffer, JDouble, 8) jpype-1.6.0/test/jpypetest/test_docstring.py000066400000000000000000000025411501674766700213050ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class DocStringTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testDocClass(self): cls = jpype.JClass('java.util.Iterator') self.assertIsNotNone(cls.__doc__) cls = jpype.JClass('java.lang.String') self.assertIsNotNone(cls.__doc__) def testDocMethod(self): cls = jpype.JClass('java.lang.String') self.assertIsNotNone(cls.substring.__doc__) def testDocEnumClass(self): cls = jpype.JClass('java.lang.Character.UnicodeScript') self.assertIsNotNone(cls.__doc__) jpype-1.6.0/test/jpypetest/test_exc.py000066400000000000000000000136651501674766700201010ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype import java, JProxy from jpype.types import * import common def throwIOException(): raise java.io.IOException("Test throw") def throwByJavaException(): JClass('jpype.exc.ExceptionTest').throwIOException() class ExceptionTestCase(common.JPypeTestCase): def testExceptionThrown(self): ext = JClass('jpype.exc.ExceptionTest') try: ext.throwRuntime() self.fail() except JException as ex: self.assertIs(type(ex), java.lang.RuntimeException) self.assertEqual('Foo', ex.message()) trace = ex.stacktrace() self.assertTrue(str(trace).startswith( 'java.lang.RuntimeException: Foo')) def testExceptionByJavaClass(self): ext = JClass('jpype.exc.ExceptionTest') try: ext.throwRuntime() self.fail() except java.lang.RuntimeException as ex: self.assertIs(type(ex), java.lang.RuntimeException) self.assertEqual('Foo', ex.message()) trace = ex.stacktrace() self.assertTrue(str(trace).startswith( 'java.lang.RuntimeException: Foo')) def testThrowException(self): exthrow = JClass('jpype.exc.ExceptionThrower') extest = JClass('jpype.exc.ExceptionTest') d = {"throwIOException": throwIOException, } p = JProxy(exthrow, dict=d) self.assertTrue(extest.delegateThrow(p)) def testThrowException3(self): exthrow = JClass('jpype.exc.ExceptionThrower') extest = JClass('jpype.exc.ExceptionTest') d = {"throwIOException": throwByJavaException, } p = JProxy(exthrow, dict=d) self.assertTrue(extest.delegateThrow(p)) # This test is problematic as __name__ is a class property not an object property # def testExceptionPYEXCName(self): # e = self.jpype.exc.ChildTestException() # name = "jpype.exc.ChildTestException" # self.assertEqual(name, e.__name__) def testExceptionInstanceof(self): e = self.jpype.exc.ChildTestException() self.assertIsInstance(e, self.jpype.exc.ParentTestException) def testExceptionPYEXCInstanceof(self): e = self.jpype.exc.ChildTestException self.assertTrue(issubclass(e, self.jpype.exc.ParentTestException)) def testThrowChildExceptionFromCatchJExceptionParentClass(self): try: self.jpype.exc.ExceptionTest.throwChildTestException() self.fail() except self.jpype.exc.ParentTestException as ex: self.assertIsInstance(ex, self.jpype.exc.ChildTestException) def testCause(self): cls = jpype.JClass("jpype.exc.ExceptionTest") try: cls.throwChain() except Exception as ex: ex1 = ex self.assertEqual(str(ex1.__cause__), "Java Exception") frame = ex1.__cause__.__traceback__ expected = [ 'jpype.exc.ExceptionTest.throwChain', 'jpype.exc.ExceptionTest.method1', 'jpype.exc.ExceptionTest.method2', ] i = 0 while (frame): self.assertEqual(frame.tb_frame.f_code.co_name, expected[i]) frame = frame.tb_next i += 1 def testIndexError(self): with self.assertRaises(IndexError): raise java.lang.IndexOutOfBoundsException("From Java") def testValueError(self): js = JObject(None, JString) with self.assertRaises(ValueError): js.substring(0) def testExcCtor(self): WE = jpype.JClass("jpype.exc.WierdException") with self.assertRaises(WE): WE.testThrow() try: WE.testThrow() except Exception as ex: ex1 = ex self.assertEqual(ex1.args, ("Got it",)) def testExcCauseChained1(self): import jpype.imports try: from org.jpype.fail import BadInitializer # type: ignore except Exception as ex: ex1 = ex self.assertIsInstance(ex1, ImportError) self.assertIsInstance(ex1.__cause__, JClass( "java.lang.ExceptionInInitializerError")) self.assertIsInstance(ex1.__cause__.__cause__, JClass( "java.lang.ArrayIndexOutOfBoundsException")) self.assertTrue(ex1.__cause__.__traceback__ is not None) self.assertTrue(ex1.__cause__.__cause__.__traceback__ is not None) def testExcCauseChained2(self): try: JClass('org.jpype.fail.BadInitializer2') except Exception as ex: ex1 = ex self.assertIsInstance(ex1, JClass( 'java.lang.ExceptionInInitializerError')) self.assertIsInstance(ex1.__cause__.__cause__, JClass( "java.lang.ArrayIndexOutOfBoundsException")) self.assertTrue(ex1.__cause__.__traceback__ is not None) self.assertTrue(ex1.__cause__.__cause__.__traceback__ is not None) def testExpandStacktrace(self): Th = jpype.JClass('java.lang.Throwable') null = jpype.JObject(None, Th) # The next line should not fail Th._expandStacktrace(null) def testException(self): Th = jpype.JClass('java.lang.Throwable')('abc') self.assertEqual(str(Th), 'java.lang.Throwable: abc') jpype-1.6.0/test/jpypetest/test_fault.py000066400000000000000000001400261501674766700204250ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import unittest import _jpype import jpype from jpype import * import common class FaultTestCase(common.JPypeTestCase): """ Test for fault paths in JPype This test is only executed if fault instrumentation is compiled in. Fault instrumentation is trigger as part of the coverage compilation. This test suite brutally tries to force an exception to be thrown at each entry point and function call. The exception is controlled based on the name of the function in the JP_TRACE_IN and JP_PY_TRY block. Specific fault points are also triggered to produce abnormal objects which can then be passed to trigger error handling behaviors for off normal conditions. Most of the time the correct response to a fault is to pass it back to the user. But there are two exception to this rule. Function in which a Java resource is released using a Release* method must not fault because these calls can occur during exception handling routines and throwing may trigger an abort. Second, Python calls to free, finalize, and dealloc are not allowed to propagate exceptions as this would potentially interrupt the GC or prevent a object from being freed. Exceptions during the destructor path should be eaten or they may trigger randomly in places the user can't control (assigning a variable, leaving a scope). Many of these tests will be moved to other test units so that they are located with rest of the tests for that unit. Tests that test for something other than SystemError should be separated from the rest of the fault test. This may be one of the most tedious files every written by human hands. Coffee is advised. """ def setUp(self): common.JPypeTestCase.setUp(self) @common.requireInstrumentation def testJPArray_new(self): _jpype.fault("PyJPArray_new") with self.assertRaisesRegex(SystemError, "fault"): JArray(JInt)(5) # FIXME investigate why the release is not happening # may indicate a leak. Disabling for now @common.unittest.SkipTest # type: ignore def testJPArray_releaseBuffer(self): _jpype.fault("PyJPArray_releaseBuffer") def f(): ja = JArray(JInt)(5) m = memoryview(ja) with self.assertRaises(SystemError): f() @common.requireInstrumentation def testJPObject_null(self): Fixture = JClass("jpype.common.Fixture") _jpype.fault("PyJPObject_init.null") null = Fixture() with self.assertRaisesRegex(TypeError, 'Not a Java value'): str(null) self.assertEqual(hash(null), hash(None)) with self.assertRaisesRegex(AttributeError, 'requires'): print(null.int_field) with self.assertRaisesRegex(AttributeError, 'requires'): null.int_field = 1 # null.callInt(1) @common.requireInstrumentation def testJPClass_init(self): _jpype.fault("PyJPClass_init") with self.assertRaises(SystemError): _jpype._JClass("foo", (_jpype._JObject,), {}, internal=True) with self.assertRaises(TypeError): _jpype._JClass("foo", (object,), {}) _jpype._JClass("foo", (_jpype._JObject,), {}, internal=True) @common.requireInstrumentation def testJPClass_getattro(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_getattro") with self.assertRaisesRegex(SystemError, "fault"): js.foo with self.assertRaises(TypeError): getattr(js, object()) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): js.substring @common.requireInstrumentation def testJPClass_setattro(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_setattro") with self.assertRaisesRegex(SystemError, "fault"): js.substring = 1 with self.assertRaises(TypeError): setattr(js, object(), 1) with self.assertRaises(AttributeError): js.substring = None _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): js.substring = 1 @common.requireInstrumentation def testJPClass_subclasscheck(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_subclasscheck") with self.assertRaisesRegex(SystemError, "fault"): issubclass(js, JObject) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): issubclass(js, JObject) @common.requireInstrumentation def testJPClass_class(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_class") with self.assertRaisesRegex(SystemError, "fault"): js.class_ with self.assertRaises(AttributeError): _jpype._JClass("foo", (_jpype.JObject,), {}, internal=True).class_ _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): js.class_ @common.requireInstrumentation def testJPClass_setClass(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_setClass") with self.assertRaisesRegex(SystemError, "fault"): js.class_ = None with self.assertRaises(TypeError): js.class_ = None with self.assertRaises(TypeError): js.class_ = JObject() _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): js.class_ = None @common.requireInstrumentation def testJPClass_hints(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_hints") with self.assertRaisesRegex(SystemError, "fault"): js._hints _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): js._hints self.assertIsInstance(js._hints, _jpype._JClassHints) @common.requireInstrumentation def testJPClass_setHints(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_setHints") with self.assertRaisesRegex(SystemError, "fault"): js._hints = None @common.requireInstrumentation def testJPClass_cnaConvertToJava(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_canConvertToJava") with self.assertRaisesRegex(SystemError, "fault"): js._canConvertToJava("f") _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): js._canConvertToJava("f") js._canConvertToJava("f") @common.requireInstrumentation def testJPClass_cast(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_cast") with self.assertRaisesRegex(SystemError, "fault"): js._cast("f") with self.assertRaises(TypeError): js._cast(object()) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): js._cast(JObject(None)) js._cast(JObject(None)) @common.requireInstrumentation def testJPClass_convertToJava(self): js = JClass("java.lang.String") _jpype.fault("PyJPClass_convertToJava") with self.assertRaisesRegex(SystemError, "fault"): js._convertToJava("f") with self.assertRaises(TypeError): js._convertToJava(object()) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): js._convertToJava("f") js._convertToJava("f") @common.requireInstrumentation def testJPClassHints_new(self): _jpype.fault("PyJPClassHints_new") with self.assertRaisesRegex(SystemError, "fault"): _jpype._JClassHints() _jpype._JClassHints() @common.requireInstrumentation def testJPClassHints_init(self): _jpype.fault("PyJPClassHints_init") with self.assertRaisesRegex(SystemError, "fault"): _jpype._JClassHints() _jpype._JClassHints() @common.requireInstrumentation def testJPClassHints_str(self): _jpype.fault("PyJPClassHints_str") with self.assertRaisesRegex(SystemError, "fault"): str(_jpype._JClassHints()) str(_jpype._JClassHints()) @common.requireInstrumentation def testJPClassHints_addAttributeConversion(self): _jpype.fault("PyJPClassHints_addAttributeConversion") with self.assertRaisesRegex(SystemError, "fault"): _jpype._JClassHints()._addAttributeConversion("f", None) def f(): pass with self.assertRaises(TypeError): _jpype._JClassHints()._addAttributeConversion(None, f) with self.assertRaises(TypeError): _jpype._JClassHints()._addAttributeConversion("f", None) _jpype._JClassHints()._addAttributeConversion("f", f) @common.requireInstrumentation def testJPClassHints_addTypeConversion(self): _jpype.fault("PyJPClassHints_addTypeConversion") with self.assertRaisesRegex(SystemError, "fault"): _jpype._JClassHints()._addTypeConversion("f", None) def f(): pass # with self.assertRaises(TypeError): # _jpype._JClassHints()._addTypeConversion(None, f, 1) with self.assertRaises(TypeError): _jpype._JClassHints()._addTypeConversion(str, None, 1) _jpype._JClassHints()._addTypeConversion(str, f, 1) # pyjp_module.cpp: JP_PY_TRY("Py_GetAttrDescriptor"); # pyjp_module.cpp: JP_PY_TRY("PyJPModule_newArrayType"); # pyjp_module.cpp: JP_PY_TRY("PyJPModule_getClass"); # pyjp_module.cpp: JP_PY_TRY("PyJPModule_setClass"); # pyjp_module.cpp: JP_PY_TRY("examine"); # pyjp_module.cpp: JP_PY_TRY("PyInit__jpype"); @common.requireInstrumentation def testJPMonitor_init(self): jo = JClass("java.lang.Object")() _jpype.fault("PyJPMonitor_init") with self.assertRaisesRegex(SystemError, "fault"): _jpype._JMonitor(jo) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): _jpype._JMonitor(jo) _jpype._JMonitor(jo) @common.requireInstrumentation def testJPMonitor_str(self): jo = JClass("java.lang.Object")() jm = _jpype._JMonitor(jo) _jpype.fault("PyJPMonitor_str") with self.assertRaisesRegex(SystemError, "fault"): str(jm) @common.requireInstrumentation def testJPMonitor_enter(self): jo = JClass("java.lang.Object")() _jpype.fault("PyJPMonitor_enter") with self.assertRaisesRegex(SystemError, "fault"): with _jpype._JMonitor(jo): pass _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): with _jpype._JMonitor(jo): pass @common.requireInstrumentation def testJPMonitor_exit(self): jo = JClass("java.lang.Object")() _jpype.fault("PyJPMonitor_exit") with self.assertRaisesRegex(SystemError, "fault"): with _jpype._JMonitor(jo): pass @common.requireInstrumentation def testJPObject_new(self): _jpype.fault("PyJPObject_new") with self.assertRaisesRegex(SystemError, "fault"): JString("a") _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): JString("a") with self.assertRaises(TypeError): _jpype._JObject() JString("a") @common.requireInstrumentation def testJPObject_hash(self): jo = JClass("java.lang.Object")() _jpype.fault("PyJPObject_hash") with self.assertRaises(SystemError): hash(jo) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(jo) hash(jo) @common.requireInstrumentation def testJPProxy_new(self): _jpype.fault("PyJPProxy_new") with self.assertRaisesRegex(SystemError, "fault"): JProxy("java.io.Serializable", dict={}) # Disable for now. Need to work through the proxy changes # with self.assertRaises(TypeError): # _jpype._JProxy(None, None) # with self.assertRaises(TypeError): # _jpype._JProxy(None, []) # with self.assertRaises(TypeError): # _jpype._JProxy(None, [type]) # _jpype.fault("JPProxy::JPProxy") # with self.assertRaises(SystemError): # _jpype._JProxy(None, [JClass("java.io.Serializable")]) # _jpype._JProxy(None, [JClass("java.io.Serializable")]) # FIXME this needs special treatment. It should call __str__() # if toString is not defined. Disable for now. @common.unittest.SkipTest # type: ignore[arg-type] def testJPProxy_str(self): # Java has a hidden requirement that toString be available @JImplements("java.util.function.DoubleUnaryOperator") class f(object): @JOverride def applyAsDouble(self, d): return d jo = JObject(f(), "java.util.function.DoubleUnaryOperator") raise RuntimeError(jo.toString()) @common.requireInstrumentation def testJPProxy_dealloc(self): _jpype.fault("PyJPProxy_dealloc") def f(): _jpype._JProxy(None, None, [JClass("java.io.Serializable")]) f() @common.requireInstrumentation def testJPProxy_call(self): @JImplements("java.util.function.DoubleUnaryOperator") class f(object): @JOverride def applyAsDouble(self, d): if d == 2: return None return d _jpype.fault("JPProxy::getProxy") with self.assertRaises(SystemError): JObject(f(), "java.util.function.DoubleUnaryOperator") jo = JObject(f(), "java.util.function.DoubleUnaryOperator") with self.assertRaises(TypeError): jo.applyAsDouble(2) def testJPProxy_void(self): @JImplements("java.util.function.Consumer") class f(object): @JOverride def accept(self, d): return None jo = JObject(f(), "java.util.function.Consumer") jo.accept(None) @common.requireInstrumentation def testJPProxy_void_2(self): @JImplements("java.util.function.Consumer") class f(object): @JOverride def accept(self, d): return None jo = JObject(f(), "java.util.function.Consumer") _jpype.fault("JPProxy::getArgs") jo.accept(None) @common.requireInstrumentation def testJPProxy_box_return(self): q = None @JImplements("java.util.function.Supplier") class f(object): @JOverride def get(self): return q jo = JObject(f(), "java.util.function.Supplier") self.assertEqual(jo.get(), None) q = 1.0 self.assertIsInstance(jo.get(), java.lang.Double) q = 1 self.assertIsInstance(jo.get(), java.lang.Long) q = "ABC" self.assertIsInstance(jo.get(), java.lang.String) q = object() with self.assertRaises(TypeError): jo.get() @common.requireInstrumentation def testJPValue_alloc(self): _jpype.fault("PyJPValue_alloc") with self.assertRaisesRegex(SystemError, "fault"): JInt(1) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): JInt(1) JInt(1) @common.requireInstrumentation def testJPValue_finalize(self): _jpype.fault("PyJPValue_finalize") a = JInt(1) del a # lgtm [py/unnecessary-delete] @common.requireInstrumentation def testJPValue_str(self): js = JString("f") _jpype.fault("PyJPValue_str") with self.assertRaisesRegex(SystemError, "fault"): str(js) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(js) str(js) @common.requireInstrumentation def testJPObject_getattro(self): jo = JString("f") _jpype.fault("PyJPObject_getattro") with self.assertRaisesRegex(SystemError, "fault"): jo.substring _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): jo.substring jo.substring @common.requireInstrumentation def testJPObject_setattro(self): jo = JString("f") _jpype.fault("PyJPObject_setattro") with self.assertRaisesRegex(SystemError, "fault"): jo.substring = None @common.requireInstrumentation def testJPField(self): jf = JClass("jpype.common.Fixture") jfi = jf() with self.assertRaises(AttributeError): jf.final_static_int_field = 2 with self.assertRaises(AttributeError): jfi.final_int_field = 2 _jpype.fault("JPField::setStaticAttribute") with self.assertRaisesRegex(SystemError, "fault"): jf.static_int_field = 2 _jpype.fault("JPField::setAttribute") with self.assertRaisesRegex(SystemError, "fault"): jfi.int_field = 2 _jpype.fault("JPField::getStaticAttribute") with self.assertRaisesRegex(SystemError, "fault"): i = jf.static_int_field _jpype.fault("JPField::getAttribute") with self.assertRaisesRegex(SystemError, "fault"): i = jfi.int_field si = jf.__dict__['static_int_field'] str(si) repr(si) i = None _jpype.fault("PyJPField_get") with self.assertRaisesRegex(SystemError, "fault"): i = jfi.int_field self.assertEqual(i, None) _jpype.fault("PyJPField_set") with self.assertRaisesRegex(SystemError, "fault"): jfi.int_field = 2 _jpype.fault("PyJPField_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(si) @common.requireInstrumentation def testConvertString(self): _jpype.fault("JPObjectType::canConvertToJava") with self.assertRaisesRegex(SystemError, "fault"): JObject._convertToJava("foo") _jpype.fault("JPConversionString::matches") with self.assertRaisesRegex(SystemError, "fault"): JString._convertToJava("foo") @common.requireInstrumentation def testJPObject(self): jf = JClass("jpype.common.Fixture") jfi = JClass("jpype.common.Fixture")() _jpype.fault("JPClass::setStaticField") with self.assertRaisesRegex(SystemError, "fault"): jf.static_object_field = None _jpype.fault("JPClass::setField") with self.assertRaisesRegex(SystemError, "fault"): jfi.object_field = None i = None _jpype.fault("JPClass::getStaticField") with self.assertRaisesRegex(SystemError, "fault"): i = jf.static_object_field _jpype.fault("JPClass::getField") with self.assertRaisesRegex(SystemError, "fault"): i = jfi.object_field self.assertEqual(i, None) # FIXME this test triggers the wrong fault which normally # indicates a problem in the exception handling path. # AssertionError: "fault" does not match "NULL context in JPRef() # Disabling for now. @common.unittest.SkipTest # type: ignore[arg-type] @common.requireInstrumentation def testJPTypeManagerFindClass(self): ja = JArray(JInt, 2)([[1, 1], [1, 1]]) _jpype.fault("JPTypeManager::findClass") with self.assertRaisesRegex(SystemError, "fault"): memoryview(ja) _jpype.fault("JPTypeManager::findClassByName") with self.assertRaisesRegex(SystemError, "fault"): JClass("foo.bar") jo = JString('a') _jpype.fault("JPTypeManager::findClassForObject") with self.assertRaisesRegex(SystemError, "fault"): jo.substring(0, 1) @common.requireInstrumentation def testJPTypeManagerPopulate(self): _jpype.fault("JPTypeManager::populateMembers") with self.assertRaisesRegex(SystemError, "fault"): JClass("java.math.MathContext") _jpype.fault("JPTypeManager::populateMethod") with self.assertRaisesRegex(SystemError, "fault"): JClass("java.math.MathContext")().getPrecision() JClass("java.math.MathContext") @common.requireInstrumentation def testMethodPack(self): js = JString("a") _jpype.fault("JPMethod::packArgs") with self.assertRaisesRegex(SystemError, "fault"): js.substring(1) @common.requireInstrumentation def testJBoxedGetJavaConversion(self): _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Boolean._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Character._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Byte._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Short._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Integer._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Long._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Float._canConvertToJava(object()) _jpype.fault("JPBoxedType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): java.lang.Double._canConvertToJava(object()) @common.requireInstrumentation def testJPJavaFrame(self): _jpype.fault("JPJavaFrame::JPJavaFrame::NewObjectA") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::JPJavaFrame::NewObject") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::NewDirectByteBuffer") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::GetPrimitiveArrayCritical") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::ReleasePrimitiveArrayCritical") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") @common.requireInstrumentation def testJPJavaFrameByteField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticByteField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_byte_field) _jpype.fault("JPJavaFrame::GetByteField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.byte_field) _jpype.fault("JPJavaFrame::SetStaticByteField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_byte_field = 1 _jpype.fault("JPJavaFrame::SetByteField") with self.assertRaisesRegex(SystemError, "fault"): fields.byte_field = 0 @common.requireInstrumentation def testJPJavaFrameByteMethods(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticByteMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticByte() _jpype.fault("JPJavaFrame::CallByteMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getByte() _jpype.fault("JPJavaFrame::CallNonvirtualByteMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getByte(obj) @common.requireInstrumentation def testJPJavaFrameShortField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticShortField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_short_field) _jpype.fault("JPJavaFrame::GetShortField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.short_field) _jpype.fault("JPJavaFrame::SetStaticShortField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_short_field = 1 _jpype.fault("JPJavaFrame::SetShortField") with self.assertRaisesRegex(SystemError, "fault"): fields.short_field = 1 @common.requireInstrumentation def testJPJavaFrameShortMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticShortMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticShort() _jpype.fault("JPJavaFrame::CallShortMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getShort() _jpype.fault("JPJavaFrame::CallNonvirtualShortMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getShort(obj) @common.requireInstrumentation def testJPJavaFrameIntField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticIntField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_int_field) _jpype.fault("JPJavaFrame::GetIntField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.int_field) _jpype.fault("JPJavaFrame::SetStaticIntField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_int_field = 1 _jpype.fault("JPJavaFrame::SetIntField") with self.assertRaisesRegex(SystemError, "fault"): fields.int_field = 1 @common.requireInstrumentation def testJPJavaFrameIntMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticIntMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticInt() _jpype.fault("JPJavaFrame::CallIntMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getInt() _jpype.fault("JPJavaFrame::CallNonvirtualIntMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getInt(obj) @common.requireInstrumentation def testJPJavaFrameLongField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticLongField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_long_field) _jpype.fault("JPJavaFrame::GetLongField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.long_field) _jpype.fault("JPJavaFrame::SetStaticLongField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_long_field = 1 _jpype.fault("JPJavaFrame::SetLongField") with self.assertRaisesRegex(SystemError, "fault"): fields.long_field = 1 @common.requireInstrumentation def testJPJavaFrameLongMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticLongMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticLong() _jpype.fault("JPJavaFrame::CallLongMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getLong() _jpype.fault("JPJavaFrame::CallNonvirtualLongMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getLong(obj) @common.requireInstrumentation def testJPJavaFrameFloatField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticFloatField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_float_field) _jpype.fault("JPJavaFrame::GetFloatField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.float_field) _jpype.fault("JPJavaFrame::SetStaticFloatField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_float_field = 1 _jpype.fault("JPJavaFrame::SetFloatField") with self.assertRaisesRegex(SystemError, "fault"): fields.float_field = 1 @common.requireInstrumentation def testJPJavaFrameFloatMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticFloatMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticFloat() _jpype.fault("JPJavaFrame::CallFloatMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getFloat() _jpype.fault("JPJavaFrame::CallNonvirtualFloatMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getFloat(obj) @common.requireInstrumentation def testJPJavaFrameDoubleField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticDoubleField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_double_field) _jpype.fault("JPJavaFrame::GetDoubleField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.double_field) _jpype.fault("JPJavaFrame::SetStaticDoubleField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_double_field = 1 _jpype.fault("JPJavaFrame::SetDoubleField") with self.assertRaisesRegex(SystemError, "fault"): fields.double_field = 1 @common.requireInstrumentation def testJPJavaFrameDoubleMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticDoubleMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticDouble() _jpype.fault("JPJavaFrame::CallDoubleMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getDouble() _jpype.fault("JPJavaFrame::CallNonvirtualDoubleMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getDouble(obj) @common.requireInstrumentation def testJPJavaFrameCharField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticCharField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_char_field) _jpype.fault("JPJavaFrame::GetCharField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.char_field) _jpype.fault("JPJavaFrame::SetStaticCharField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_char_field = 1 _jpype.fault("JPJavaFrame::SetCharField") with self.assertRaisesRegex(SystemError, "fault"): fields.char_field = 1 @common.requireInstrumentation def testJPJavaFrameCharMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticCharMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticChar() _jpype.fault("JPJavaFrame::CallCharMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getChar() _jpype.fault("JPJavaFrame::CallNonvirtualCharMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getChar(obj) @common.requireInstrumentation def testJPJavaFrameBooleanField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticBooleanField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_bool_field) _jpype.fault("JPJavaFrame::GetBooleanField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.bool_field) _jpype.fault("JPJavaFrame::SetStaticBooleanField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_bool_field = 1 _jpype.fault("JPJavaFrame::SetBooleanField") with self.assertRaisesRegex(SystemError, "fault"): fields.bool_field = 1 @common.requireInstrumentation def testJPJavaFrameBooleanMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticBooleanMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticBool() _jpype.fault("JPJavaFrame::CallBooleanMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getBool() _jpype.fault("JPJavaFrame::CallNonvirtualBooleanMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getBool(obj) @common.requireInstrumentation def testJPJavaFrameObjectField(self): fields = JClass("jpype.common.Fixture")() _jpype.fault("JPJavaFrame::GetStaticObjectField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.static_object_field) _jpype.fault("JPJavaFrame::GetObjectField") with self.assertRaisesRegex(SystemError, "fault"): print(fields.object_field) _jpype.fault("JPJavaFrame::SetStaticObjectField") with self.assertRaisesRegex(SystemError, "fault"): fields.static_object_field = None _jpype.fault("JPJavaFrame::SetObjectField") with self.assertRaisesRegex(SystemError, "fault"): fields.object_field = None @common.requireInstrumentation def testJPJavaFrameObjectMethod(self): cls = JClass("jpype.common.Fixture") obj = cls() _jpype.fault("JPJavaFrame::CallStaticObjectMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getStaticObject() _jpype.fault("JPJavaFrame::CallObjectMethodA") with self.assertRaisesRegex(SystemError, "fault"): obj.getObject() _jpype.fault("JPJavaFrame::CallNonvirtualObjectMethodA") with self.assertRaisesRegex(SystemError, "fault"): cls.getObject(obj) @common.requireInstrumentation def testJPJavaFrameBooleanArray(self): _jpype.fault("JPJavaFrame::NewBooleanArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JBoolean)(1) ja = JArray(JBoolean)(5) _jpype.fault("JPJavaFrame::SetBooleanArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetBooleanArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetBooleanArrayElements") with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseBooleanArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseBooleanArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseBooleanArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() @common.requireInstrumentation def testJPJavaFrameMonitor(self): jo = JClass("java.lang.Object")() _jpype.fault("JPJavaFrame::MonitorEnter") with self.assertRaisesRegex(SystemError, "fault"): with jpype.synchronized(jo): pass _jpype.fault("JPJavaFrame::MonitorExit") with self.assertRaisesRegex(SystemError, "fault"): with jpype.synchronized(jo): pass @common.requireInstrumentation def testJPJavaFrameMonitor_2(self): _jpype.fault("JPJavaFrame::FromReflectedMethod") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::FromReflectedField") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::FindClass") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") @common.requireInstrumentation def testJPJavaFrameObjectArray(self): _jpype.fault("JPJavaFrame::NewObjectArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JObject)(1) ja = JArray(JObject)(1) _jpype.fault("JPJavaFrame::SetObjectArrayElement") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = None _jpype.fault("JPJavaFrame::GetObjectArrayElement") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) @common.requireInstrumentation def testJPJavaFrameVoidMethod(self): _jpype.fault("JPJavaFrame::CallStaticVoidMethodA") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::CallVoidMethodA") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("JPJavaFrame::CallVoidMethodA") with self.assertRaisesRegex(SystemError, "fault"): raise SystemError("fault") _jpype.fault("None") @common.requireInstrumentation def testJPJavaFrameAssignable(self): _jpype.fault("JPJavaFrame::IsAssignableFrom") with self.assertRaisesRegex(SystemError, "fault"): issubclass(JString, JObject) @common.requireInstrumentation def testJPJavaFrameString(self): _jpype.fault("JPJavaFrame::NewString") with self.assertRaisesRegex(SystemError, "fault"): JString("aa") _jpype.fault("JPJavaFrame::GetStringUTFChars") with self.assertRaisesRegex(SystemError, "fault"): str(JString("a")) _jpype.fault("JPJavaFrame::ReleaseStringUTFChars") # Releas must not pass the exception because it would # cause an abort. str(JString("a")) _jpype.fault("JPJavaFrame::GetStringUTFLength") with self.assertRaisesRegex(SystemError, "fault"): str(JString("a")) @common.requireInstrumentation def testJPJavaFrameArrayLength(self): _jpype.fault("JPJavaFrame::GetArrayLength") with self.assertRaisesRegex(SystemError, "fault"): JArray(JInt)(5) # These are very hard to hit as they are all startup routines # _jpype.fault("JPJavaFrame::GetMethodID") # with self.assertRaisesRegex(SystemError, "fault"): # raise SystemError("fault") # _jpype.fault("JPJavaFrame::GetStaticMethodID") # with self.assertRaisesRegex(SystemError, "fault"): # raise SystemError("fault") # _jpype.fault("JPJavaFrame::GetFieldID") # with self.assertRaisesRegex(SystemError, "fault"): # raise SystemError("fault") # _jpype.fault("JPJavaFrame::DefineClass") # with self.assertRaisesRegex(SystemError, "fault"): # raise SystemError("fault") # _jpype.fault("JPJavaFrame::RegisterNatives") # with self.assertRaisesRegex(SystemError, "fault"): # raise SystemError("fault") @common.requireInstrumentation def testJPClassTypeGetJavaConversion(self): jc = JClass("java.lang.StringBuilder") _jpype.fault("JPClass::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): jc._canConvertToJava(object()) @jpype.JConversion("java.lang.StringBuilder", exact=object) def f(self, obj): raise SystemError("fail") with self.assertRaisesRegex(SystemError, "fail"): jc._convertToJava(object()) @common.requireInstrumentation def testStartup(self): _jpype.fault("PyJPModule_startup") with self.assertRaisesRegex(SystemError, "fault"): _jpype.startup() @common.requireInstrumentation def testShutdown(self): _jpype.fault("PyJPModule_shutdown") with self.assertRaisesRegex(SystemError, "fault"): _jpype.shutdown() @common.requireInstrumentation def testAttachThread(self): _jpype.fault("PyJPModule_attachThread") with self.assertRaisesRegex(SystemError, "fault"): _jpype.attachThreadToJVM() @common.requireInstrumentation def testAttachThreadAsDaemon(self): _jpype.fault("PyJPModule_attachThreadAsDaemon") with self.assertRaisesRegex(SystemError, "fault"): _jpype.attachThreadAsDaemon() @common.requireInstrumentation def testDetachThreadFault(self): _jpype.fault("PyJPModule_detachThread") with self.assertRaisesRegex(SystemError, "fault"): _jpype.detachThreadFromJVM() def testDetachThread(self): self.assertTrue(_jpype.isThreadAttachedToJVM()) _jpype.detachThreadFromJVM() self.assertFalse(_jpype.isThreadAttachedToJVM()) _jpype.attachThreadToJVM() @common.requireInstrumentation def testIsThreadAttached(self): _jpype.fault("PyJPModule_isThreadAttached") with self.assertRaisesRegex(SystemError, "fault"): _jpype.isThreadAttachedToJVM() @common.requireInstrumentation def testConvertToDirectBufferFault(self): _jpype.fault("PyJPModule_convertToDirectByteBuffer") with self.assertRaisesRegex(SystemError, "fault"): _jpype.convertToDirectBuffer(1, False) def testConvertToDirectBufferExc(self): with self.assertRaisesRegex(TypeError, "buffer support"): _jpype.convertToDirectBuffer(1, False) with self.assertRaisesRegex(BufferError, "not writable"): _jpype.convertToDirectBuffer(bytes([1, 2, 3]), False) with self.assertRaisesRegex(TypeError, "function takes exactly 2 arguments"): _jpype.convertToDirectBuffer(bytes([1, 2, 3])) def testStartupBadArg(self): with self.assertRaisesRegex(TypeError, "takes exactly"): _jpype.startup() with self.assertRaisesRegex(TypeError, "must be tuple"): _jpype.startup(object(), object(), True, True, True, None) with self.assertRaisesRegex(TypeError, "must be strings"): _jpype.startup("", (object(),), True, True, True, None) with self.assertRaisesRegex(TypeError, "must be a string"): _jpype.startup(object(), tuple(), True, True, True, None) with self.assertRaisesRegex(TypeError, "must be a string"): _jpype.startup("", tuple(), True, True, True, object()) with self.assertRaisesRegex(OSError, "started"): _jpype.startup("", tuple(), True, True, True, None) def testGetClass(self): with self.assertRaisesRegex(TypeError, "not found"): _jpype._getClass("not.a.class") def testHasClass(self): with self.assertRaisesRegex(TypeError, "not found"): _jpype._hasClass("not.a.class") with self.assertRaisesRegex(TypeError, "str is required"): _jpype._hasClass(object()) def testArrayFromBuffer(self): with self.assertRaisesRegex(TypeError, "2 arguments"): _jpype.arrayFromBuffer() with self.assertRaisesRegex(TypeError, "does not support buffers"): _jpype.arrayFromBuffer(object(), object()) def testArrayNewType(self): with self.assertRaisesRegex(TypeError, "2 arguments"): _jpype._newArrayType() with self.assertRaisesRegex(TypeError, "must be an integer"): _jpype._newArrayType(object(), object()) with self.assertRaisesRegex(TypeError, "class required"): _jpype._newArrayType(object(), 1) @common.requireInstrumentation def testClassHintsFaults(self): class CTest(object): pass @JConversion("java.lang.Number", exact=CTest) def _CTestConversion(jcls, obj): return java.lang.Integer(123) class ATest(object): def __foo__(self): pass @JConversion("java.lang.Number", attribute="__foo__") def _ATestConversion(jcls, obj): return java.lang.Integer(123) fixture = JClass("jpype.common.Fixture")() _jpype.fault("JPPythonConversion::convert") with self.assertRaisesRegex(SystemError, "fault"): fixture.callNumber(CTest()) _jpype.fault("JPAttributeConversion::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callNumber(ATest()) _jpype.fault("JPClassHints::addAttributeConversion") with self.assertRaisesRegex(SystemError, "fault"): @JConversion("java.lang.Number", attribute="__woo__") def f(jcls, obj): pass _jpype.fault("JPTypeConversion::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callNumber(CTest()) _jpype.fault("JPClassHints::addTypeConversion") with self.assertRaisesRegex(SystemError, "fault"): @JConversion("java.lang.Number", exact=CTest) def f(jcls, obj): pass @common.requireInstrumentation def testConversionFaults(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("JPConversionCharArray::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callCharArray(object()) _jpype.fault("JPConversionByteArray::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callByteArray(object()) _jpype.fault("JPConversionNull::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(None) _jpype.fault("JPConversionClass::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callClass(object()) _jpype.fault("JPConversionObject::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(object()) _jpype.fault("JPConversionJavaObjectAny::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(object()) _jpype.fault("JPConversionJavaNumberAny::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callNumber(object()) _jpype.fault("JPConversionString::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callString(object()) _jpype.fault("JPConversionBoxBoolean::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(object()) _jpype.fault("JPConversionBoxLong::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(object()) _jpype.fault("JPConversionBoxDouble::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(object()) _jpype.fault("JPConversionProxy::matches") with self.assertRaisesRegex(SystemError, "fault"): fixture.callObject(object()) jpype-1.6.0/test/jpypetest/test_fields.py000066400000000000000000000207071501674766700205630ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * from jpype import JPackage, java import common class FieldsTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.cls = JClass("jpype.common.Fixture") self.obj = self.cls() def testBoolean(self): self.obj.bool_field = True self.assertTrue(self.obj.bool_field) self.assertTrue(self.obj.getBool()) self.obj.bool_field = False self.assertFalse(self.obj.bool_field) self.assertFalse(self.obj.getBool()) self.obj.static_bool_field = True self.assertTrue(self.obj.static_bool_field) self.assertTrue(self.cls.static_bool_field) self.obj.static_bool_field = False self.assertFalse(self.obj.static_bool_field) self.assertFalse(self.cls.static_bool_field) with self.assertRaises(AttributeError): self.cls.bool_field = True with self.assertRaises(AttributeError): self.cls.bool_field = False self.cls.static_bool_field = True self.assertTrue(self.cls.static_bool_field) self.assertTrue(self.cls.getStaticBool()) self.cls.static_bool_field = False self.assertFalse(self.cls.static_bool_field) self.assertFalse(self.cls.getStaticBool()) def testChar(self): self.obj.char_field = 'Q' self.assertEqual(self.obj.char_field, 'Q') self.assertEqual(self.obj.getChar(), 'Q') self.obj.static_char_field = 'S' self.assertEqual(self.obj.static_char_field, 'S') self.assertEqual(self.cls.static_char_field, 'S') self.assertEqual(self.obj.getStaticChar(), 'S') self.assertEqual(self.cls.getStaticChar(), 'S') with self.assertRaises(AttributeError): self.cls.char_field = 'U' with self.assertRaises(AttributeError): self.cls.char_field = 'V' self.cls.static_char_field = 'W' self.assertEqual(self.cls.static_char_field, 'W') self.assertEqual(self.obj.getStaticChar(), 'W') self.assertEqual(self.cls.getStaticChar(), 'W') def testByte(self): self.obj.byte_field = 34 self.assertEqual(self.obj.byte_field, 34) self.assertEqual(self.obj.getByte(), 34) self.obj.static_byte_field = 36 self.assertEqual(self.obj.static_byte_field, 36) self.assertEqual(self.cls.static_byte_field, 36) self.assertEqual(self.obj.getStaticByte(), 36) self.assertEqual(self.cls.getStaticByte(), 36) with self.assertRaises(AttributeError): self.cls.byte_field = 38 with self.assertRaises(AttributeError): self.cls.byte_field = 39 self.cls.static_byte_field = 40 self.assertEqual(self.cls.static_byte_field, 40) self.assertEqual(self.obj.getStaticByte(), 40) self.assertEqual(self.cls.getStaticByte(), 40) def testShort(self): self.obj.short_field = 34 self.assertEqual(self.obj.short_field, 34) self.assertEqual(self.obj.getShort(), 34) self.obj.static_short_field = 36 self.assertEqual(self.obj.static_short_field, 36) self.assertEqual(self.cls.static_short_field, 36) self.assertEqual(self.obj.getStaticShort(), 36) self.assertEqual(self.cls.getStaticShort(), 36) with self.assertRaises(AttributeError): self.cls.short_field = 38 with self.assertRaises(AttributeError): self.cls.short_field = 39 self.cls.static_short_field = 40 self.assertEqual(self.cls.static_short_field, 40) self.assertEqual(self.obj.getStaticShort(), 40) self.assertEqual(self.cls.getStaticShort(), 40) def testInt(self): self.obj.int_field = 34 self.assertEqual(self.obj.int_field, 34) self.assertEqual(self.obj.getInt(), 34) self.obj.static_int_field = 36 self.assertEqual(self.obj.static_int_field, 36) self.assertEqual(self.cls.static_int_field, 36) self.assertEqual(self.obj.getStaticInt(), 36) self.assertEqual(self.cls.getStaticInt(), 36) with self.assertRaises(AttributeError): self.cls.int_field = 38 with self.assertRaises(AttributeError): self.cls.int_field = 39 self.cls.static_int_field = 40 self.assertEqual(self.cls.static_int_field, 40) self.assertEqual(self.obj.getStaticInt(), 40) self.assertEqual(self.cls.getStaticInt(), 40) def testLong(self): self.obj.long_field = 34 self.assertEqual(self.obj.long_field, 34) self.assertEqual(self.obj.getLong(), 34) self.obj.static_long_field = 36 self.assertEqual(self.obj.static_long_field, 36) self.assertEqual(self.cls.static_long_field, 36) self.assertEqual(self.obj.getStaticLong(), 36) self.assertEqual(self.cls.getStaticLong(), 36) with self.assertRaises(AttributeError): self.cls.long_field = 38 with self.assertRaises(AttributeError): self.cls.long_field = 39 self.cls.static_long_field = 40 self.assertEqual(self.cls.static_long_field, 40) self.assertEqual(self.obj.getStaticLong(), 40) self.assertEqual(self.cls.getStaticLong(), 40) def testFloat(self): self.obj.float_field = 34 self.assertEqual(self.obj.float_field, 34) self.assertEqual(self.obj.getFloat(), 34) self.obj.static_float_field = 36 self.assertEqual(self.obj.static_float_field, 36) self.assertEqual(self.cls.static_float_field, 36) self.assertEqual(self.obj.getStaticFloat(), 36) self.assertEqual(self.cls.getStaticFloat(), 36) with self.assertRaises(AttributeError): self.cls.float_field = 38 with self.assertRaises(AttributeError): self.cls.float_field = 39 self.cls.static_float_field = 40 self.assertEqual(self.cls.static_float_field, 40) self.assertEqual(self.obj.getStaticFloat(), 40) self.assertEqual(self.cls.getStaticFloat(), 40) def testDouble(self): self.obj.double_field = 34 self.assertEqual(self.obj.double_field, 34) self.assertEqual(self.obj.getDouble(), 34) self.obj.static_double_field = 36 self.assertEqual(self.obj.static_double_field, 36) self.assertEqual(self.cls.static_double_field, 36) self.assertEqual(self.obj.getStaticDouble(), 36) self.assertEqual(self.cls.getStaticDouble(), 36) with self.assertRaises(AttributeError): self.cls.double_field = 38 with self.assertRaises(AttributeError): self.cls.double_field = 39 self.cls.static_double_field = 40 self.assertEqual(self.cls.static_double_field, 40) self.assertEqual(self.obj.getStaticDouble(), 40) self.assertEqual(self.cls.getStaticDouble(), 40) def testObject(self): self.obj.object_field = "Alice" self.assertEqual(self.obj.object_field, "Alice") self.assertEqual(self.obj.getObject(), "Alice") self.obj.static_object_field = "Bob" self.assertEqual(self.obj.static_object_field, "Bob") self.assertEqual(self.cls.static_object_field, "Bob") self.assertEqual(self.obj.getStaticObject(), "Bob") self.assertEqual(self.cls.getStaticObject(), "Bob") with self.assertRaises(AttributeError): self.cls.object_field = "Xena" with self.assertRaises(AttributeError): self.cls.object_field = "Gabrielle" self.cls.static_object_field = "Charlie" self.assertEqual(self.cls.static_object_field, "Charlie") self.assertEqual(self.obj.getStaticObject(), "Charlie") self.assertEqual(self.cls.getStaticObject(), "Charlie") jpype-1.6.0/test/jpypetest/test_forname.py000066400000000000000000000032511501674766700207370ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common class ForNameTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testForName(self): cls = jpype.JClass('java.lang.Class') test = cls.forName('jpype.overloads.Test1') # Should return a java.lang.Class, rather than the python wrapper for java.lang.Class self.assertTrue(type(test) == type(cls.class_)) self.assertEqual(test.getName(), 'jpype.overloads.Test1') def testForName2(self): cls = jpype.JClass('java.lang.Class') clsloader = jpype.JClass( 'java.lang.ClassLoader').getSystemClassLoader() test = cls.forName('jpype.overloads.Test1', True, clsloader) # Should return a java.lang.Class, rather than the python wrapper for java.lang.Class self.assertTrue(type(test) == type(cls.class_)) self.assertEqual(test.getName(), 'jpype.overloads.Test1') jpype-1.6.0/test/jpypetest/test_functional.py000066400000000000000000000042311501674766700214510ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common def callFunctional(cls, i): fun = cls @(lambda x: x) return fun.call(i) class FunctionalTestCase(common.JPypeTestCase): def _checkValidFunctional(self, name, value): cls = jpype.JClass("jpype.functional." + name) self.assertEqual(callFunctional(cls, value), value) def testAnnotated(self): self._checkValidFunctional("Annotated", 0) def testNonAnnotated(self): self._checkValidFunctional("NonAnnotated", 1) def testExtendsFunctional(self): self._checkValidFunctional("ExtendsFunctional", 2) def testExtendsNonFunctional(self): with self.assertRaises(TypeError): self._checkValidFunctional("ExtendsNonFunctional", 3) def testAnnotatedWithObjectMethods(self): self._checkValidFunctional("AnnotatedWithObjectMethods", 4) def testNonAnnotatedWithObjectMethods(self): self._checkValidFunctional("NonAnnotatedWithObjectMethods", 5) def testRedeclaresAnnotated(self): self._checkValidFunctional("RedeclaresAnnotated", 6) def testRedeclaresNonAnnotated(self): self._checkValidFunctional("RedeclaresNonAnnotated", 7) def testStaticDefaultMethods(self): # Predicate has both static and default methods Predicate = jpype.JClass("java.util.function.Predicate") fun = Predicate @ (lambda x: True) self.assertTrue(fun.test(fun)) jpype-1.6.0/test/jpypetest/test_generic.py000066400000000000000000000033421501674766700207250ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class GenericTestCase(common.JPypeTestCase): """ Test for JClass with generic types """ def setUp(self): common.JPypeTestCase.setUp(self) def testArray0(self): cls = jpype.JClass('java.util.ArrayList<>') def testArray1(self): cls = jpype.JClass('java.util.ArrayList') def testArray2(self): with self.assertRaises(TypeError): cls = jpype.JClass('java.util.ArrayList') def testMap0(self): cls = jpype.JClass('java.util.HashMap<>') def testMap1(self): with self.assertRaises(TypeError): cls = jpype.JClass('java.util.HashMap') def testMap2(self): cls = jpype.JClass('java.util.HashMap') def testObject0(self): with self.assertRaises(TypeError): cls = jpype.JClass('java.lang.Object<>') def testObject1(self): with self.assertRaises(TypeError): cls = jpype.JClass('java.lang.Object') jpype-1.6.0/test/jpypetest/test_hash.py000066400000000000000000000054401501674766700202350ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common import sys class HashTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testHashString(self): self.assertIsNotNone(hash(jpype.java.lang.String("upside down"))) self.assertIsNotNone(hash(jpype.JString("upside down"))) self.assertEqual(hash(jpype.JString("upside down")), hash("upside down")) def testHashArray(self): self.assertIsNotNone(hash(jpype.JArray(jpype.JInt)([1, 2, 3]))) def testHashObject(self): self.assertIsNotNone(hash(jpype.java.lang.Object())) def testHashBoolean(self): self.assertIsNotNone(hash(jpype.java.lang.Boolean(True))) self.assertEqual(hash(jpype.java.lang.Boolean(True)), hash(True)) def testHashByte(self): self.assertIsNotNone(hash(jpype.java.lang.Byte(5))) self.assertEqual(hash(jpype.java.lang.Byte(5)), hash(5)) def testHashChar(self): self.assertIsNotNone(hash(jpype.java.lang.Character("a"))) # Differences in implementation yield different hashes currently. #self.assertEqual(hash(jpype.java.lang.Character("a")), hash("a")) def testHashShort(self): self.assertIsNotNone(hash(jpype.java.lang.Short(1))) self.assertEqual(hash(jpype.java.lang.Short(1)), hash(1)) def testHashLong(self): self.assertIsNotNone(hash(jpype.java.lang.Long(55))) self.assertEqual(hash(jpype.java.lang.Long(55)), hash(55)) def testHashInteger(self): self.assertIsNotNone(hash(jpype.java.lang.Integer(123))) self.assertEqual(hash(jpype.java.lang.Integer(123)), hash(123)) def testHashFloat(self): self.assertIsNotNone(hash(jpype.java.lang.Float(3.141592))) def testHashDouble(self): self.assertIsNotNone(hash(jpype.java.lang.Double(6.62607004e-34))) def testHashNone(self): self.assertEqual(hash(None), hash(jpype.JObject(None))) q = jpype.JObject(None, jpype.java.lang.Double) self.assertEqual(hash(None), hash(jpype.JObject(None))) jpype-1.6.0/test/jpypetest/test_hints.py000066400000000000000000000444221501674766700204420ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype import java from jpype.types import * from jpype import JConversion import common import jpype.protocol as proto class HintsTestCase(common.JPypeTestCase): def testCache(self): cls = JClass('java.lang.Object') hints = cls._hints hints2 = cls._hints self.assertEqual(hints, hints2) def testProtocol(self): protocol = jpype.protocol.SupportsFloat annot = protocol.__float__.__annotations__ self.assertEqual(annot['return'], float) protocol = jpype.protocol.SupportsIndex annot = protocol.__index__.__annotations__ self.assertEqual(annot['return'], int) self.assertTrue(hasattr(proto, "Sequence")) self.assertTrue(hasattr(proto, "Mapping")) self.assertTrue(hasattr(proto, "Protocol")) def testObject(self): cls = JClass('java.lang.Object') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(str in hints.implicit) self.assertTrue(bool in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.implicit) self.assertTrue(proto._JClass in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testNumber(self): cls = JClass('java.lang.Number') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(proto._JNumberLong in hints.implicit) self.assertTrue(proto._JNumberFloat in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testString(self): cls = JClass('java.lang.String') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(str in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testClass(self): cls = JClass('java.lang.Class') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto._JClass in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testCollection(self): cls = JClass('java.util.Collection') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testList(self): cls = JClass('java.util.List') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(len(hints.implicit) == 0) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testJBoolean(self): cls = JBoolean hints = cls._hints self.assertTrue(bool in hints.returns) self.assertTrue(bool in hints.exact) self.assertTrue(cls in hints.exact) self.assertTrue(JClass("java.lang.Boolean") in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(len(hints.none) == 0) def testJChar(self): cls = JChar hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JClass("java.lang.Character") in hints.implicit) self.assertTrue(str in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testJShort(self): cls = JShort hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JByte in hints.implicit) self.assertTrue(JChar in hints.implicit) self.assertTrue(JClass("java.lang.Short") in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(len(hints.none) == 0) def testJInt(self): cls = JInt hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JByte in hints.implicit) self.assertTrue(JChar in hints.implicit) self.assertTrue(JShort in hints.implicit) self.assertTrue(JClass('java.lang.Integer') in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(len(hints.none) == 0) def testJLong(self): cls = JLong hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JByte in hints.implicit) self.assertTrue(JChar in hints.implicit) self.assertTrue(JShort in hints.implicit) self.assertTrue(JInt in hints.implicit) self.assertTrue(JClass('java.lang.Long') in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(len(hints.none) == 0) def testJFloat(self): cls = JFloat hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JByte in hints.implicit) self.assertTrue(JChar in hints.implicit) self.assertTrue(JShort in hints.implicit) self.assertTrue(JInt in hints.implicit) self.assertTrue(JLong in hints.implicit) self.assertTrue(JClass('java.lang.Float') in hints.implicit) self.assertTrue(int in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testJDouble(self): cls = JDouble hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JByte in hints.implicit) self.assertTrue(JChar in hints.implicit) self.assertTrue(JShort in hints.implicit) self.assertTrue(JInt in hints.implicit) self.assertTrue(JLong in hints.implicit) self.assertTrue(JFloat in hints.implicit) self.assertTrue(JClass('java.lang.Double') in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.implicit) self.assertTrue(int in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testBoxedByte(self): cls = JClass('java.lang.Byte') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JByte in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(JClass('java.lang.Byte') in hints.explicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedBoolean(self): cls = JClass('java.lang.Boolean') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(bool in hints.implicit) self.assertTrue(JBoolean in hints.implicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(JClass('java.lang.Boolean') in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedCharacter(self): cls = JClass('java.lang.Character') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JChar in hints.implicit) self.assertTrue(JClass('java.lang.Character') in hints.explicit) self.assertTrue(str in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedShort(self): cls = JClass('java.lang.Short') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JShort in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(JByte in hints.explicit) self.assertTrue(JChar in hints.explicit) self.assertTrue(JClass('java.lang.Short') in hints.explicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedInteger(self): cls = JClass('java.lang.Integer') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JInt in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(JByte in hints.explicit) self.assertTrue(JChar in hints.explicit) self.assertTrue(JShort in hints.explicit) self.assertTrue(JClass('java.lang.Integer') in hints.explicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedLong(self): cls = JClass('java.lang.Long') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JLong in hints.implicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(JByte in hints.explicit) self.assertTrue(JChar in hints.explicit) self.assertTrue(JShort in hints.explicit) self.assertTrue(JInt in hints.explicit) self.assertTrue(JClass('java.lang.Long') in hints.explicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedFloat(self): cls = JClass('java.lang.Float') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JFloat in hints.implicit) self.assertTrue(JByte in hints.explicit) self.assertTrue(JChar in hints.explicit) self.assertTrue(JShort in hints.explicit) self.assertTrue(JInt in hints.explicit) self.assertTrue(JLong in hints.explicit) self.assertTrue(JClass('java.lang.Float') in hints.explicit) self.assertTrue(int in hints.explicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(len(hints.none) == 0) def testBoxedDouble(self): cls = JClass('java.lang.Double') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(JDouble in hints.implicit) self.assertTrue(JByte in hints.explicit) self.assertTrue(JChar in hints.explicit) self.assertTrue(JShort in hints.explicit) self.assertTrue(JInt in hints.explicit) self.assertTrue(JLong in hints.explicit) self.assertTrue(JFloat in hints.explicit) self.assertTrue(JClass('java.lang.Double') in hints.explicit) self.assertTrue(proto.SupportsIndex in hints.explicit) self.assertTrue(proto.SupportsFloat in hints.explicit) self.assertTrue(int in hints.explicit) self.assertTrue(len(hints.none) == 0) def testObjectArray(self): cls = JClass('java.lang.Object[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testBooleanArray(self): cls = JClass('boolean[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testCharArray(self): cls = JClass('char[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(str in hints.implicit) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testShortArray(self): cls = JClass('short[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testIntArray(self): cls = JClass('int[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testLongArray(self): cls = JClass('long[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testFloatArray(self): cls = JClass('float[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testDoubleArray(self): cls = JClass('double[]') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.Sequence in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(str in hints.none) def testPath(self): cls = JClass('java.nio.file.Path') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.SupportsPath in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testFile(self): cls = JClass('java.io.File') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(proto.SupportsPath in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testInstant(self): import datetime cls = JClass('java.time.Instant') hints = cls._hints self.assertTrue(cls in hints.returns) self.assertTrue(cls in hints.exact) self.assertTrue(datetime.datetime in hints.implicit) self.assertTrue(len(hints.explicit) == 0) self.assertTrue(len(hints.none) == 0) def testDate(self): cls = JClass("java.sql.Date") d = cls(120, 0, 5) d2 = d._py() d3 = JObject(d2, cls) self.assertEqual(d, d2) self.assertEqual(d, d3) def testTimestamp(self): cls = JClass("java.sql.Timestamp") d = cls(120, 0, 5, 9, 22, 51, 123456000) d2 = d._py() d3 = JObject(d2, cls) self.assertEqual(d, d2) self.assertEqual(d, d3) def testTime(self): cls = JClass("java.sql.Time") d = cls(11, 53, 1) d2 = d._py() d3 = JObject(d2, cls) self.assertEqual(d, d2) self.assertEqual(d, d3) def testBigDecimal(self): cls = JClass("java.math.BigDecimal") d = cls('1000234600000000000000') d2 = d._py() d3 = JObject(d2, cls) self.assertEqual(d, d2) self.assertEqual(d, d3) def testAddTypeBad(self): cls = JClass('java.lang.Object') with self.assertRaises(TypeError): cls._hints._addTypeConversion() with self.assertRaisesRegex(TypeError, "callable method is required"): cls._hints._addTypeConversion(object, object(), True) with self.assertRaisesRegex(TypeError, "type or protocol is required"): def foo(): pass cls._hints._addTypeConversion(object(), foo, True) def testExcludeBad(self): cls = JClass('java.lang.Object') with self.assertRaisesRegex(TypeError, "type or protocol is required, not 'object'"): cls._hints._excludeConversion(object()) with self.assertRaisesRegex(TypeError, "type or protocol is required, not 'object'"): cls._hints._excludeConversion((object(),)) def testClassToClass(self): fixture = JClass("jpype.common.Fixture")() @JConversion(JByte[:], instanceof=java.lang.String) def ToByteArray(cls, obj): return obj.getBytes() obj1 = java.lang.String("hello") fixture.callByteArray(obj1) jpype-1.6.0/test/jpypetest/test_imports.py000066400000000000000000000117701501674766700210120ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import sys import logging import time import common import subrun def haveJImports(): try: import jpype.imports return True except ImportError: return False def isJavaClass(tp): return isinstance(tp, jpype.JClass) def isJavaEnum(tp): return issubclass(tp, jpype.JClass('java.lang.Enum')) class ImportsTestCase(common.JPypeTestCase): def setUp(self): # logger = logging.getLogger(__name__) # logger.info("TEST:JImports") common.JPypeTestCase.setUp(self) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportPackage(self): import java.lang self.assertTrue(isJavaClass(java.lang.String)) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportClass(self): from java.lang import String self.assertTrue(isJavaClass(String)) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportClassAs(self): from java.lang import String as Str self.assertTrue(isJavaClass(Str)) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportClassMultiple(self): from java.lang import Number, Integer, Double self.assertTrue(isJavaClass(Number)) self.assertTrue(isJavaClass(Integer)) self.assertTrue(isJavaClass(Double)) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportStatic(self): from java.lang.ProcessBuilder import Redirect self.assertTrue(isJavaClass(Redirect)) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportInner(self): from java.lang import Character self.assertTrue(isJavaClass(Character.UnicodeScript)) @common.unittest.skipUnless(haveJImports(), "jpype.imports not available") def testImportInnerEnum(self): from java.lang import Character self.assertTrue(isJavaEnum(Character.UnicodeScript)) def testImportFail(self): with self.assertRaises(ImportError): from java.lang import NotThere def testAlias1(self): jpype.imports.registerDomain("jpypex", alias="jpype") from jpypex.common import Fixture # type: ignore self.assertEqual(Fixture, jpype.JClass("jpype.common.Fixture")) def testAlias2(self): jpype.imports.registerDomain("commonx", alias="jpype.common") from commonx import Fixture as Fixture2 # type: ignore self.assertEqual(Fixture2, jpype.JClass("jpype.common.Fixture")) def testAliasBad(self): jpype.imports.registerDomain("brokenx", alias="jpype.broken") with self.assertRaises(ImportError): from brokenx import Fixture as Fixture2 # type: ignore def testIsPackage(self): import java.lang self.assertIsInstance(java, jpype.JPackage) self.assertIsInstance(java.lang, jpype.JPackage) self.assertFalse(isinstance(java.lang.Class, jpype.JPackage)) self.assertTrue(issubclass(type(java.lang), jpype.JPackage)) def testMRJar(self): import org.jpype.mrjar as mrjar # type: ignore u = dir(mrjar) self.assertTrue("A" in u) self.assertTrue("B" in u) self.assertTrue("sub" in u) def testAddClassPath(self): import pathlib import org.jpype as ojp self.assertFalse("late" in dir(ojp)) with self.assertRaises(ImportError): import org.jpype.late as late # type: ignore jpype.addClassPath(pathlib.Path("test/jar/late/late.jar").absolute()) import org.jpype.late as late self.assertTrue("Test" in dir(late)) t = late.Test() self.assertTrue(t.field == 5) self.assertTrue(t.method() == "Yes") def testStar(self): import importstar def testMissing(self): import org self.assertTrue("missing" in dir(org.jpype)) @subrun.TestCase class ImportsBeforeCase(common.unittest.TestCase): def setUp(self): self.jvmpath = jpype.getDefaultJVMPath() def testPre(self): with self.assertRaises(ImportError): import java with self.assertRaises(ImportError): import java.lang jpype-1.6.0/test/jpypetest/test_inherit.py000066400000000000000000000126771501674766700207660ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * from jpype import java, JInterface import common class InheritTestCase(common.JPypeTestCase): """ test for isinstance and issubclass """ def setUp(self): common.JPypeTestCase.setUp(self) def assertIsSubclass(self, a, b): if not issubclass(a, b): raise AssertionError("'%s' is not a subclass of '%s'" % (a, b)) def assertNotIsSubclass(self, a, b): if issubclass(a, b): raise AssertionError("'%s' is a subclass of '%s'" % (a, b)) def testPrimitivesAsObjects(self): self.assertNotIsInstance(JBoolean(1), JObject) self.assertNotIsInstance(JByte(1), JObject) self.assertNotIsInstance(JChar(1), JObject) self.assertNotIsInstance(JShort(1), JObject) self.assertNotIsInstance(JInt(1), JObject) self.assertNotIsInstance(JLong(1), JObject) self.assertNotIsInstance(JFloat(1), JObject) self.assertNotIsInstance(JDouble(1), JObject) self.assertNotIsSubclass(JBoolean, JObject) self.assertNotIsSubclass(JByte, JObject) self.assertNotIsSubclass(JChar, JObject) self.assertNotIsSubclass(JShort, JObject) self.assertNotIsSubclass(JInt, JObject) self.assertNotIsSubclass(JLong, JObject) self.assertNotIsSubclass(JFloat, JObject) self.assertNotIsSubclass(JDouble, JObject) def testPrimitivesAsInterfaces(self): self.assertNotIsInstance(JBoolean, JInterface) self.assertNotIsInstance(JByte, JInterface) self.assertNotIsInstance(JChar, JInterface) self.assertNotIsInstance(JShort, JInterface) self.assertNotIsInstance(JInt, JInterface) self.assertNotIsInstance(JLong, JInterface) self.assertNotIsInstance(JFloat, JInterface) self.assertNotIsInstance(JDouble, JInterface) def testPrimitivesAsClasses(self): self.assertIsInstance(JBoolean, JClass) self.assertIsInstance(JByte, JClass) self.assertIsInstance(JChar, JClass) self.assertIsInstance(JShort, JClass) self.assertIsInstance(JInt, JClass) self.assertIsInstance(JLong, JClass) self.assertIsInstance(JFloat, JClass) self.assertIsInstance(JDouble, JClass) def testString(self): self.assertIsSubclass(JString, JObject) self.assertIsSubclass(java.lang.String, JObject) self.assertIsSubclass(JString, JString) self.assertIsSubclass(java.lang.String, JString) self.assertIsInstance(JString("f"), JObject) self.assertIsInstance(java.lang.String("f"), JObject) self.assertIsInstance(JString("f"), JString) self.assertIsInstance(java.lang.String("f"), JString) self.assertNotIsInstance(JString, JInterface) self.assertNotIsInstance(java.lang.String, JInterface) self.assertIsSubclass(JString, java.lang.String) def testObject(self): self.assertIsInstance(JObject, JClass) self.assertIsInstance(java.lang.Object, JClass) self.assertNotIsSubclass(JObject, JInterface) self.assertNotIsSubclass(java.lang.Object, JInterface) self.assertIsSubclass(JObject, JObject) self.assertIsSubclass(JObject, java.lang.Object) self.assertNotIsSubclass(JObject, JException) self.assertNotIsInstance(JObject(), JInterface) self.assertNotIsInstance(java.lang.Object(), JInterface) self.assertIsInstance(JObject(), JObject) self.assertIsInstance(JObject(), java.lang.Object) def testException(self): # JException is a bit of screw ball as it must be # a subclass of Exception so we can catch it, but at the same # time it is also a meta class in the original API. self.assertIsInstance(JException, JClass) self.assertIsInstance(java.lang.Throwable, JClass) self.assertNotIsSubclass(JException, JInterface) self.assertNotIsSubclass(java.lang.Throwable, JInterface) self.assertIsSubclass(JException, Exception) self.assertIsSubclass(JException, JException) self.assertNotIsInstance(java.lang.Throwable("f"), JInterface) th = java.lang.Throwable("foo") self.assertIsInstance(th, JObject) self.assertIsInstance(th, java.lang.Object) self.assertIsInstance(th, java.lang.Throwable) self.assertIsInstance(th, JException) self.assertIsInstance(th, Exception) with self.assertRaises(Exception): raise th with self.assertRaises(JException): raise th def testInterface(self): # JInterface is a meta but was a base class at some point self.assertIsInstance(java.io.Serializable, JInterface) self.assertIsSubclass(java.io.Serializable, JInterface) jpype-1.6.0/test/jpypetest/test_javacoverage.py000066400000000000000000000136561501674766700217570ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype import _jpype from jpype.types import * from jpype import java, JImplements, JOverride import common class JavaCoverageTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.fixture = JClass('jpype.common.Fixture')() JPypeContext = JClass('org.jpype.JPypeContext') self.inst = JPypeContext.getInstance() def testTypeFactory(self): self.assertNotEqual(self.inst.getTypeFactory(), None) def testContext(self): self.assertEqual(self.inst.collectRectangular(None), None) self.assertEqual(self.inst.collectRectangular(JString('hello')), None) self.assertEqual(self.inst.collectRectangular( JArray(JObject, 2)([JArray(JObject)(0)])), None) self.assertEqual(self.inst.collectRectangular( JArray(JObject)([None, None])), None) self.assertEqual(self.inst.getExcValue(None), 0) def testReference(self): JPypeReference = JClass('org.jpype.ref.JPypeReference') u = JPypeReference(None, None, 0, 0) u2 = JPypeReference(None, None, 1, 0) self.assertTrue(u.equals(u)) self.assertFalse(u.equals(u2)) self.assertFalse(u.equals(JString("a"))) def testModifiers(self): cls = JClass('org.jpype.manager.ModifierCode') self.assertEqual(cls.get(cls.decode(1171)), 1171) def testTypeFactory_2(self): TypeFactory = JClass("org.jpype.manager.TypeFactory") TypeManager = JClass("org.jpype.manager.TypeManager") @JImplements(TypeFactory) class TF(object): def __init__(self): self.id = 0 self.entities = {} def define(self, name): self.id += 1 self.entities[self.id] = name return self.id @JOverride def newWrapper(self, context, cls): pass @JOverride def defineArrayClass(self, context, cls, name, superClass, componentPtr, modifiers): return self.define(name) @JOverride def defineObjectClass(self, context, cls, name, superClass, interfaces, modifiers): return self.define(name) @JOverride def definePrimitive(self, context, name, cls, boxedPtr, modifiers): return self.define(name) @JOverride def assignMembers(self, context, cls, ctorMethod, methodList, fieldList): return @JOverride def defineField(self, context, cls, name, field, fieldType, modifiers): return self.define(name) @JOverride def defineMethod(self, context, cls, name, method, overloadList, modifiers): return self.define(name) @JOverride def populateMethod(self, context, method, returnType, argumentTypes): return @JOverride def defineMethodDispatch(self, context, cls, name, overloadList, modifiers): return self.define(name) @JOverride def destroy(self, context, resources, sz): for i in range(sz): del self.entities[resources[i]] manager = TypeManager() factory = TF() manager = TypeManager(62621463, factory) manager.init() # Can only be initialized once with self.assertRaises(JException): manager.init() self.assertEqual( factory.entities[manager.findClassByName('boolean')], 'boolean') self.assertEqual( factory.entities[manager.findClassByName('byte')], 'byte') self.assertEqual( factory.entities[manager.findClassByName('char')], 'char') self.assertEqual( factory.entities[manager.findClassByName('short')], 'short') self.assertEqual( factory.entities[manager.findClassByName('int')], 'int') self.assertEqual( factory.entities[manager.findClassByName('long')], 'long') self.assertEqual( factory.entities[manager.findClassByName('float')], 'float') self.assertEqual( factory.entities[manager.findClassByName('double')], 'double') self.assertEqual( factory.entities[manager.findClass(JBoolean)], "boolean") self.assertEqual(factory.entities[manager.findClass(JChar)], "char") self.assertEqual(factory.entities[manager.findClass(JByte)], "byte") self.assertEqual(factory.entities[manager.findClass(JShort)], "short") self.assertEqual(factory.entities[manager.findClass(JInt)], "int") self.assertEqual(factory.entities[manager.findClass(JLong)], "long") self.assertEqual(factory.entities[manager.findClass(JFloat)], "float") self.assertEqual( factory.entities[manager.findClass(JDouble)], "double") self.assertEqual(manager.populateMethod(0, None), None) sb = JClass("java.lang.StringBuilder") with self.assertRaises(JException): manager.populateMembers(sb) manager.shutdown() # See if we leaked any entities self.assertEqual(len(factory.entities), 0) jpype-1.6.0/test/jpypetest/test_javadoc.py000066400000000000000000000035021501674766700207160ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * from jpype import java import common class HtmlTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testEntities(self): html = JClass("org.jpype.html.Html") for k, v in html.ENTITIES.items(): u = html.decode("&" + str(k) + ";") self.assertIsInstance(u, JString) self.assertEqual(len(u), 1) self.assertEqual(ord(u[0][0]), v) def testClass(self): JC = jpype.JClass("jpype.doc.Test") jd = JC.__doc__ self.assertIsInstance(jd, str) # Disabled this test for now. Java needs a better API for accessing Java doc. # It is hard to deal with random changes every version. #self.assertRegex(jd, "random stuff") def testMethod(self): JC = jpype.JClass("jpype.doc.Test") jd = JC.methodOne.__doc__ self.assertIsInstance(jd, str) # Disabling this test for now. Something fails in Linux but I can't replicate it. #self.assertRegex(jd, "something special") jpype-1.6.0/test/jpypetest/test_jboolean.py000066400000000000000000000126331501674766700211050ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * import sys import logging import time import common try: import numpy as np except ImportError: pass class JBooleanTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.Test = jpype.JClass("jpype.common.Fixture")() @common.requireInstrumentation def testJPBoolean_str(self): jb = JBoolean(True) _jpype.fault("PyJPBoolean_str") with self.assertRaisesRegex(SystemError, "fault"): str(jb) _jpype.fault("PyJPModule_getContext") str(jb) @common.requireInstrumentation def testJPBooleanType(self): ja = JArray(JBoolean)(5) # lgtm [py/similar-function] _jpype.fault("JPBooleanType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_bool_field = object() with self.assertRaises(TypeError): jf().bool_field = object() @common.requireInstrumentation def testJBooleanGetJavaConversion(self): _jpype.fault("JPBooleanType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JBoolean._canConvertToJava(object()) def testBooleanFromInt(self): self.assertEqual(self.Test.callBoolean(int(123)), True) self.assertEqual(self.Test.callBoolean(int(0)), False) @common.requireNumpy def testBooleanFromNPInt(self): import numpy as np self.assertEqual(self.Test.callBoolean(np.int_(123)), True) @common.requireNumpy def testBooleanFromNPInt8(self): import numpy as np self.assertEqual(self.Test.callBoolean(np.int8(123)), True) self.assertEqual(self.Test.callBoolean(np.uint8(123)), True) @common.requireNumpy def testBooleanFromNPInt16(self): import numpy as np self.assertEqual(self.Test.callBoolean(np.int16(123)), True) self.assertEqual(self.Test.callBoolean(np.uint16(123)), True) @common.requireNumpy def testBooleanFromNPInt32(self): import numpy as np self.assertEqual(self.Test.callBoolean(np.int32(123)), True) self.assertEqual(self.Test.callBoolean(np.uint32(123)), True) @common.requireNumpy def testBooleanFromNPInt64(self): import numpy as np self.assertEqual(self.Test.callBoolean(np.int64(123)), True) self.assertEqual(self.Test.callBoolean(np.uint64(123)), True) def testBooleanFromFloat(self): with self.assertRaises(TypeError): self.Test.callBoolean(float(2)) @common.requireNumpy def testBooleanFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): self.Test.callBoolean(np.float16(2)) @common.requireNumpy def testBooleanFromNPFloat32(self): import numpy as np with self.assertRaises(TypeError): self.Test.callBoolean(np.float32(2)) @common.requireNumpy def testBooleanFromNPFloat64(self): import numpy as np with self.assertRaises(TypeError): self.Test.callBoolean(np.float64(2)) def testBooleanFromNone(self): with self.assertRaises(TypeError): self.Test.callBoolean(None) def testJArrayConversionBool(self): expected = [True, False, False, True] jarr = jpype.JArray(jpype.JBoolean)(expected) self.assertEqual(expected, list(jarr[:])) @common.requireNumpy def testSetFromNPBoolArray(self): import numpy as np n = 100 a = np.random.randint(0, 1, size=n, dtype=np.bool_) jarr = jpype.JArray(jpype.JBoolean)(n) jarr[:] = a self.assertCountEqual(a, jarr) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JBoolean)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __bool__(self): raise SystemError("nope") ja = JArray(JBoolean)(5) a = [1, 2, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JBoolean)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a jpype-1.6.0/test/jpypetest/test_jbyte.py000066400000000000000000000166471501674766700204420ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * from jpype import java import sys import logging import time import common try: import numpy as np except ImportError: pass class JByteTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.fixture = jpype.JClass("jpype.common.Fixture")() @common.requireInstrumentation def testConversionFault(self): _jpype.fault("JPByteType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JByte._canConvertToJava(object()) @common.requireInstrumentation def testArrayFaults(self): ja = JArray(JByte)(5) _jpype.fault("JPByteType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] _jpype.fault("JPJavaFrame::NewByteArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JByte)(1) _jpype.fault("JPJavaFrame::SetByteArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetByteArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetByteArrayElements") with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseByteArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseByteArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseByteArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() def testByteFromInt(self): self.assertEqual(self.fixture.callByte(int(123)), 123) @common.requireNumpy def testByteFromNPInt(self): import numpy as np self.assertEqual(self.fixture.callByte(np.int_(123)), 123) @common.requireNumpy def testByteFromNPInt8(self): import numpy as np self.assertEqual(self.fixture.callByte(np.int8(123)), 123) self.assertEqual(self.fixture.callByte(np.uint8(123)), 123) @common.requireNumpy def testByteFromNPInt16(self): import numpy as np self.assertEqual(self.fixture.callByte(np.int16(123)), 123) self.assertEqual(self.fixture.callByte(np.uint16(123)), 123) @common.requireNumpy def testByteFromNPInt32(self): import numpy as np self.assertEqual(self.fixture.callByte(np.int32(123)), 123) self.assertEqual(self.fixture.callByte(np.uint32(123)), 123) @common.requireNumpy def testByteFromNPInt64(self): import numpy as np self.assertEqual(self.fixture.callByte(np.int64(123)), 123) self.assertEqual(self.fixture.callByte(np.uint64(123)), 123) def testByteFromFloat(self): with self.assertRaises(TypeError): self.fixture.callByte(float(2)) @common.requireNumpy def testByteFromNPFloat16(self): import numpy as np with self.assertRaises(TypeError): self.fixture.callByte(np.float16(2)) @common.requireNumpy def testByteFromNPFloat32(self): import numpy as np with self.assertRaises(TypeError): self.fixture.callByte(np.float32(2)) @common.requireNumpy def testByteFromNPFloat64(self): import numpy as np with self.assertRaises(TypeError): self.fixture.callByte(np.float64(2)) def testByteRange(self): with self.assertRaises(OverflowError): self.fixture.callByte(int(1e10)) with self.assertRaises(OverflowError): self.fixture.callByte(int(-1e10)) def testExplicitRange(self): # These will not overflow as they are explicit casts self.assertEqual(JByte(2**8), 0) self.assertEqual(JByte(-2**8), 0) def testByteFromNone(self): with self.assertRaises(TypeError): self.fixture.callByte(None) def testByteArrayAsString(self): t = JClass("jpype.array.TestArray")() v = t.getByteArray() self.assertEqual(str(v), 'avcd') def testByteArrayIntoVector(self): ba = jpype.JArray(jpype.JByte)(b'123') v = jpype.java.util.Vector(1) v.add(ba) self.assertEqual(len(v), 1) self.assertNotEqual(v[0], None) def testByteArraySimple(self): a = JArray(JByte)(2) a[1] = 2 self.assertEqual(a[1], 2) def testJArrayConversionByte(self): expected = (0, 1, 2, 3) ByteBuffer = jpype.java.nio.ByteBuffer bb = ByteBuffer.allocate(4) buf = bb.array() for i in range(len(expected)): buf[i] = expected[i] for i in range(len(expected)): self.assertEqual(expected[i], buf[i]) def testFromObject(self): ja = JArray(JByte)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_byte_field = object() with self.assertRaises(TypeError): jf().byte_field = object() def testArrayHash(self): ja = JArray(JByte)([1, 2, 3]) self.assertIsInstance(hash(ja), int) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JByte)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __int__(self): raise SystemError("nope") def __index__(self): raise SystemError("nope") ja = JArray(JByte)(5) a = [1, -1, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JByte)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a def testArraySetRange(self): ja = JArray(JByte)(3) ja[0:1] = [123] self.assertEqual(ja[0], 123) ja[0:1] = [-1] self.assertEqual(ja[0], -1) with self.assertRaises(TypeError): ja[0:1] = [1.000] with self.assertRaises(TypeError): ja[0:1] = [java.lang.Double(321)] with self.assertRaises(TypeError): ja[0:1] = [object()] jpype-1.6.0/test/jpypetest/test_jchar.py000066400000000000000000000361761501674766700204130ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype import common from jpype.types import * class JChar2TestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testCharRange(self): self.assertEqual(ord(str(jpype.JChar(65))), 65) self.assertEqual(ord(str(jpype.JChar(512))), 512) def testCharFromStr(self): self.assertEqual(ord(jpype.JChar(chr(65))), 65) self.assertEqual(ord(jpype.JChar(chr(128))), 128) self.assertEqual(ord(jpype.JChar(chr(255))), 255) self.assertEqual(ord(jpype.JChar(chr(10255))), 10255) def testCharCastFloat(self): self.assertEqual(ord(jpype.JChar(65.0)), 65) @common.requireInstrumentation def testJPChar_new(self): _jpype.fault("PyJPChar_new") with self.assertRaisesRegex(SystemError, "fault"): JChar("a") _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): JChar("a") JChar("a") @common.requireInstrumentation def testJPChar_str(self): jc = JChar("a") _jpype.fault("PyJPChar_str") with self.assertRaisesRegex(SystemError, "fault"): str(jc) @common.requireInstrumentation def testJCharGetJavaConversion(self): _jpype.fault("JPCharType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JChar._canConvertToJava(object()) @common.requireInstrumentation def testJPJavaFrameCharArray(self): ja = JArray(JChar)(5) _jpype.fault("JPJavaFrame::NewCharArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JChar)(1) _jpype.fault("JPJavaFrame::SetCharArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetCharArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetCharArrayElements") # Special case, only BufferError is allowed from getBuffer with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseCharArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) # Not sure why this one changed. _jpype.fault("JPJavaFrame::ReleaseCharArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseCharArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() ja = JArray(JChar)(5) # lgtm [py/similar-function] _jpype.fault("JPCharType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] def testFromObject(self): ja = JArray(JChar)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_char_field = object() with self.assertRaises(TypeError): jf().char_field = object() def testCharArrayAsString(self): t = JClass("jpype.array.TestArray")() v = t.getCharArray() self.assertEqual(str(v), 'avcd') def testArrayConversionChar(self): t = JClass("jpype.array.TestArray")() v = t.getCharArray() self.assertEqual(str(v[:]), 'avcd') def testArrayEqualsChar(self): contents = "abc" array = jpype.JArray(jpype.JChar)(contents) array2 = jpype.JArray(jpype.JChar)(contents) self.assertEqual(array, array) self.assertNotEqual(array, array2) self.assertEqual(array, "abc") def testArrayHash(self): ja = JArray(JByte)([1, 2, 3]) self.assertIsInstance(hash(ja), int) def testNone(self): self.assertEqual(JChar._canConvertToJava(None), "none") class JCharTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.nc = JChar('B') def testStr(self): self.assertEqual(type(str(self.nc)), str) self.assertEqual(str(self.nc), 'B') def testRepr(self): self.assertEqual(type(repr(self.nc)), str) self.assertEqual(repr(self.nc), "'B'") def testAbs(self): self.assertEqual(abs(self.nc), self.nc) def testOrd(self): self.assertEqual(ord(self.nc), 66) def testInt(self): self.assertEqual(int(self.nc), 66) def testFloat(self): self.assertEqual(float(self.nc), 66.0) def testLen(self): self.assertEqual(len(self.nc), 1) def testHash(self): self.assertEqual(hash(self.nc), hash('B')) def testAdd(self): self.assertEqual(self.nc + 1, 67) self.assertIsInstance(self.nc + 1, int) self.assertEqual(self.nc + 1.1, 67.1) self.assertIsInstance(self.nc + 1.1, float) def testSub(self): self.assertEqual(self.nc - 1, 65) self.assertIsInstance(self.nc - 1, int) self.assertEqual(self.nc - 1.1, 64.9) self.assertIsInstance(self.nc - 1.1, float) def testMult(self): self.assertEqual(self.nc * 2, 132) self.assertIsInstance(self.nc * 2, int) self.assertEqual(self.nc * 0.25, 16.5) self.assertIsInstance(self.nc * 2.0, float) def testRshift(self): self.assertEqual(self.nc >> 1, 33) self.assertIsInstance(self.nc >> 2, int) def testLshift(self): self.assertEqual(self.nc << 1, 132) self.assertIsInstance(self.nc << 2, int) def testAnd(self): self.assertEqual(self.nc & 244, 66 & 244) self.assertIsInstance(self.nc & 2, int) def testOr(self): self.assertEqual(self.nc | 40, 66 | 40) self.assertIsInstance(self.nc | 2, int) def testXor(self): self.assertEqual(self.nc ^ 1, 66 ^ 1) self.assertIsInstance(self.nc ^ 1, int) def testPass(self): fixture = jpype.JClass('jpype.common.Fixture')() self.assertEqual(type(fixture.callChar(self.nc)), JChar) self.assertEqual(type(fixture.callObject(self.nc)), jpype.java.lang.Character) def check(self, u, v0, v1, v2): self.assertEqual(v1, u) self.assertEqual(u, v1) self.assertNotEqual(v0, u) self.assertNotEqual(u, v0) self.assertNotEqual(v2, u) self.assertNotEqual(u, v2) self.assertTrue(u > v0) self.assertFalse(u > v2) self.assertTrue(u < v2) self.assertFalse(u < v0) self.assertTrue(v0 < u) self.assertFalse(v2 < u) self.assertTrue(v2 > u) self.assertFalse(v0 > u) self.assertTrue(u >= v1) self.assertFalse(u >= v2) self.assertTrue(v1 <= u) self.assertFalse(v2 <= u) def testCompareInt(self): self.check(self.nc, 65, 66, 67) def testCompareFloat(self): self.check(self.nc, 65.0, 66.0, 67.0) def testCompareJInt(self): self.check(self.nc, JInt(65), JInt(66), JInt(67)) def testCompareJFloat(self): self.check(self.nc, JFloat(65.0), JFloat(66.0), JFloat(67.0)) class JCharBoxedTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.nc = jpype.java.lang.Character('B') def testStr(self): self.assertEqual(type(str(self.nc)), str) self.assertEqual(str(self.nc), 'B') def testRepr(self): self.assertEqual(type(repr(self.nc)), str) self.assertEqual(repr(self.nc), "'B'") def testOrd(self): self.assertEqual(ord(self.nc), 66) def testInt(self): self.assertEqual(int(self.nc), 66) def testFloat(self): self.assertEqual(float(self.nc), 66.0) def testLen(self): self.assertEqual(len(self.nc), 1) def testHash(self): self.assertEqual(hash(self.nc), hash('B')) def testAdd(self): self.assertEqual(self.nc + 1, 67) self.assertIsInstance(self.nc + 1, int) self.assertEqual(self.nc + 1.1, 67.1) self.assertIsInstance(self.nc + 1.1, float) def testSub(self): self.assertEqual(self.nc - 1, 65) self.assertIsInstance(self.nc - 1, int) self.assertEqual(self.nc - 1.1, 64.9) self.assertIsInstance(self.nc - 1.1, float) def testMult(self): self.assertEqual(self.nc * 2, 132) self.assertIsInstance(self.nc * 2, int) self.assertEqual(self.nc * 0.25, 16.5) self.assertIsInstance(self.nc * 2.0, float) def testRshift(self): self.assertEqual(self.nc >> 1, 33) self.assertIsInstance(self.nc >> 2, int) def testLshift(self): self.assertEqual(self.nc << 1, 132) self.assertIsInstance(self.nc << 2, int) def testAnd(self): self.assertEqual(self.nc & 244, 66 & 244) self.assertIsInstance(self.nc & 2, int) def testOr(self): self.assertEqual(self.nc | 40, 66 | 40) self.assertIsInstance(self.nc | 2, int) def testXor(self): self.assertEqual(self.nc ^ 1, 66 ^ 1) self.assertIsInstance(self.nc ^ 1, int) def testFloorDiv(self): self.assertEqual(self.nc // 3, 66 // 3) self.assertEqual(3 // self.nc, 3 // 66) def testDivMod(self): self.assertEqual(divmod(self.nc, 3), divmod(66, 3)) self.assertEqual(divmod(3, self.nc), divmod(3, 66)) def testInv(self): self.assertEqual(~self.nc, ~66) def testPos(self): self.assertEqual(+self.nc, +66) def testNeg(self): self.assertEqual(-self.nc, -66) def testBool(self): self.assertTrue(bool(self.nc)) self.assertFalse(bool(JChar(0))) def testPass(self): fixture = jpype.JClass('jpype.common.Fixture')() self.assertEqual(type(fixture.callObject(self.nc)), type(self.nc)) def check(self, u, v0, v1, v2): self.assertEqual(v1, u) self.assertEqual(u, v1) self.assertNotEqual(v0, u) self.assertNotEqual(u, v0) self.assertNotEqual(v2, u) self.assertNotEqual(u, v2) self.assertTrue(u > v0) self.assertFalse(u > v2) self.assertTrue(u < v2) self.assertFalse(u < v0) self.assertTrue(v0 < u) self.assertFalse(v2 < u) self.assertTrue(v2 > u) self.assertFalse(v0 > u) self.assertTrue(u >= v1) self.assertFalse(u >= v2) self.assertTrue(v1 <= u) self.assertFalse(v2 <= u) def testCompareInt(self): self.check(self.nc, 65, 66, 67) def testCompareFloat(self): self.check(self.nc, 65.0, 66.0, 67.0) def testCompareJInt(self): self.check(self.nc, JInt(65), JInt(66), JInt(67)) def testCompareJFloat(self): self.check(self.nc, JFloat(65.0), JFloat(66.0), JFloat(67.0)) class JCharBoxedNullTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.nc = jpype.JObject(None, jpype.java.lang.Character) def testStr(self): self.assertEqual(type(str(self.nc)), str) self.assertEqual(str(self.nc), 'None') def testRepr(self): self.assertEqual(type(repr(self.nc)), str) self.assertEqual(repr(self.nc), 'None') def testInt(self): with self.assertRaises(TypeError): int(self.nc) def testFloat(self): with self.assertRaises(TypeError): float(self.nc) def testLen(self): with self.assertRaises(TypeError): len(self.nc) def testHash(self): self.assertEqual(hash(self.nc), hash(None)) def testAdd(self): with self.assertRaises(TypeError): self.nc + 1 with self.assertRaises(TypeError): 1 + self.nc def testSub(self): with self.assertRaises(TypeError): self.nc - 1 with self.assertRaises(TypeError): 1 - self.nc def testMult(self): with self.assertRaises(TypeError): self.nc * 1 with self.assertRaises(TypeError): 1 * self.nc def testRshift(self): with self.assertRaises(TypeError): self.nc >> 1 with self.assertRaises(TypeError): 1 >> self.nc def testLshift(self): with self.assertRaises(TypeError): self.nc << 1 with self.assertRaises(TypeError): 1 << self.nc def testAnd(self): with self.assertRaises(TypeError): self.nc & 1 with self.assertRaises(TypeError): 1 & self.nc def testOr(self): with self.assertRaises(TypeError): self.nc | 1 with self.assertRaises(TypeError): 1 | self.nc def testXor(self): with self.assertRaises(TypeError): self.nc ^ 1 with self.assertRaises(TypeError): 1 ^ self.nc def testFloorDiv(self): with self.assertRaises(TypeError): self.nc // 1 with self.assertRaises(TypeError): 1 // self.nc def testDivMod(self): with self.assertRaises(TypeError): divmod(self.nc, 1) with self.assertRaises(TypeError): divmod(1, self.nc) def testInv(self): with self.assertRaises(TypeError): ~self.nc def testPos(self): with self.assertRaises(TypeError): +self.nc def testNeg(self): with self.assertRaises(TypeError): -self.nc def testBool(self): self.assertFalse(bool(self.nc)) def testEq(self): self.assertTrue(self.nc == self.nc) self.assertFalse(self.nc != self.nc) def testPass(self): fixture = jpype.JClass('jpype.common.Fixture')() self.assertEqual(fixture.callObject(self.nc), None) class JCharTestCase_2(common.JPypeTestCase): def testOps(self): self.assertEqual(JChar("x") + "test", "xtest") self.assertEqual("test" + JChar("x"), "testx") self.assertEqual(JChar("x") + 1, 121) self.assertEqual(1 + JChar("x"), 121) self.assertEqual(JChar(1) + JChar("x"), 121) self.assertEqual(JChar("x") - 1, 119) self.assertEqual(1 - JChar("x"), -119) self.assertEqual(JChar("x") - JChar(1), 119) self.assertEqual(JChar("x") * 1, 120) self.assertEqual(1 * JChar("x"), 120) self.assertEqual(JChar("x") & 1, 0) self.assertEqual(1 & JChar("x"), 0) self.assertEqual(JChar("x") | 1, 121) self.assertEqual(1 | JChar("x"), 121) jpype-1.6.0/test/jpypetest/test_jclass.py000066400000000000000000000236221501674766700205730ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * import common class JClassTestCase(common.JPypeTestCase): """ Test for methods of JClass Should test: - ``__getattribute__`` against methods, fields, python methods, and python properties - ``__setattr__`` against fields, final fields, python private fields - ``class_`` property - ``mro`` """ def setUp(self): common.JPypeTestCase.setUp(self) def testGetAttrPyMethod(self): cls = JClass('java.util.Iterator') obj = JClass('java.util.ArrayList')() obj.add(123) # Static python methods should be the same when accessed as classes or objects self.assertEqual(obj.iterator().next(), cls.next(obj.iterator())) def testGetAttrStaticMethod(self): cls = JClass('java.lang.Long') obj = cls(10) # Static java methods should be the same when accessed as classes or objects self.assertEqual(cls.bitCount(123), obj.bitCount(123)) def testGetAttrStaticField(self): cls = JClass('java.lang.String') obj = cls("foo") # Static fields should be the same when accessed as classes or objects self.assertEqual(cls.CASE_INSENSITIVE_ORDER, obj.CASE_INSENSITIVE_ORDER) def testSetAttrPythonField(self): cls = JClass('java.lang.String') # Setting a private field on a Java class is allowed cls._allowed = 1 with self.assertRaises(AttributeError): # Setting a public field on a Java class is forbidden cls.forbidden = 1 def testSetAttrFinal(self): cls = JClass('java.lang.Long') with self.assertRaises(AttributeError): # Setting a final field is forbidden cls.SIZE = 1 def testClass(self): cls = JClass('java.lang.Long') clsType = JClass('java.lang.Class') # Get class must return a java.lang.Class instance belonging to the class self.assertIsInstance(cls.class_, clsType) self.assertEqual(cls.class_.getSimpleName(), "Long") def testGetAttrProperty(self): cls = JClass('java.lang.RuntimeException') with self.assertRaises(AttributeError): value = cls.args def testSetAttrProperty(self): cls = JClass('java.lang.RuntimeException') with self.assertRaises(AttributeError): cls.args = 1 def testGetAttrStaticField_2(self): cls = JClass('jpype.common.Fixture') cls.static_object_field = "fred" self.assertEqual(cls.static_object_field, "fred") def testSetAttrStaticField_3(self): cls = JClass('jpype.common.Fixture') cls.static_object_field = "fred" def testGetAttrField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): v = cls.object_field def testSetAttrField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.object_field = "fred" def testGetAttrPrivateField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): v = cls.privateObjectField def testSetAttrPrivateField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.private_object_field = "fred" def testGetAttrFinalField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): v = cls.final_object_field def testSetAttrFinalField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.final_object_field = "fred" def testGetAttrStaticFinalField(self): cls = JClass('jpype.common.Fixture') self.assertEqual(cls.final_static_object_field, "final static object field") def testSetAttrStaticFinalField(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.final_static_object_field = "bar" def testStaticMethod(self): cls = JClass('jpype.common.Fixture') cls.callStaticObject(JObject()) def testPrivateStaticMethod(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.callPrivateStaticObject(JObject()) def testMethod(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(TypeError): cls.callObject(JObject()) cls.callObject(cls(), JObject()) def testPrivateMethod(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.callPrivateObject(JObject()) def testProtectedMethod(self): cls = JClass('jpype.common.Fixture') with self.assertRaises(AttributeError): cls.callProtectedObject(JObject()) def testJClassFail(self): with self.assertRaises(TypeError): cls = JClass("asdw.gqyr.jhnw") def testGetClassFromClass(self): cls = JClass('java.lang.Class') self.assertIsInstance(cls.class_, cls) def testGetClassFromInterface(self): intr = JClass('java.io.Serializable') cls = JClass('java.lang.Class') self.assertIsInstance(intr.class_, cls) def testInterfaceCtor(self): intr = JClass('java.io.Serializable') with self.assertRaises(TypeError): intr() def testJClassWithLoader(self): cl = JClass('java.lang.Class').class_.getClassLoader() self.assertIsInstance( JClass('java.lang.StringBuilder', loader=cl), JClass) @common.requireInstrumentation def testJavaConversionFault(self): _jpype.fault("JPClass::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): print(jpype.java.lang.Class._canConvertToJava(None)) def testJavaConversion(self): a = JString("a") self.assertEqual( jpype.java.lang.Class._canConvertToJava(object()), "none") self.assertEqual( jpype.java.lang.Class._canConvertToJava(a.getClass()), "exact") self.assertEqual( jpype.java.lang.Class._canConvertToJava(JString), "exact") def testInnerClass(self): # This tests for problems when the inner class implements the # outer interface which creates a race condition. Success is # not throwing an exception test = JClass("jpype.types.InnerTest")() test.test() def testLookupGeneric(self): self.assertEqual(JClass('java.util.ArrayList<>'), JClass("java.util.ArrayList")) def testLookupJNI(self): self.assertEqual(JClass('java/lang/Object'), JClass("java.lang.Object")) def testLookupArray(self): self.assertEqual(JClass('int[]'), JArray(JInt)) self.assertEqual(JClass('int[][]'), JArray(JInt, 2)) self.assertEqual(JClass('int[][][]'), JArray(JInt, 3)) self.assertEqual(JClass('java.lang.Object[][][]'), JArray(JObject, 3)) def testLookupArrayJNI(self): self.assertEqual(JClass('[I'), JArray(JInt)) self.assertEqual(JClass('[[J'), JArray(JLong, 2)) self.assertEqual(JClass('[Ljava.lang.Object;'), JArray(JObject)) self.assertEqual(JClass('[Ljava/lang/Object;'), JArray(JObject)) def testClosed(self): with self.assertRaises(TypeError): class Q(JBoolean): pass with self.assertRaises(TypeError): class Q(JChar): pass with self.assertRaises(TypeError): class Q(JByte): pass with self.assertRaises(TypeError): class Q(JShort): pass with self.assertRaises(TypeError): class Q(JInt): pass with self.assertRaises(TypeError): class Q(JLong): pass with self.assertRaises(TypeError): class Q(JFloat): pass with self.assertRaises(TypeError): class Q(JDouble): pass def testAsArray(self): jo = JClass('java.lang.Object') self.assertEqual(jo[:], JArray(jo)) self.assertEqual(jo[:][:], JArray(jo, 2)) self.assertIsInstance(jo[5], JArray(jo)) self.assertEqual(len(jo[5]), 5) self.assertIsInstance(jo[5, :], JArray(jo, 2)) self.assertEqual(len(jo[5, :]), 5) with self.assertRaises(TypeError): self.assertEqual(jo[:, 5], JArray(jo, 2)) with self.assertRaises(TypeError): jo['foo'] def testCastEqual(self): jo = JClass('java.lang.Object') with self.assertRaises(TypeError): jo @= 5 def testCanCast(self): String = JClass("java.lang.String") self.assertFalse(String._canCast(1)) self.assertTrue(String._canCast("foo")) def testUnsatisfied(self): # This is testing what happens if a class fails to load properly due to # failures. A partial loaded class can lead to crashes. with self.assertRaises(JClass("java.lang.NoClassDefFoundError")): JClass("org.jpype.unsatisfied.TestClass") jpype-1.6.0/test/jpypetest/test_jdouble.py000066400000000000000000000363531501674766700207450ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from unittest.util import safe_repr import jpype import common import random import _jpype from jpype import java from jpype.types import * try: import numpy as np except ImportError: pass class JDoubleTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.value = 1.0 + 1.0 / 65536 self.cls = JClass("jpype.common.Fixture") self.fixture = self.cls() def compareDoubleEqual(self, x, y, msg=None): if x == y: return if x < 0: x = -x if y < 0: y = -y a = (x + y) / 2 b = (x - y) if b < 0: b = -b if b < a * 1e-14: return msg = self._formatMessage(msg, '%s == %s' % (safe_repr(x), safe_repr(y))) raise self.failureException(msg) @common.requireInstrumentation def testJPNumberFloat_int(self): jd = JDouble(1) _jpype.fault("PyJPNumberFloat_int") with self.assertRaisesRegex(SystemError, "fault"): int(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): int(jd) int(jd) @common.requireInstrumentation def testJPNumberFloat_float(self): jd = JDouble(1) _jpype.fault("PyJPNumberFloat_float") with self.assertRaisesRegex(SystemError, "fault"): float(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): float(jd) float(jd) @common.requireInstrumentation def testJPNumberFloat_str(self): jd = JDouble(1) _jpype.fault("PyJPNumberFloat_str") with self.assertRaisesRegex(SystemError, "fault"): str(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(jd) str(jd) @common.requireInstrumentation def testJPNumberFloat_repr(self): jd = JDouble(1) _jpype.fault("PyJPNumberFloat_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) repr(jd) @common.requireInstrumentation def testJPNumberFloat_compare(self): jd = JDouble(1) _jpype.fault("PyJPNumberFloat_compare") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 jd == 1 @common.requireInstrumentation def testJPNumberFloat_hash(self): jd = JDouble(1) _jpype.fault("PyJPNumberFloat_hash") with self.assertRaises(SystemError): hash(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(jd) hash(jd) @common.requireInstrumentation def testFault(self): _jpype.fault("JPDoubleType::findJavaConversion") with self.assertRaises(SystemError): JDouble(1.0) @common.requireInstrumentation def testConversionFault(self): _jpype.fault("JPDoubleType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JDouble._canConvertToJava(object()) @common.requireInstrumentation def testArrayFault(self): ja = JArray(JDouble)(5) _jpype.fault("JPJavaFrame::NewDoubleArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JDouble)(1) _jpype.fault("JPJavaFrame::SetDoubleArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetDoubleArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetDoubleArrayElements") # Special case, only BufferError is allowed from getBuffer with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseDoubleArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseDoubleArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseDoubleArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() _jpype.fault("JPDoubleType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] def testFromJIntWiden(self): self.assertEqual(JDouble(JByte(123)), 123) self.assertEqual(JDouble(JShort(12345)), 12345) self.assertEqual(JDouble(JInt(123456789)), 123456789) self.assertEqual(JDouble(JLong(123456789)), 123456789) def testFromJFloatWiden(self): self.assertEqual(JDouble(JFloat(12345678)), 12345678) def testFromNone(self): with self.assertRaises(TypeError): JDouble(None) self.assertEqual(JDouble._canConvertToJava(None), "none") def testFromJDouble(self): self.assertEqual(JDouble(JDouble(1.2345)), 1.2345) def testUnBox(self): self.assertEqual(JDouble(java.lang.Double(1.2345)), 1.2345) def testFromFloat(self): self.assertEqual(JDouble(1.2345), 1.2345) self.assertEqual(JDouble._canConvertToJava(1.2345), "exact") def testFromLong(self): self.assertEqual(JDouble(12345), 12345) self.assertEqual(JDouble._canConvertToJava(12345), "implicit") def testFromObject(self): with self.assertRaises(TypeError): JDouble(object()) with self.assertRaises(TypeError): JDouble(JObject()) with self.assertRaises(TypeError): JDouble(JString("A")) self.assertEqual(JDouble._canConvertToJava(object()), "none") ja = JArray(JDouble)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_double_field = object() with self.assertRaises(TypeError): jf().double_field = object() def testCallDoubleFromNone(self): with self.assertRaises(TypeError): self.fixture.callDouble(None) with self.assertRaises(TypeError): self.fixture.static_double_field = None with self.assertRaises(TypeError): self.fixture.double_field = None def testThrow(self): with self.assertRaises(JException): self.fixture.throwDouble() with self.assertRaises(JException): self.cls.throwStaticDouble() with self.assertRaises(JException): self.fixture.throwStaticDouble() def checkType(self, q): # Check field self.fixture.double_field = q self.assertEqual(self.fixture.double_field, q) self.assertEqual(self.fixture.getDouble(), q) # Check static field self.cls.static_double_field = q self.assertEqual(self.fixture.static_double_field, q) self.assertEqual(self.fixture.getStaticDouble(), q) self.assertEqual(self.cls.getStaticDouble(), q) # Check call self.assertEqual(self.fixture.callDouble(q), q) self.assertEqual(self.cls.callStaticDouble(q), q) def testCheckInt(self): self.checkType(1) def testCheckFloat(self): self.checkType(2.0) def testCheckRange(self): self.checkType(float(1e340)) self.checkType(float(-1e340)) def testCheckNaN(self): import math nan = float("nan") self.assertTrue(math.isnan(self.fixture.callDouble(nan))) self.fixture.static_double_field = nan self.assertTrue(math.isnan(self.fixture.static_double_field)) self.fixture.double_field = nan self.assertTrue(math.isnan(self.fixture.double_field)) def testCheckInf(self): import math inf = float("inf") self.assertTrue(math.isinf(self.fixture.callDouble(inf))) self.fixture.static_double_field = inf self.assertTrue(math.isinf(self.fixture.static_double_field)) self.fixture.double_field = inf self.assertTrue(math.isinf(self.fixture.double_field)) def testCheckBool(self): self.checkType(True) self.checkType(False) def testCheckJBoolean(self): # FIXME fails # self.checkType(JBoolean(True)) # self.checkType(JBoolean(False)) pass def testCheckJChar(self): self.checkType(JChar("A")) def testCheckJByte(self): self.checkType(JByte(-128)) self.checkType(JByte(127)) def testCheckJShort(self): self.checkType(JShort(-2**15)) self.checkType(JShort(2**15 - 1)) def testCheckJInt(self): self.checkType(JInt(-2**31 + 1)) self.checkType(JInt(2**31 - 1)) def testCheckJLong(self): with self.useEqualityFunc(self.compareDoubleEqual): self.checkType(JLong(-2**63 + 1)) self.checkType(JLong(2**63 - 1)) def testCheckJFloat(self): self.checkType(JFloat(1.515313)) def testCheckDouble(self): self.checkType(JDouble(11.85193)) @common.requireNumpy def testCheckNumpyInt8(self): self.checkType(np.random.randint(-127, 128, dtype=np.int8)) self.checkType(np.random.randint(0, 255, dtype=np.uint8)) self.checkType(np.uint8(0)) self.checkType(np.uint8(255)) self.checkType(np.int8(-128)) self.checkType(np.int8(127)) @common.requireNumpy def testCheckNumpyInt16(self): self.checkType(np.random.randint(-2**15, 2**15 - 1, dtype=np.int16)) self.checkType(np.random.randint(0, 2**16 - 1, dtype=np.uint16)) self.checkType(np.uint16(0)) self.checkType(np.uint16(2**16 - 1)) self.checkType(np.int16(-2**15)) self.checkType(np.int16(2**15 - 1)) @common.requireNumpy def testCheckNumpyInt32(self): self.checkType(np.random.randint(-2**31, 2**31 - 1, dtype=np.int32)) self.checkType(np.random.randint(0, 2**32 - 1, dtype=np.uint32)) self.checkType(np.uint32(0)) self.checkType(np.uint32(2**32 - 1)) self.checkType(np.int32(-2**31)) self.checkType(np.int32(2**31 - 1)) @common.requireNumpy def testCheckNumpyInt64(self): self.checkType(np.random.randint(-2**63, 2**63 - 1, dtype=np.int64)) self.checkType( np.uint64(np.random.randint(0, 2**64 - 1, dtype=np.uint64))) self.checkType(np.uint64(0)) self.checkType(np.uint64(2**64 - 1)) self.checkType(np.int64(-2**63)) self.checkType(np.int64(2**63 - 1)) @common.requireNumpy def testCheckNumpyFloat32(self): self.checkType(np.float32(np.random.rand())) @common.requireNumpy def testCheckNumpyFloat64(self): self.checkType(np.float64(np.random.rand())) def testArrayConversionDouble(self): VALUES = [float(random.random()) for i in range(10)] jarr = JArray(JDouble)(VALUES) self.assertElementsAlmostEqual(VALUES, jarr) result = jarr[:] self.assertElementsAlmostEqual(VALUES, result) result = jarr[2:10] self.assertEqual(len(VALUES[2:10]), len(result)) self.assertElementsAlmostEqual(VALUES[2:10], result) # empty slice result = jarr[-1:3] expected = VALUES[-1:3] self.assertElementsAlmostEqual(expected, result) result = jarr[3:-2] expected = VALUES[3:-2] self.assertElementsEqual(expected, result) @common.requireNumpy def testArraySetFromNPDouble(self): a = np.random.random(100).astype(np.float64) jarr = JArray(JDouble)(100) jarr[:] = a self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy def testArrayInitFromNPFloat16(self): a = np.random.random(100).astype(np.float16) jarr = JArray(JDouble)(a) self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy def testArrayInitFromNPFloat32(self): a = np.random.random(100).astype(np.float32) jarr = JArray(JDouble)(a) self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy def testArrayInitFromNPFloat64(self): a = np.random.random(100).astype(np.float64) jarr = JArray(JDouble)(a) self.assertElementsAlmostEqual(a, jarr) def testArraySetRange(self): ja = JArray(JDouble)(3) ja[0:1] = [123] self.assertEqual(ja[0], 123) ja[0:1] = [-1] self.assertEqual(ja[0], -1) ja[0:1] = [java.lang.Double(321)] self.assertEqual(ja[0], 321) with self.assertRaises(TypeError): ja[0:1] = [object()] def testArrayHash(self): ja = JArray(JDouble)([1, 2, 3]) self.assertIsInstance(hash(ja), int) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JDouble)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __float__(self): raise SystemError("nope") ja = JArray(JDouble)(5) a = [1, -1, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JDouble)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a def testCastBoolean(self): self.assertEqual(JDouble._canConvertToJava(JBoolean(True)), "none") @common.requireNumpy def testNPFloat16(self): v= [0.000000e+00, 5.960464e-08, 1.788139e-07, 1.788139e-07, 4.172325e-07, 8.940697e-07, 1.847744e-06, 3.755093e-06, 7.569790e-06, 1.519918e-05, 3.045797e-05, 6.097555e-05, 6.103516e-05, 3.332520e-01, 1.000000e+00, 6.550400e+04, np.inf, -np.inf] a = np.array(v, dtype=np.float16) jarr = JArray(JDouble)(a) for v1,v2 in zip(a, jarr): self.assertEqual(v1,v2) a = np.array([np.nan], dtype=np.float16) jarr = JArray(JDouble)(a) self.assertTrue(np.isnan(jarr[0])) jpype-1.6.0/test/jpypetest/test_jedi.py000066400000000000000000000051361501674766700202270ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common import types import functools import inspect have_jedi = False try: import jedi have_jedi = (common.version(jedi.__version__) > (0, 14)) except: pass # FIXME: some jedi version is causing an issue jpype-project/jpype#920 so we pretend not to have jedi, until it is resolved. have_jedi = False class JediTestCase(common.JPypeTestCase): """Test tab completion on JPype objects """ def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.cls = jpype.JClass('java.lang.String') self.obj = self.cls('foo') @common.unittest.skipUnless(have_jedi, "jedi not available") def testCompleteClass(self): src = 'self.obj.con' script = jedi.Interpreter(src, [locals()]) compl = [i.name for i in script.complete()] self.assertEqual(compl, ['concat', 'contains', 'contentEquals']) @common.unittest.skipUnless(have_jedi, "jedi not available") def testCompleteMethod(self): src = 'self.obj.substring(1).con' script = jedi.Interpreter(src, [locals()]) compl = [i.name for i in script.complete()] self.assertEqual(compl, ['concat', 'contains', 'contentEquals']) @common.unittest.skipUnless(have_jedi, "jedi not available") def testCompleteField(self): src = 'self.obj.CASE_INSENSITIVE_ORDER.wa' script = jedi.Interpreter(src, [locals()]) compl = [i.name for i in script.complete()] self.assertEqual(compl, ['wait']) @common.unittest.skipUnless(have_jedi, "jedi not available") def testCompleteMethodField(self): src = 'self.obj.substring(1).CAS' script = jedi.Interpreter(src, [locals()]) compl = [i.name for i in script.complete()] self.assertEqual(compl, ['CASE_INSENSITIVE_ORDER']) jpype-1.6.0/test/jpypetest/test_jfloat.py000066400000000000000000000371451501674766700206000ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import random from unittest.util import safe_repr import _jpype import common import jpype from jpype import java from jpype.types import * try: import numpy as np except ImportError: pass class JFloatTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.value = 1.0 + 1.0 / 65536 self.cls = JClass("jpype.common.Fixture") self.fixture = self.cls() def compareFloatEqual(self, x, y, msg=None): if x == y: return if x < 0: x = -x if y < 0: y = -y a = (x + y) / 2 b = (x - y) if b < 0: b = -b if b < a * 1e-7: return msg = self._formatMessage(msg, '%s == %s' % (safe_repr(x), safe_repr(y))) raise self.failureException(msg) @common.requireInstrumentation def testJPNumberFloat_int(self): jd = JFloat(1) _jpype.fault("PyJPNumberFloat_int") with self.assertRaisesRegex(SystemError, "fault"): int(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): int(jd) int(jd) @common.requireInstrumentation def testJPNumberFloat_float(self): jd = JFloat(1) _jpype.fault("PyJPNumberFloat_float") with self.assertRaisesRegex(SystemError, "fault"): float(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): float(jd) float(jd) @common.requireInstrumentation def testJPNumberFloat_str(self): jd = JFloat(1) _jpype.fault("PyJPNumberFloat_str") with self.assertRaisesRegex(SystemError, "fault"): str(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(jd) str(jd) @common.requireInstrumentation def testJPNumberFloat_repr(self): jd = JFloat(1) _jpype.fault("PyJPNumberFloat_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) repr(jd) @common.requireInstrumentation def testJPNumberFloat_compare(self): jd = JFloat(1) _jpype.fault("PyJPNumberFloat_compare") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 jd == 1 @common.requireInstrumentation def testJPNumberFloat_hash(self): jd = JFloat(1) _jpype.fault("PyJPNumberFloat_hash") with self.assertRaises(SystemError): hash(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(jd) hash(jd) @common.requireInstrumentation def testFault(self): _jpype.fault("JPFloatType::findJavaConversion") with self.assertRaises(SystemError): JFloat(1.0) @common.requireInstrumentation def testConversionFault(self): _jpype.fault("JPFloatType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JFloat._canConvertToJava(object()) @common.requireInstrumentation def testArrayFault(self): ja = JArray(JFloat)(5) _jpype.fault("JPJavaFrame::NewFloatArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JFloat)(1) _jpype.fault("JPJavaFrame::SetFloatArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetFloatArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetFloatArrayElements") # Special case, only BufferError is allowed from getBuffer with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseFloatArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseFloatArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseFloatArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() _jpype.fault("JPFloatType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] def testFromJIntWiden(self): self.assertEqual(JFloat(JByte(123)), 123) self.assertEqual(JFloat(JShort(12345)), 12345) self.assertEqual(JFloat(JInt(12345678)), 12345678) self.assertEqual(JFloat(JLong(12345678)), 12345678) def testFromJFloatWiden(self): self.assertEqual(JFloat(JDouble(12345678)), 12345678) def testFromNone(self): with self.assertRaises(TypeError): JFloat(None) self.assertEqual(JFloat._canConvertToJava(None), "none") def testFromJFloat(self): with self.useEqualityFunc(self.compareFloatEqual): self.assertEqual(JFloat(JFloat(1.2345)), 1.2345) def testFromJDouble(self): with self.useEqualityFunc(self.compareFloatEqual): self.assertEqual(JFloat(JDouble(1.2345)), 1.2345) def testUnBox(self): pass # with self.useEqualityFunc(self.foo): # self.assertEqual(JFloat(java.lang.Double(1.2345)), 1.2345) def testFromFloat(self): with self.useEqualityFunc(self.compareFloatEqual): self.assertEqual(JFloat(1.2345), 1.2345) self.assertEqual(JFloat._canConvertToJava(1.2345), "implicit") def testFromLong(self): self.assertEqual(JFloat(12345), 12345) self.assertEqual(JFloat._canConvertToJava(12345), "implicit") def testFromObject(self): with self.assertRaises(TypeError): JFloat(object()) with self.assertRaises(TypeError): JFloat(JObject()) with self.assertRaises(TypeError): JFloat(JString("A")) self.assertEqual(JFloat._canConvertToJava(object()), "none") ja = JArray(JFloat)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_float_field = object() with self.assertRaises(TypeError): jf().float_field = object() def testCallFloatFromNone(self): with self.assertRaises(TypeError): self.fixture.callFloat(None) with self.assertRaises(TypeError): self.fixture.static_float_field = None with self.assertRaises(TypeError): self.fixture.float_field = None def checkType(self, q): # Check field self.fixture.float_field = q self.assertEqual(self.fixture.float_field, q) self.assertEqual(self.fixture.getFloat(), q) # Check static field self.cls.static_float_field = q self.assertEqual(self.fixture.static_float_field, q) self.assertEqual(self.fixture.getStaticFloat(), q) self.assertEqual(self.cls.getStaticFloat(), q) # Check call self.assertEqual(self.fixture.callFloat(q), q) self.assertEqual(self.cls.callStaticFloat(q), q) # Check throw with self.assertRaises(JException): self.fixture.throwFloat() with self.assertRaises(JException): self.cls.throwStaticFloat() with self.assertRaises(JException): self.fixture.throwStaticFloat() def testCheckInt(self): self.checkType(1) def testCheckFloat(self): self.checkType(2.0) def testCheckRange(self): self.checkType(float(1e340)) self.checkType(float(-1e340)) def testCheckNaN(self): import math nan = float("nan") self.assertTrue(math.isnan(self.fixture.callFloat(nan))) self.fixture.static_float_field = nan self.assertTrue(math.isnan(self.fixture.static_float_field)) self.fixture.float_field = nan self.assertTrue(math.isnan(self.fixture.float_field)) def testCheckInf(self): import math inf = float("inf") self.assertTrue(math.isinf(self.fixture.callFloat(inf))) self.fixture.static_float_field = inf self.assertTrue(math.isinf(self.fixture.static_float_field)) self.fixture.float_field = inf self.assertTrue(math.isinf(self.fixture.float_field)) def testCheckBool(self): self.checkType(True) self.checkType(False) def testCheckJBoolean(self): # FIXME fails # self.checkType(JBoolean(True)) # self.checkType(JBoolean(False)) pass def testCheckJChar(self): self.checkType(JChar("A")) def testCheckJByte(self): self.checkType(JByte(-128)) self.checkType(JByte(127)) def testCheckJShort(self): self.checkType(JShort(-2**15)) self.checkType(JShort(2**15 - 1)) def testCheckJInt(self): with self.useEqualityFunc(self.compareFloatEqual): self.checkType(JInt(-2**31 + 1)) self.checkType(JInt(2**31 - 1)) def testCheckJLong(self): with self.useEqualityFunc(self.compareFloatEqual): self.checkType(JLong(-2**63 + 1)) self.checkType(JLong(2**63 - 1)) def testCheckJFloat(self): self.checkType(JFloat(1.515313)) @common.requireNumpy def testCheckNumpyInt8(self): self.checkType(np.random.randint(-127, 128, dtype=np.int8)) self.checkType(np.random.randint(0, 255, dtype=np.uint8)) self.checkType(np.uint8(0)) self.checkType(np.uint8(255)) self.checkType(np.int8(-128)) self.checkType(np.int8(127)) @common.requireNumpy def testCheckNumpyInt16(self): self.checkType(np.random.randint(-2**15, 2**15 - 1, dtype=np.int16)) self.checkType(np.random.randint(0, 2**16 - 1, dtype=np.uint16)) self.checkType(np.uint16(0)) self.checkType(np.uint16(2**16 - 1)) self.checkType(np.int16(-2**15)) self.checkType(np.int16(2**15 - 1)) @common.requireNumpy def testCheckNumpyInt32(self): with self.useEqualityFunc(self.compareFloatEqual): self.checkType(np.random.randint(-2**31, 2**31 - 1, dtype=np.int32)) self.checkType(np.random.randint(0, 2**32 - 1, dtype=np.uint32)) self.checkType(np.uint32(0)) self.checkType(np.uint32(2**32 - 1)) self.checkType(np.int32(-2**31)) self.checkType(np.int32(2**31 - 1)) @common.requireNumpy def testCheckNumpyInt64(self): with self.useEqualityFunc(self.compareFloatEqual): self.checkType(np.random.randint(-2**63, 2**63 - 1, dtype=np.int64)) self.checkType( np.uint64(np.random.randint(0, 2**64 - 1, dtype=np.uint64))) self.checkType(np.uint64(0)) self.checkType(np.uint64(2**64 - 1)) self.checkType(np.int64(-2**63)) self.checkType(np.int64(2**63 - 1)) @common.requireNumpy def testCheckNumpyFloat32(self): self.checkType(np.float32(np.random.rand())) @common.requireNumpy def testCheckNumpyFloat64(self): with self.useEqualityFunc(self.compareFloatEqual): self.checkType(np.float64(np.random.rand())) def testArrayConversionDouble(self): VALUES = [float(random.random()) for i in range(100)] jarr = JArray(JFloat)(VALUES) self.assertElementsAlmostEqual(VALUES, jarr) result = jarr[:] self.assertElementsAlmostEqual(VALUES, result) result = jarr[2:10] self.assertEqual(len(VALUES[2:10]), len(result)) self.assertElementsAlmostEqual(VALUES[2:10], result) # empty slice result = jarr[-1:3] expected = VALUES[-1:3] self.assertElementsAlmostEqual(expected, result) result = jarr[3:-2] expected = VALUES[3:-2] self.assertElementsAlmostEqual(expected, result) @common.requireNumpy def testArraySetFromNPDouble(self): a = np.random.random(100).astype(np.float64) jarr = JArray(JFloat)(100) jarr[:] = a self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy def testArrayInitFromNPFloat16(self): a = np.random.random(1000).astype(np.float16) jarr = JArray(JFloat)(a) self.assertElementsEqual(a, jarr) @common.requireNumpy def testArrayInitFromNPFloat32(self): a = np.random.random(100).astype(np.float32) jarr = JArray(JFloat)(a) self.assertElementsAlmostEqual(a, jarr) @common.requireNumpy def testArrayInitFromNPFloat64(self): a = np.random.random(100).astype(np.float64) jarr = JArray(JFloat)(a) self.assertElementsAlmostEqual(a, jarr) def testArraySetRange(self): ja = JArray(JFloat)(3) ja[0:1] = [123] self.assertEqual(ja[0], 123) ja[0:1] = [-1] self.assertEqual(ja[0], -1) ja[0:1] = [java.lang.Double(321)] self.assertEqual(ja[0], 321) with self.assertRaises(TypeError): ja[0:1] = [object()] def testArrayHash(self): ja = JArray(JFloat)([1, 2, 3]) self.assertIsInstance(hash(ja), int) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JFloat)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __float__(self): raise SystemError("nope") ja = JArray(JFloat)(5) a = [1, -1, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JFloat)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a @common.requireNumpy def testNPFloat16(self): v= [0.000000e+00, 5.960464e-08, 1.788139e-07, 1.788139e-07, 4.172325e-07, 8.940697e-07, 1.847744e-06, 3.755093e-06, 7.569790e-06, 1.519918e-05, 3.045797e-05, 6.097555e-05, 6.103516e-05, 3.332520e-01, 1.000000e+00, 6.550400e+04, np.inf, -np.inf] a = np.array(v, dtype=np.float16) jarr = JArray(JFloat)(a) for v1,v2 in zip(a, jarr): self.assertEqual(v1,v2) a = np.array([np.nan], dtype=np.float16) jarr = JArray(JFloat)(a) self.assertTrue(np.isnan(jarr[0])) jpype-1.6.0/test/jpypetest/test_jint.py000066400000000000000000000464561501674766700202720ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common import random import _jpype import jpype from jpype import java from jpype.types import * try: import numpy as np except ImportError: pass VALUES = [random.randint(-2**31, 2**31 - 1) for i in range(10)] class JIntTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.cls = JClass("jpype.common.Fixture") self.fixture = self.cls() @common.requireInstrumentation def testJPNumberLong_int(self): jd = JInt(1) _jpype.fault("PyJPNumberLong_int") with self.assertRaisesRegex(SystemError, "fault"): int(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): int(jd) int(jd) @common.requireInstrumentation def testJPNumberLong_float(self): jd = JInt(1) _jpype.fault("PyJPNumberLong_float") with self.assertRaisesRegex(SystemError, "fault"): float(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): float(jd) float(jd) @common.requireInstrumentation def testJPNumberLong_str(self): jd = JInt(1) _jpype.fault("PyJPNumberLong_str") with self.assertRaisesRegex(SystemError, "fault"): str(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(jd) str(jd) @common.requireInstrumentation def testJPNumberLong_repr(self): jd = JInt(1) _jpype.fault("PyJPNumberLong_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) repr(jd) @common.requireInstrumentation def testJPNumberLong_compare(self): jd = JInt(1) _jpype.fault("PyJPNumberLong_compare") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 jd == 1 @common.requireInstrumentation def testJPNumberLong_hash(self): jd = JInt(1) _jpype.fault("PyJPNumberLong_hash") with self.assertRaises(SystemError): hash(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(jd) hash(jd) @common.requireInstrumentation def testFault(self): _jpype.fault("JPIntType::findJavaConversion") with self.assertRaises(SystemError): JInt(1.0) @common.requireInstrumentation def testConversionFault(self): _jpype.fault("JPIntType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JInt._canConvertToJava(object()) @common.requireInstrumentation def testArrayFault(self): ja = JArray(JInt)(5) _jpype.fault("JPJavaFrame::NewIntArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JInt)(1) _jpype.fault("JPJavaFrame::SetIntArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetIntArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetIntArrayElements") # Special case, only BufferError is allowed from getBuffer with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseIntArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseIntArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseIntArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() _jpype.fault("JPIntType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] def testFromJIntWiden(self): self.assertEqual(JInt(JByte(123)), 123) self.assertEqual(JInt(JShort(12345)), 12345) self.assertEqual(JInt(JInt(12345678)), 12345678) self.assertEqual(JInt(JLong(12345678)), 12345678) def testFromJIntWiden_2(self): self.assertEqual(JInt(JDouble(12345678)), 12345678) def testFromNone(self): with self.assertRaises(TypeError): JInt(None) self.assertEqual(JInt._canConvertToJava(None), "none") def testUnBox(self): self.assertEqual(JInt(java.lang.Double(1.2345)), 1) def testFromFloat(self): self.assertEqual(JInt._canConvertToJava(1.2345), "explicit") @jpype.JImplements("java.util.function.IntSupplier") class q(object): @jpype.JOverride def getAsInt(self): return 4.5 # this will hit explicit conversion self.assertEqual(JObject(q()).getAsInt(), 4) def testFromLong(self): self.assertEqual(JInt(12345), 12345) self.assertEqual(JInt._canConvertToJava(12345), "implicit") def testFromObject(self): with self.assertRaises(TypeError): JInt(object()) with self.assertRaises(TypeError): JInt(JObject()) with self.assertRaises(TypeError): JInt(JString("A")) self.assertEqual(JInt._canConvertToJava(object()), "none") ja = JArray(JInt)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_int_field = object() with self.assertRaises(TypeError): jf().int_field = object() def testCallFloatFromNone(self): with self.assertRaises(TypeError): self.fixture.callFloat(None) with self.assertRaises(TypeError): self.fixture.static_int_field = None with self.assertRaises(TypeError): self.fixture.int_field = None def testThrow(self): # Check throw with self.assertRaises(JException): self.fixture.throwInt() with self.assertRaises(JException): self.cls.throwStaticInt() with self.assertRaises(JException): self.fixture.throwStaticInt() def checkType(self, q): # Check field self.fixture.int_field = q self.assertEqual(self.fixture.int_field, q) self.assertEqual(self.fixture.getInt(), q) # Check static field self.cls.static_int_field = q self.assertEqual(self.fixture.static_int_field, q) self.assertEqual(self.fixture.getStaticInt(), q) self.assertEqual(self.cls.getStaticInt(), q) # Check call self.assertEqual(self.fixture.callInt(q), q) self.assertEqual(self.cls.callStaticInt(q), q) def checkTypeFail(self, q, exc=TypeError): with self.assertRaises(exc): self.fixture.int_field = q with self.assertRaises(exc): self.fixture.callInt(q) with self.assertRaises(exc): self.fixture.callStaticInt(q) def testCastFloat(self): self.fixture.int_field = JInt(6.0) self.assertEqual(self.fixture.int_field, 6) def testCheckInt(self): self.checkType(1) def testCheckFloat(self): self.checkTypeFail(2.0) def testCheckRange(self): self.checkType(2**31 - 1) self.checkType(-2**31) self.checkTypeFail(2**31, exc=OverflowError) self.checkTypeFail(-2**31 - 1, exc=OverflowError) def testExplicitRange(self): # These will not overflow as they are explicit casts self.assertEqual(JInt(2**32), 0) self.assertEqual(JInt(-2**32), 0) def testCheckBool(self): self.checkType(True) self.checkType(False) def testCheckJBoolean(self): self.checkTypeFail(JBoolean(True)) self.checkTypeFail(JBoolean(False)) def testCheckJChar(self): self.checkType(JChar("A")) def testCheckJByte(self): self.checkType(JByte(-128)) self.checkType(JByte(127)) def testCheckJShort(self): self.checkType(JShort(-2**15)) self.checkType(JShort(2**15 - 1)) def testCheckJInt(self): self.checkType(JInt(-2**31 + 1)) self.checkType(JInt(2**31 - 1)) def testCheckJLong(self): self.checkTypeFail(JLong(-2**63 + 1)) self.checkTypeFail(JLong(2**63 - 1)) @common.requireNumpy def testCheckNumpyInt8(self): self.checkType(np.random.randint(-127, 128, dtype=np.int8)) self.checkType(np.random.randint(0, 255, dtype=np.uint8)) self.checkType(np.uint8(0)) self.checkType(np.uint8(255)) self.checkType(np.int8(-128)) self.checkType(np.int8(127)) @common.requireNumpy def testCheckNumpyInt16(self): self.checkType(np.random.randint(-2**15, 2**15 - 1, dtype=np.int16)) self.checkType(np.random.randint(0, 2**16 - 1, dtype=np.uint16)) self.checkType(np.uint16(0)) self.checkType(np.uint16(2**16 - 1)) self.checkType(np.int16(-2**15)) self.checkType(np.int16(2**15 - 1)) @common.requireNumpy def testCheckNumpyInt32(self): self.checkType(np.uint32(0)) self.checkTypeFail(np.uint32(2**32 - 1), exc=OverflowError) self.checkType(np.int32(-2**31)) self.checkType(np.int32(2**31 - 1)) @common.requireNumpy def testCheckNumpyInt64(self): #self.checkTypeFail(np.random.randint(-2**63,2**63-1, dtype=np.int64)) # FIXME OverflowError #self.checkType(np.uint64(np.random.randint(0,2**64-1, dtype=np.uint64))) # FIXME OverflowError # self.checkType(np.uint64(2**64-1)) self.checkTypeFail(np.int64(-2**63), OverflowError) self.checkTypeFail(np.int64(2**63 - 1), OverflowError) @common.requireNumpy def testCheckNumpyFloat32(self): self.checkTypeFail(np.float32(np.random.rand())) @common.requireNumpy def testCheckNumpyFloat64(self): self.checkTypeFail(np.float64(np.random.rand())) def checkArrayType(self, a, expected): # Check init ja = JArray(JInt)(a) self.assertElementsEqual(ja, expected) ja = JArray(JInt)(len(a)) ja[:] = a self.assertElementsEqual(ja, expected) return ja def checkArrayTypeFail(self, a): # Check init ja = JArray(JInt)(a) ja = JArray(JInt)(len(a)) ja[:] = a def testArrayConversion(self): a = [random.randint(-2**31, 2**31) for i in range(100)] jarr = self.checkArrayType(a, a) result = jarr[2:10] self.assertEqual(len(a[2:10]), len(result)) self.assertElementsAlmostEqual(a[2:10], result) # empty slice result = jarr[-1:3] expected = a[-1:3] self.assertElementsAlmostEqual(expected, result) result = jarr[3:-2] expected = a[3:-2] self.assertElementsAlmostEqual(expected, result) @common.requireNumpy def testArrayInitFromNPInt(self): a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int_) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt8(self): a = np.random.randint(-2**7, 2**7 - 1, size=100, dtype=np.int8) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt16(self): a = np.random.randint(-2**15, 2**15 - 1, size=100, dtype=np.int16) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt32(self): a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int32) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt64(self): a = np.random.randint(-2**63, 2**63 - 1, size=100, dtype=np.int64) self.checkArrayType(a, a.astype(np.int32)) @common.requireNumpy def testArrayInitFromNPFloat32(self): a = np.random.random(100).astype(np.float32) self.checkArrayType(a, a.astype(np.int32)) @common.requireNumpy def testArrayInitFromNPFloat64(self): a = np.random.random(100).astype(np.float64) self.checkArrayType(a, a.astype(np.int32)) def testArraySetRange(self): ja = JArray(JInt)(3) ja[0:1] = [123] self.assertEqual(ja[0], 123) ja[0:1] = [-1] self.assertEqual(ja[0], -1) with self.assertRaises(TypeError): ja[0:1] = [1.000] with self.assertRaises(TypeError): ja[0:1] = [java.lang.Double(321)] with self.assertRaises(TypeError): ja[0:1] = [object()] def testArrayConversionFail(self): jarr = JArray(JInt)(VALUES) with self.assertRaises(TypeError): jarr[1] = 'a' def testArraySliceLength(self): jarr = JArray(JInt)(VALUES) jarr[1:2] = [1] with self.assertRaises(ValueError): jarr[1:2] = [1, 2, 3] def testArrayConversionInt(self): jarr = JArray(JInt)(VALUES) result = jarr[0: len(jarr)] self.assertElementsEqual(VALUES, result) result = jarr[2:10] self.assertElementsEqual(VALUES[2:10], result) def testArrayConversionError(self): jarr = JArray(JInt, 1)(10) with self.assertRaises(TypeError): jarr[1:2] = [dict()] # -1 is returned by python, if conversion fails also, ensure this works jarr[1:2] = [-1] def testArrayClone(self): array = JArray(JInt, 2)([[1, 2], [3, 4]]) carray = array.clone() # Verify the first dimension is cloned self.assertFalse(array.equals(carray)) # Copy is shallow self.assertTrue(array[0].equals(carray[0])) def testArrayGetSlice(self): contents = VALUES array = JArray(JInt)(contents) self.assertEqual(list(array[1:]), contents[1:]) self.assertEqual(list(array[:-1]), contents[:-1]) self.assertEqual(list(array[1:-1]), contents[1:-1]) def testArraySetSlice(self): contents = [1, 2, 3, 4] array = JArray(JInt)(contents) array[1:] = [5, 6, 7] contents[1:] = [5, 6, 7] self.assertEqual(list(array[:]), contents[:]) array[:-1] = [8, 9, 10] contents[:-1] = [8, 9, 10] self.assertEqual(list(array[:]), contents[:]) def testArrayGetSliceStep(self): contents = VALUES array = JArray(JInt)(contents) self.assertEqual(list(array[::2]), contents[::2]) self.assertEqual(list(array[::3]), contents[::3]) self.assertEqual(list(array[::4]), contents[::4]) self.assertEqual(list(array[::5]), contents[::5]) self.assertEqual(list(array[::6]), contents[::6]) self.assertEqual(list(array[::7]), contents[::7]) self.assertEqual(list(array[::8]), contents[::8]) self.assertEqual(list(array[1::3]), contents[1::3]) self.assertEqual(list(array[1:-2:3]), contents[1:-2:3]) def testArraySliceStepNeg(self): contents = VALUES array = JArray(JInt)(contents) self.assertEqual(list(array[::-1]), contents[::-1]) self.assertEqual(list(array[::-2]), contents[::-2]) self.assertEqual(list(array[::-3]), contents[::-3]) self.assertEqual(list(array[::-4]), contents[::-4]) self.assertEqual(list(array[::-5]), contents[::-5]) self.assertEqual(list(array[::-6]), contents[::-6]) self.assertEqual(list(array[2::-3]), contents[2::-3]) self.assertEqual(list(array[-2::-3]), contents[-2::-3]) def testArraySetArraySliceStep(self): contents = [1, 2, 3, 4, 5, 6] array = JArray(JInt)(contents) array[::2] = [5, 6, 7] contents[::2] = [5, 6, 7] self.assertEqual(list(array[:]), contents[:]) def testArrayEquals(self): contents = VALUES array = JArray(JInt)(contents) array2 = JArray(JInt)(contents) self.assertEqual(array, array) self.assertNotEqual(array, array2) def testArrayIter(self): contents = VALUES array = JArray(JInt)(contents) contents2 = [i for i in array] self.assertEqual(contents, contents2) def testArrayGetOutOfBounds(self): contents = [1, 2, 3, 4] array = JArray(JInt)(contents) with self.assertRaises(IndexError): array[5] self.assertEqual(array[-1], contents[-1]) self.assertEqual(array[-4], contents[-4]) with self.assertRaises(IndexError): array[-5] def testArraySetOutOfBounds(self): contents = [1, 2, 3, 4] array = JArray(JInt)(contents) with self.assertRaises(IndexError): array[5] = 1 array[-1] = 5 contents[-1] = 5 array[-4] = 6 contents[-4] = 6 self.assertEqual(list(array[:]), contents) with self.assertRaises(IndexError): array[-5] = 1 def testArraySliceCast(self): JA = JArray(JInt) ja = JA(VALUES) ja2 = ja[::2] jo = jpype.JObject(ja2, jpype.JObject) ja3 = jpype.JObject(jo, JA) self.assertEqual(type(jo), jpype.JClass("java.lang.Object")) self.assertEqual(type(ja2), JA) self.assertEqual(type(ja3), JA) self.assertEqual(list(ja2), list(ja3)) def testArrayReverse(self): n = list(VALUES) ja = JArray(JInt)(n) a = [i for i in reversed(ja)] n = [i for i in reversed(n)] self.assertEqual(a, n) def testArrayHash(self): ja = JArray(JInt)([1, 2, 3]) self.assertIsInstance(hash(ja), int) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JInt)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __int__(self): raise SystemError("nope") def __index__(self): raise SystemError("nope") ja = JArray(JInt)(5) a = [1, -1, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JInt)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a jpype-1.6.0/test/jpypetest/test_jlong.py000066400000000000000000000465101501674766700204260ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common import random import _jpype import jpype from jpype import java from jpype.types import * try: import numpy as np except ImportError: pass VALUES = [random.randint(-2**63, 2**63 - 1) for i in range(10)] class JLongTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.cls = JClass("jpype.common.Fixture") self.fixture = self.cls() @common.requireInstrumentation def testJPNumberLong_int(self): jd = JLong(1) _jpype.fault("PyJPNumberLong_int") with self.assertRaisesRegex(SystemError, "fault"): int(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): int(jd) int(jd) @common.requireInstrumentation def testJPNumberLong_float(self): jd = JLong(1) _jpype.fault("PyJPNumberLong_float") with self.assertRaisesRegex(SystemError, "fault"): float(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): float(jd) float(jd) @common.requireInstrumentation def testJPNumberLong_str(self): jd = JLong(1) _jpype.fault("PyJPNumberLong_str") with self.assertRaisesRegex(SystemError, "fault"): str(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(jd) str(jd) @common.requireInstrumentation def testJPNumberLong_repr(self): jd = JLong(1) _jpype.fault("PyJPNumberLong_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) repr(jd) @common.requireInstrumentation def testJPNumberLong_compare(self): jd = JLong(1) _jpype.fault("PyJPNumberLong_compare") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 jd == 1 @common.requireInstrumentation def testJPNumberLong_hash(self): jd = JLong(1) _jpype.fault("PyJPNumberLong_hash") with self.assertRaises(SystemError): hash(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(jd) hash(jd) @common.requireInstrumentation def testFault(self): _jpype.fault("JPLongType::findJavaConversion") with self.assertRaises(SystemError): JLong(1.0) @common.requireInstrumentation def testConversionFault(self): _jpype.fault("JPLongType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JLong._canConvertToJava(object()) @common.requireInstrumentation def testArrayFault(self): ja = JArray(JLong)(5) _jpype.fault("JPJavaFrame::NewLongArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JLong)(1) _jpype.fault("JPJavaFrame::SetLongArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetLongArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetLongArrayElements") # Special case, only BufferError is allowed from getBuffer with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseLongArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseLongArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseLongArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() _jpype.fault("JPLongType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] def testFromJLongWiden_2(self): self.assertEqual(JLong(JByte(123)), 123) self.assertEqual(JLong(JShort(12345)), 12345) self.assertEqual(JLong(JInt(12345678)), 12345678) self.assertEqual(JLong(JLong(12345678)), 12345678) def testFromJLongWiden_3(self): self.assertEqual(JLong(JDouble(12345678)), 12345678) def testFromNone(self): with self.assertRaises(TypeError): JLong(None) self.assertEqual(JLong._canConvertToJava(None), "none") def testUnBox(self): self.assertEqual(JLong(java.lang.Double(1.2345)), 1) def testFromFloat(self): self.assertEqual(JLong._canConvertToJava(1.2345), "explicit") @jpype.JImplements("java.util.function.LongSupplier") class q(object): @jpype.JOverride def getAsLong(self): return 4.5 # this will hit explicit conversion self.assertEqual(JObject(q()).getAsLong(), 4) def testFromLong(self): self.assertEqual(JLong(12345), 12345) self.assertEqual(JLong._canConvertToJava(12345), "implicit") def testFromObject(self): with self.assertRaises(TypeError): JLong(object()) with self.assertRaises(TypeError): JLong(JObject()) with self.assertRaises(TypeError): JLong(JString("A")) self.assertEqual(JLong._canConvertToJava(object()), "none") ja = JArray(JLong)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_long_field = object() with self.assertRaises(TypeError): jf().long_field = object() def testCallFloatFromNone(self): with self.assertRaises(TypeError): self.fixture.callFloat(None) with self.assertRaises(TypeError): self.fixture.static_long_field = None with self.assertRaises(TypeError): self.fixture.long_field = None def testThrow(self): # Check throw with self.assertRaises(JException): self.fixture.throwInt() with self.assertRaises(JException): self.cls.throwStaticInt() with self.assertRaises(JException): self.fixture.throwStaticInt() def checkType(self, q): # Check field self.fixture.long_field = q self.assertEqual(self.fixture.long_field, q) self.assertEqual(self.fixture.getLong(), q) # Check static field self.cls.static_long_field = q self.assertEqual(self.fixture.static_long_field, q) self.assertEqual(self.fixture.getStaticLong(), q) self.assertEqual(self.cls.getStaticLong(), q) # Check call self.assertEqual(self.fixture.callLong(q), q) self.assertEqual(self.cls.callStaticLong(q), q) def checkTypeFail(self, q, exc=TypeError): with self.assertRaises(exc): self.fixture.long_field = q with self.assertRaises(exc): self.fixture.callLong(q) with self.assertRaises(exc): self.fixture.callStaticLong(q) def testCastFloat(self): self.fixture.long_field = JLong(6.0) self.assertEqual(self.fixture.long_field, 6) def testCheckInt(self): self.checkType(1) def testCheckFloat(self): self.checkTypeFail(2.0) def testCheckRange(self): self.checkType(2**63 - 1) self.checkType(-2**63) self.checkTypeFail(2**63, exc=OverflowError) self.checkTypeFail(-2**63 - 1, exc=OverflowError) def testExplicitRange(self): # These will not overflow as they are explicit casts self.assertEqual(JLong(2**64), 0) self.assertEqual(JLong(-2**64), 0) def testCheckBool(self): self.checkType(True) self.checkType(False) def testCheckJBoolean(self): self.checkTypeFail(JBoolean(True)) self.checkTypeFail(JBoolean(False)) def testCheckJChar(self): self.checkType(JChar("A")) def testCheckJByte(self): self.checkType(JByte(-128)) self.checkType(JByte(127)) def testCheckJShort(self): self.checkType(JShort(-2**15)) self.checkType(JShort(2**15 - 1)) def testCheckJLong(self): self.checkType(JLong(-2**31 + 1)) self.checkType(JLong(2**31 - 1)) def testCheckJLong_2(self): self.checkType(JLong(-2**63 + 1)) self.checkType(JLong(2**63 - 1)) @common.requireNumpy def testCheckNumpyInt8(self): self.checkType(np.random.randint(-127, 128, dtype=np.int8)) self.checkType(np.random.randint(0, 255, dtype=np.uint8)) self.checkType(np.uint8(0)) self.checkType(np.uint8(255)) self.checkType(np.int8(-128)) self.checkType(np.int8(127)) @common.requireNumpy def testCheckNumpyInt16(self): self.checkType(np.random.randint(-2**15, 2**15 - 1, dtype=np.int16)) self.checkType(np.random.randint(0, 2**16 - 1, dtype=np.uint16)) self.checkType(np.uint16(0)) self.checkType(np.uint16(2**16 - 1)) self.checkType(np.int16(-2**15)) self.checkType(np.int16(2**15 - 1)) @common.requireNumpy def testCheckNumpyInt32(self): self.checkType(np.uint32(0)) self.checkType(np.uint32(2**32 - 1)) self.checkType(np.int32(-2**31)) self.checkType(np.int32(2**31 - 1)) @common.requireNumpy def testCheckNumpyInt64(self): #self.checkTypeFail(np.random.randint(-2**63,2**63-1, dtype=np.int64)) # FIXME OverflowError #self.checkType(np.uint64(np.random.randint(0,2**64-1, dtype=np.uint64))) # FIXME OverflowError # self.checkType(np.uint64(2**64-1)) self.checkType(np.int64(-2**63)) self.checkType(np.int64(2**63 - 1)) @common.requireNumpy def testCheckNumpyFloat32(self): self.checkTypeFail(np.float32(np.random.rand())) @common.requireNumpy def testCheckNumpyFloat64(self): self.checkTypeFail(np.float64(np.random.rand())) def checkArrayType(self, a, expected): # Check init ja = JArray(JLong)(a) self.assertElementsEqual(ja, expected) ja = JArray(JLong)(len(a)) ja[:] = a self.assertElementsEqual(ja, expected) return ja def checkArrayTypeFail(self, a): # Check init ja = JArray(JLong)(a) ja = JArray(JLong)(len(a)) ja[:] = a def testArrayConversion(self): a = [random.randint(-2**31, 2**31) for i in range(100)] jarr = self.checkArrayType(a, a) result = jarr[2:10] self.assertEqual(len(a[2:10]), len(result)) self.assertElementsAlmostEqual(a[2:10], result) # empty slice result = jarr[-1:3] expected = a[-1:3] self.assertElementsAlmostEqual(expected, result) result = jarr[3:-2] expected = a[3:-2] self.assertElementsAlmostEqual(expected, result) @common.requireNumpy def testArrayInitFromNPInt(self): a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int_) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt8(self): a = np.random.randint(-2**7, 2**7 - 1, size=100, dtype=np.int8) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt16(self): a = np.random.randint(-2**15, 2**15 - 1, size=100, dtype=np.int16) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt32(self): a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int32) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt64(self): a = np.random.randint(-2**63, 2**63 - 1, size=100, dtype=np.int64) self.checkArrayType(a, a.astype(np.int64)) @common.requireNumpy def testArrayInitFromNPFloat32(self): a = np.random.random(100).astype(np.float32) self.checkArrayType(a, a.astype(np.int64)) @common.requireNumpy def testArrayInitFromNPFloat64(self): a = np.random.random(100).astype(np.float64) self.checkArrayType(a, a.astype(np.int64)) def testArraySetRange(self): ja = JArray(JLong)(3) ja[0:1] = [123] self.assertEqual(ja[0], 123) ja[0:1] = [-1] self.assertEqual(ja[0], -1) with self.assertRaises(TypeError): ja[0:1] = [1.000] with self.assertRaises(TypeError): ja[0:1] = [java.lang.Double(321)] with self.assertRaises(TypeError): ja[0:1] = [object()] def testArrayConversionFail(self): jarr = JArray(JLong)(VALUES) with self.assertRaises(TypeError): jarr[1] = 'a' def testArraySliceLength(self): jarr = JArray(JLong)(VALUES) jarr[1:2] = [1] with self.assertRaises(ValueError): jarr[1:2] = [1, 2, 3] def testArrayConversionInt(self): jarr = JArray(JLong)(VALUES) result = jarr[0: len(jarr)] self.assertElementsEqual(VALUES, result) result = jarr[2:10] self.assertElementsEqual(VALUES[2:10], result) def testArrayConversionError(self): jarr = JArray(JLong, 1)(10) with self.assertRaises(TypeError): jarr[1:2] = [dict()] # -1 is returned by python, if conversion fails also, ensure this works jarr[1:2] = [-1] def testArrayClone(self): array = JArray(JLong, 2)([[1, 2], [3, 4]]) carray = array.clone() # Verify the first dimension is cloned self.assertFalse(array.equals(carray)) # Copy is shallow self.assertTrue(array[0].equals(carray[0])) def testArrayGetSlice(self): contents = VALUES array = JArray(JLong)(contents) self.assertEqual(list(array[1:]), contents[1:]) self.assertEqual(list(array[:-1]), contents[:-1]) self.assertEqual(list(array[1:-1]), contents[1:-1]) def testArraySetSlice(self): contents = [1, 2, 3, 4] array = JArray(JLong)(contents) array[1:] = [5, 6, 7] contents[1:] = [5, 6, 7] self.assertEqual(list(array[:]), contents[:]) array[:-1] = [8, 9, 10] contents[:-1] = [8, 9, 10] self.assertEqual(list(array[:]), contents[:]) def testArrayGetSliceStep(self): contents = VALUES array = JArray(JLong)(contents) self.assertEqual(list(array[::2]), contents[::2]) self.assertEqual(list(array[::3]), contents[::3]) self.assertEqual(list(array[::4]), contents[::4]) self.assertEqual(list(array[::5]), contents[::5]) self.assertEqual(list(array[::6]), contents[::6]) self.assertEqual(list(array[::7]), contents[::7]) self.assertEqual(list(array[::8]), contents[::8]) self.assertEqual(list(array[1::3]), contents[1::3]) self.assertEqual(list(array[1:-2:3]), contents[1:-2:3]) def testArraySliceStepNeg(self): contents = VALUES array = JArray(JLong)(contents) self.assertEqual(list(array[::-1]), contents[::-1]) self.assertEqual(list(array[::-2]), contents[::-2]) self.assertEqual(list(array[::-3]), contents[::-3]) self.assertEqual(list(array[::-4]), contents[::-4]) self.assertEqual(list(array[::-5]), contents[::-5]) self.assertEqual(list(array[::-6]), contents[::-6]) self.assertEqual(list(array[2::-3]), contents[2::-3]) self.assertEqual(list(array[-2::-3]), contents[-2::-3]) def testArraySetArraySliceStep(self): contents = [1, 2, 3, 4, 5, 6] array = JArray(JLong)(contents) array[::2] = [5, 6, 7] contents[::2] = [5, 6, 7] self.assertEqual(list(array[:]), contents[:]) def testArrayEquals(self): contents = VALUES array = JArray(JLong)(contents) array2 = JArray(JLong)(contents) self.assertEqual(array, array) self.assertNotEqual(array, array2) def testArrayIter(self): contents = VALUES array = JArray(JLong)(contents) contents2 = [i for i in array] self.assertEqual(contents, contents2) def testArrayGetOutOfBounds(self): contents = [1, 2, 3, 4] array = JArray(JLong)(contents) with self.assertRaises(IndexError): array[5] self.assertEqual(array[-1], contents[-1]) self.assertEqual(array[-4], contents[-4]) with self.assertRaises(IndexError): array[-5] def testArraySetOutOfBounds(self): contents = [1, 2, 3, 4] array = JArray(JLong)(contents) with self.assertRaises(IndexError): array[5] = 1 array[-1] = 5 contents[-1] = 5 array[-4] = 6 contents[-4] = 6 self.assertEqual(list(array[:]), contents) with self.assertRaises(IndexError): array[-5] = 1 def testArraySliceCast(self): JA = JArray(JLong) ja = JA(VALUES) ja2 = ja[::2] jo = jpype.JObject(ja2, jpype.JObject) ja3 = jpype.JObject(jo, JA) self.assertEqual(type(jo), jpype.JClass("java.lang.Object")) self.assertEqual(type(ja2), JA) self.assertEqual(type(ja3), JA) self.assertEqual(list(ja2), list(ja3)) def testArrayReverse(self): n = list(VALUES) ja = JArray(JLong)(n) a = [i for i in reversed(ja)] n = [i for i in reversed(n)] self.assertEqual(a, n) def testArrayHash(self): ja = JArray(JLong)([1, 2, 3]) self.assertIsInstance(hash(ja), int) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JLong)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __int__(self): raise SystemError("nope") def __index__(self): raise SystemError("nope") ja = JArray(JLong)(5) a = [1, -1, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JInt)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a jpype-1.6.0/test/jpypetest/test_jmethod.py000066400000000000000000000273041501674766700207470ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import _jpype import jpype from jpype.types import * import common import types import functools import inspect # Code from stackoverflow # Reference http://stackoverflow.com/questions/13503079/how-to-create-a-copy-of-a-python-function def copy_func(f): """Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)""" g = types.FunctionType(f.__code__, f.__globals__, name=f.__name__, argdefs=f.__defaults__, closure=f.__closure__) g.__kwdefaults__ = f.__kwdefaults__ g = functools.update_wrapper(g, f) return g class JMethodTestCase(common.JPypeTestCase): """ Test for methods of JMethod (_jpype._JMethod) This should test how well the object matchs a Python3 function. * __self__: should appear on a bound, None otherwise. * __name__: should be set * __qualname__: should be set * __doc__: should be set * __annotations__: should give return type * __defaults__, __kwdefaults__, __code__, __globals__, __closure__: should be enough to "clone" the method. It should also be callable as a method, class method. Further inspect should work * inspect.getdoc() should match __doc__ * inspect.signature() should work * inspect.isroutine() should be True We are not going to try to pretend to be Python2. """ def setUp(self): common.JPypeTestCase.setUp(self) self.cls = jpype.JClass('java.lang.String') self.obj = self.cls('foo') def testMethodSelf(self): self.assertEqual(self.cls.substring.__self__, None) self.assertEqual(self.obj.substring.__self__, self.obj) def testMethodName(self): self.assertEqual(self.cls.substring.__name__, "substring") self.assertEqual(self.obj.substring.__name__, "substring") def testMethodQualName(self): self.assertEqual(self.cls.substring.__qualname__, "java.lang.String.substring") self.assertEqual(self.obj.substring.__qualname__, "java.lang.String.substring") def testMethodDoc(self): self.assertIsInstance(self.cls.substring.__doc__, str) self.assertIsInstance(self.obj.substring.__doc__, str) d = self.cls.substring.__doc__ self.cls.substring.__doc__ = None self.assertIsNone(self.cls.substring.__doc__) self.assertIsNone(self.obj.substring.__doc__) self.cls.substring.__doc__ = d def testMethodInspectDoc(self): self.assertIsInstance(inspect.getdoc(self.cls.substring), str) self.assertIsInstance(inspect.getdoc(self.obj.substring), str) self.assertIsInstance(inspect.getdoc(self.obj.format), str) def testMethodAnnotations(self): self.assertIsInstance(self.cls.substring.__annotations__, dict) self.assertIsNotNone(self.obj.substring.__annotations__) a = self.cls.substring.__annotations__ d = {} self.cls.substring.__annotations__ = d self.assertEqual(self.cls.substring.__annotations__, d) self.cls.substring.__annotations__ = a self.assertIsNotNone(self.cls.substring.__annotations__) # This one will need to change in Python 3.8 self.assertEqual(self.cls.substring.__annotations__[ "return"], self.cls) self.assertEqual(self.cls.trim.__annotations__[ "return"], self.cls) self.assertEqual(self.cls.getBytes.__annotations__, {}) def testMethodInspectSignature(self): self.assertIsInstance(inspect.signature( self.cls.substring), inspect.Signature) self.assertIsInstance(inspect.signature( self.obj.substring), inspect.Signature) self.assertEqual(inspect.signature( self.obj.substring).return_annotation, self.cls) def testMethodInspectFunction(self): self.assertTrue(inspect.isfunction(self.cls.substring)) self.assertTrue(inspect.isfunction(self.obj.substring)) def testMethodInspectRoutine(self): self.assertTrue(inspect.isroutine(self.cls.substring)) self.assertTrue(inspect.isroutine(self.obj.substring)) def testMethodClassCall(self): self.assertEqual(self.cls.substring(self.obj, 1), "oo") def testMethodClassCallWierd(self): self.assertEqual(self.cls.substring("foo", 1), "oo") def testMethodClassCallFail(self): with self.assertRaises(TypeError): self.cls.substring(1, 1) def testMethodCall(self): self.assertEqual(self.obj.substring(1), "oo") def testMethodClone(self): a = copy_func(self.cls.substring) self.assertEqual(a(self.obj, 1), "oo") a = copy_func(self.obj.substring) self.assertEqual(a(1), "oo") def testMethodDump(self): # This is replaced by doc, should be removed (or do something useful) self.assertIsInstance(jpype.JString( "foo").substring.matchReport(), str) def testMethodHelp(self): import io import contextlib f = io.StringIO() with contextlib.redirect_stdout(f): help(jpype.JString("a").substring) s = f.getvalue() self.assertTrue("Java method dispatch" in s) self.assertTrue("substring(int)" in s) self.assertTrue("substring(int, int)" in s) @common.requireInstrumentation def testJMethod_get(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_get") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt(1) @common.requireInstrumentation def testJMethod_str(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_get") with self.assertRaisesRegex(SystemError, "fault"): str(fixture.callInt) @common.requireInstrumentation def testJMethod_str_2(self): Fixture = JClass("jpype.common.Fixture") fixture = Fixture() _jpype.fault("PyJPMethod_get") with self.assertRaisesRegex(SystemError, "fault"): repr(fixture.callInt) repr(Fixture.callInt) repr(fixture.callInt) @common.requireInstrumentation def testJMethod_selfFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_getSelf") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.__self__ @common.requireInstrumentation def testJMethod_nameFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_getName") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.__name__ @common.requireInstrumentation def testJMethod_qualnameFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_getQualName") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.__qualname__ @common.requireInstrumentation def testJMethod_annotationsFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_getAnnotations") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.__annotations__ @common.requireInstrumentation def testJMethod_docFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_getDoc") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.__doc__ _jpype.fault("PyJPMethod_setDoc") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.__doc__ = None @common.requireInstrumentation def testJMethod_docFault_2(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_getCodeAttr") with self.assertRaisesRegex(SystemError, "fault"): fixture.callFloat.__code__ @common.requireInstrumentation def testJMethod_beansFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_isBeanAccessor") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt._isBeanAccessor() _jpype.fault("PyJPMethod_isBeanMutator") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt._isBeanMutator() @common.requireInstrumentation def testJMethod_diagnosticsFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_matchReport") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt.matchReport() @common.requireInstrumentation def testJMethod_callFault(self): fixture = JClass("jpype.common.Fixture")() _jpype.fault("PyJPMethod_call") with self.assertRaisesRegex(SystemError, "fault"): fixture.callInt(1) def testJMethod_self(self): Fixture = JClass("jpype.common.Fixture") fixture = JClass("jpype.common.Fixture")() self.assertEqual(fixture.callInt.__self__, fixture) self.assertEqual(Fixture.callStaticInt.__self__, None) def testJMethod_name(self): fixture = JClass("jpype.common.Fixture")() self.assertIsInstance(fixture.callInt.__name__, str) self.assertEqual(fixture.callInt.__name__, 'callInt') def testJMethod_doc(self): fixture = JClass("jpype.common.Fixture")() self.assertIsInstance(fixture.callInt.__doc__, str) def testJMethod_annotations(self): fixture = JClass("jpype.common.Fixture")() self.assertIsInstance(fixture.callInt.__annotations__, dict) ann = fixture.callInt.__annotations__ expected = {'arg0': JInt, 'return': JInt} self.assertEqual(ann, expected) def testJMethod_closure(self): fixture = JClass("jpype.common.Fixture")() self.assertNotEqual(fixture.callInt.__closure__, None) def testJMethod_code(self): fixture = JClass("jpype.common.Fixture")() def f(): pass self.assertIsInstance(fixture.callInt.__code__, type(f.__code__)) def testJMethod_defaults(self): fixture = JClass("jpype.common.Fixture")() self.assertEqual(fixture.callInt.__defaults__, None) def testJMethod_kwdefaults(self): fixture = JClass("jpype.common.Fixture")() self.assertEqual(fixture.callInt.__kwdefaults__, None) def testJMethod_globals(self): fixture = JClass("jpype.common.Fixture")() self.assertIsInstance(fixture.callInt.__globals__, dict) def testJMethod_qualname(self): fixture = JClass("jpype.common.Fixture")() self.assertIsInstance(fixture.callInt.__qualname__, str) self.assertEqual(fixture.callInt.__qualname__, 'jpype.common.Fixture.callInt') def testMatches(self): js = JClass("java.lang.String")() self.assertFalse(js.substring._matches(object())) self.assertTrue(js.substring._matches(1)) self.assertTrue(js.substring._matches(1, 2)) self.assertFalse(js.substring._matches(1, 2, 3)) jpype-1.6.0/test/jpypetest/test_jobject.py000066400000000000000000000262341501674766700207360ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * from jpype import java import common try: import numpy as np except ImportError: pass class JClassTestCase(common.JPypeTestCase): """ Test for methods of JObject Should test: - ``__getattribute__`` against methods, fields, python methods, and python properties - ``__setattr__`` against fields, final fields, python private fields - ``class_`` property - ``mro`` """ def setUp(self): common.JPypeTestCase.setUp(self) self.fixture = JClass('jpype.common.Fixture')() def testSetAttrPythonField(self): cls = JClass('java.lang.String') obj = cls('foo') # Setting a private field on a Java class is allowed obj._allowed = 1 with self.assertRaises(AttributeError): # Setting a public field on a Java class is forbidden obj.forbidden = 1 def testSetAttrFinal(self): cls = JClass('java.lang.Long') obj = cls(1) with self.assertRaises(AttributeError): # Setting a final field is forbidden obj.SIZE = 1 def testClass(self): obj = JClass('java.lang.Long') clsType = JClass('java.lang.Class') # Get class must return a java.lang.Class instance belonging to the class self.assertIsInstance(obj.class_, clsType) self.assertEqual(obj.class_.getSimpleName(), "Long") def testGetAttrProperty(self): obj = JClass('java.lang.RuntimeException')('oo') value = obj.args self.assertEqual(value, ('oo',)) def testSetAttrProperty(self): obj = JClass('java.lang.RuntimeException')('oo') with self.assertRaises(AttributeError): obj.args = 1 def testAttrStaticField(self): self.fixture.static_object_field = "fred" self.assertEqual(self.fixture.static_object_field, "fred") def testGetAttrField(self): v = self.fixture.object_field def testSetAttrField(self): self.fixture.object_field = "fred" def testGetAttrPrivateField(self): with self.assertRaises(AttributeError): v = self.fixture.private_object_field def testSetAttrPrivateField(self): with self.assertRaises(AttributeError): self.fixture.private_object_field = "fred" def testGetAttrFinalField(self): v = self.fixture.final_object_field def testSetAttrFinalField(self): with self.assertRaises(AttributeError): self.fixture.final_object_field = "fred" def testGetAttrStaticFinalField(self): self.assertEqual(self.fixture.final_static_object_field, "final static object field") def testSetAttrStaticFinalField(self): with self.assertRaises(AttributeError): self.fixture.finalStaticObjectField = "bar" def testStaticMethod(self): self.fixture.callStaticObject(JObject()) def testPrivateStaticMethod(self): with self.assertRaises(AttributeError): self.fixture.callPrivateStaticObject(JObject()) def testMethod(self): self.fixture.callObject(JObject()) def testPrivateMethod(self): with self.assertRaises(AttributeError): self.fixture.callPrivateObject(JObject()) def testProtectedMethod(self): with self.assertRaises(AttributeError): self.fixture.callProtectedObject(JObject()) def testObjectBoolTrue(self): self.fixture.object_field = True self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Boolean')) self.assertEqual(str(self.fixture.object_field), str(True)) self.assertEqual(self.fixture.object_field, True) def testObjectBoolFalse(self): self.fixture.object_field = False self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Boolean')) self.assertEqual(str(self.fixture.object_field), str(False)) self.assertEqual(self.fixture.object_field, False) def testObjectBoolJValue(self): self.fixture.object_field = JBoolean(True) self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Boolean')) self.assertEqual(self.fixture.object_field, True) def testObjectShort(self): self.fixture.object_field = JShort(1) self.assertEqual(self.fixture.object_field, 1) self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Short')) def testObjectInteger(self): self.fixture.object_field = JInt(2) self.assertEqual(self.fixture.object_field, 2) self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Integer')) def testObjectLong(self): self.fixture.object_field = JLong(3) self.assertEqual(self.fixture.object_field, 3) self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Long')) def testObjectFloat(self): self.fixture.object_field = JFloat(1.125) self.assertEqual(self.fixture.object_field, 1.125) self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Float')) def testObjectDouble(self): self.fixture.object_field = JDouble(2.6125) self.assertEqual(self.fixture.object_field, 2.6125) self.assertIsInstance(self.fixture.object_field, JClass('java.lang.Double')) def testObjectField(self): with self.assertRaises(TypeError): self.fixture.object_field = object() with self.assertRaises(TypeError): self.fixture.static_object_field = object() def testArraySetRangeFail(self): ja = JArray(JObject)(4) with self.assertRaises(TypeError): ja[:] = [1, 2, object(), 3] ja[:] = [1, 2, 3, 4] @common.requireInstrumentation def testArraySetRangeFault(self): _jpype.fault("JPClass::setArrayRange") ja = JArray(JObject)(4) with self.assertRaisesRegex(SystemError, "fault"): ja[:] = [1, 2, 3, 4] def testAssignClass(self): self.fixture.object_field = JClass("java.lang.StringBuilder") self.assertIsInstance(self.fixture.object_field, jpype.java.lang.Class) self.assertEqual(self.fixture.object_field, JClass("java.lang.StringBuilder")) @common.requireInstrumentation def testSetFinalField(self): _jpype.fault("JPField::setStaticAttribute") with self.assertRaisesRegex(SystemError, "fault"): self.fixture.static_object_field = None def testHashNone(self): self.assertEqual(hash(JObject(None)), hash(None)) def testStrPrimitive(self): with self.assertRaisesRegex(TypeError, "requires a Java object"): _jpype._JObject.__str__(JInt(1)) def testGetAttrFail(self): jo = JClass("java.lang.Object")() with self.assertRaisesRegex(TypeError, "must be string"): getattr(jo, object()) def testSetAttrFail(self): jo = JClass("java.lang.Object")() with self.assertRaisesRegex(TypeError, "must be string"): setattr(jo, object(), 1) def testSetAttrFail2(self): fixture = JClass("jpype.common.Fixture")() with self.assertRaisesRegex(AttributeError, "is not settable"): setattr(fixture, "callObject", 4) def testJavaPrimitives(self): self.assertIsInstance( self.fixture.callObject(JByte(1)), java.lang.Byte) self.assertIsInstance( self.fixture.callObject(JShort(1)), java.lang.Short) self.assertIsInstance( self.fixture.callObject(JInt(1)), java.lang.Integer) self.assertIsInstance( self.fixture.callObject(JLong(1)), java.lang.Long) self.assertIsInstance( self.fixture.callObject(JFloat(1)), java.lang.Float) self.assertIsInstance(self.fixture.callObject( JDouble(1)), java.lang.Double) def testPythonPrimitives(self): self.assertIsInstance(self.fixture.callObject(1), java.lang.Long) self.assertIsInstance(self.fixture.callObject(1.0), java.lang.Double) @common.requireNumpy def testNumpyPrimitives(self): self.assertIsInstance( self.fixture.callObject(np.int8(1)), java.lang.Byte) self.assertIsInstance(self.fixture.callObject( np.int16(1)), java.lang.Short) self.assertIsInstance(self.fixture.callObject( np.int32(1)), java.lang.Integer) self.assertIsInstance(self.fixture.callObject( np.int64(1)), java.lang.Long) self.assertIsInstance(self.fixture.callObject( np.float32(1)), java.lang.Float) self.assertIsInstance(self.fixture.callObject( np.float64(1)), java.lang.Double) def testCompare(self): jo = JClass("java.lang.Object")() with self.assertRaises(TypeError): jo < 0 with self.assertRaises(TypeError): jo <= 0 with self.assertRaises(TypeError): jo > 0 with self.assertRaises(TypeError): jo >= 0 def testCompareNull(self): jo = JClass("java.lang.Object") jv = JObject(None, jo) self.assertEqual(jv, None) self.assertEqual(None, jv) self.assertNotEqual(JInt(1), jv) self.assertNotEqual(jv, JInt(1)) def testRepr(self): jo = JClass("java.lang.Object") jv = jo() jvn = JObject(None, jo) self.assertIsInstance(repr(jv), str) self.assertIsInstance(repr(jvn), str) self.assertEqual(repr(jv), "") self.assertEqual(repr(jvn), "") def testDeprecated(self): # this one should issue a warning jo = JClass("java.lang.Object") with self.assertWarns(DeprecationWarning): self.assertIsInstance(JObject(None, object), jo) def testGetSetBad(self): JS = JClass("java.lang.String") js = JS() with self.assertRaises(TypeError): JS.__getattribute__(js, object()) with self.assertRaises(TypeError): setattr(js, object(), 1) def testGetSetBad_2(self): jo = JClass("java.lang.Object")() self.assertTrue(jo != JInt(0)) self.assertFalse(jo == JInt(0)) self.assertTrue(JInt(0) != jo) self.assertFalse(JInt(0) == jo) jpype-1.6.0/test/jpypetest/test_jpackage.py000066400000000000000000000044711501674766700210620ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * from jpype import JPackage import common class JPackageTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.jl = JPackage('java.lang') def testCreate(self): self.assertEqual(self.jl.String, JClass('java.lang.String')) def testFail0(self): with self.assertRaises(TypeError): JPackage(1) # def testFail1(self): # #with self.assertRaises(RuntimeError): # jl = JPackage('java.nosuch') def testFail2(self): with self.assertRaises(AttributeError): self.jl.NoSuch def testFail3(self): with self.assertRaises(AttributeError): self.jl.bar def testStr(self): self.assertIsInstance(str(self.jl), str) self.assertEqual(str(self.jl), "java.lang") def testRepr(self): self.assertIsInstance(repr(self.jl), str) self.assertEqual(repr(self.jl), "") def testCall(self): with self.assertRaises(TypeError): self.jl() def testDir(self): self.assertIsInstance(dir(self.jl), list) def testGetAttr(self): with self.assertRaises(TypeError): self.jl.__getattribute__(object()) def testSetAttr(self): with self.assertRaises(TypeError): self.jl.__setattr__(object(), 1) def testInvalid(self): JL = JPackage("java.lng") with self.assertRaisesRegex(AttributeError, "Java package 'java.lng' is not valid"): getattr(JL, "foo") jpype-1.6.0/test/jpypetest/test_jshort.py000066400000000000000000000462641501674766700206340ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common import random import _jpype import jpype from jpype import java from jpype.types import * try: import numpy as np except ImportError: pass VALUES = [random.randint(-2**15, 2**15 - 1) for i in range(10)] class JShortTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.cls = JClass("jpype.common.Fixture") self.fixture = self.cls() @common.requireInstrumentation def testJPNumberLong_int(self): jd = JShort(1) _jpype.fault("PyJPNumberLong_int") with self.assertRaisesRegex(SystemError, "fault"): int(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): int(jd) int(jd) @common.requireInstrumentation def testJPNumberLong_float(self): jd = JShort(1) _jpype.fault("PyJPNumberLong_float") with self.assertRaisesRegex(SystemError, "fault"): float(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): float(jd) float(jd) @common.requireInstrumentation def testJPNumberLong_str(self): jd = JShort(1) _jpype.fault("PyJPNumberLong_str") with self.assertRaisesRegex(SystemError, "fault"): str(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(jd) str(jd) @common.requireInstrumentation def testJPNumberLong_repr(self): jd = JShort(1) _jpype.fault("PyJPNumberLong_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): repr(jd) repr(jd) @common.requireInstrumentation def testJPNumberLong_compare(self): jd = JShort(1) _jpype.fault("PyJPNumberLong_compare") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): jd == 1 jd == 1 @common.requireInstrumentation def testJPNumberLong_hash(self): jd = JShort(1) _jpype.fault("PyJPNumberLong_hash") with self.assertRaises(SystemError): hash(jd) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(jd) hash(jd) @common.requireInstrumentation def testFault(self): _jpype.fault("JPShortType::findJavaConversion") with self.assertRaises(SystemError): JShort(1.0) @common.requireInstrumentation def testConversionFault(self): _jpype.fault("JPShortType::findJavaConversion") with self.assertRaisesRegex(SystemError, "fault"): JShort._canConvertToJava(object()) @common.requireInstrumentation def testArrayFault(self): ja = JArray(JShort)(5) _jpype.fault("JPJavaFrame::NewShortArray") with self.assertRaisesRegex(SystemError, "fault"): JArray(JShort)(1) _jpype.fault("JPJavaFrame::SetShortArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): ja[0] = 0 _jpype.fault("JPJavaFrame::GetShortArrayRegion") with self.assertRaisesRegex(SystemError, "fault"): print(ja[0]) _jpype.fault("JPJavaFrame::GetShortArrayElements") # Special case, only BufferError is allowed from getBuffer with self.assertRaises(BufferError): memoryview(ja[0:3]) _jpype.fault("JPJavaFrame::ReleaseShortArrayElements") with self.assertRaisesRegex(SystemError, "fault"): ja[0:3] = bytes([1, 2, 3]) _jpype.fault("JPJavaFrame::ReleaseShortArrayElements") with self.assertRaisesRegex(SystemError, "fault"): jpype.JObject(ja[::2], jpype.JObject) _jpype.fault("JPJavaFrame::ReleaseShortArrayElements") def f(): # Special case no fault is allowed memoryview(ja[0:3]) f() _jpype.fault("JPShortType::setArrayRange") with self.assertRaisesRegex(SystemError, "fault"): ja[1:3] = [0, 0] def testFromJShortWiden(self): self.assertEqual(JShort(JByte(123)), 123) self.assertEqual(JShort(JShort(12345)), 12345) self.assertEqual(JShort(JInt(12345678)), JShort(12345678)) self.assertEqual(JShort(JLong(12345678)), JShort(12345678)) def testFromNone(self): with self.assertRaises(TypeError): JShort(None) self.assertEqual(JShort._canConvertToJava(None), "none") def testUnBox(self): self.assertEqual(JShort(java.lang.Double(1.2345)), 1) def testFromFloat(self): self.assertEqual(JShort._canConvertToJava(1.2345), "explicit") def testFromLong(self): self.assertEqual(JShort(12345), 12345) self.assertEqual(JShort._canConvertToJava(12345), "implicit") def testFromObject(self): with self.assertRaises(TypeError): JShort(object()) with self.assertRaises(TypeError): JShort(JObject()) with self.assertRaises(TypeError): JShort(JString("A")) self.assertEqual(JShort._canConvertToJava(object()), "none") ja = JArray(JShort)(5) with self.assertRaises(TypeError): ja[1] = object() jf = JClass("jpype.common.Fixture") with self.assertRaises(TypeError): jf.static_short_field = object() with self.assertRaises(TypeError): jf().short_field = object() def testCallFloatFromNone(self): with self.assertRaises(TypeError): self.fixture.callShort(None) with self.assertRaises(TypeError): self.fixture.static_short_field = None with self.assertRaises(TypeError): self.fixture.short_field = None def testThrow(self): # Check throw with self.assertRaises(JException): self.fixture.throwFloat() with self.assertRaises(JException): self.cls.throwStaticFloat() with self.assertRaises(JException): self.fixture.throwStaticFloat() def checkType(self, q): # Check field self.fixture.short_field = q self.assertEqual(self.fixture.short_field, q) self.assertEqual(self.fixture.getShort(), q) # Check static field self.cls.static_short_field = q self.assertEqual(self.fixture.static_short_field, q) self.assertEqual(self.fixture.getStaticShort(), q) self.assertEqual(self.cls.getStaticShort(), q) # Check call self.assertEqual(self.fixture.callShort(q), q) self.assertEqual(self.cls.callStaticShort(q), q) def checkTypeFail(self, q, exc=TypeError): with self.assertRaises(exc): self.fixture.short_field = q with self.assertRaises(exc): self.fixture.callShort(q) with self.assertRaises(exc): self.fixture.callStaticShort(q) def testCastFloat(self): self.fixture.short_field = JShort(6.0) self.assertEqual(self.fixture.short_field, 6) def testCheckInt(self): self.checkType(1) def testCheckFloat(self): self.checkTypeFail(2.0) def testCheckRange(self): self.checkType(2**15 - 1) self.checkType(-2**15) self.checkTypeFail(2**15, exc=OverflowError) self.checkTypeFail(-2**15 - 1, exc=OverflowError) def testExplicitRange(self): # These will not overflow as they are explicit casts self.assertEqual(JShort(2**16), 0) self.assertEqual(JShort(-2**16), 0) def testCheckBool(self): self.checkType(True) self.checkType(False) def testCheckJBoolean(self): self.checkTypeFail(JBoolean(True)) self.checkTypeFail(JBoolean(False)) def testCheckJChar(self): self.checkType(JChar("A")) def testCheckJByte(self): self.checkType(JByte(-128)) self.checkType(JByte(127)) def testCheckJShort(self): self.checkType(JShort(-2**15)) self.checkType(JShort(2**15 - 1)) def testCheckJInt(self): self.checkTypeFail(JInt(-2**31 + 1)) self.checkTypeFail(JInt(2**31 - 1)) def testCheckJLong(self): self.checkTypeFail(JLong(-2**63 + 1)) self.checkTypeFail(JLong(2**63 - 1)) @common.requireNumpy def testCheckNumpyInt8(self): self.checkType(np.random.randint(-127, 128, dtype=np.int8)) self.checkType(np.random.randint(0, 255, dtype=np.uint8)) self.checkType(np.uint8(0)) self.checkType(np.uint8(255)) self.checkType(np.int8(-128)) self.checkType(np.int8(127)) @common.requireNumpy def testCheckNumpyInt16(self): self.checkType(np.random.randint(-2**15, 2**15 - 1, dtype=np.int16)) self.checkType(np.uint16(0)) self.checkTypeFail(np.uint16(2**16 - 1), exc=OverflowError) self.checkType(np.int16(-2**15)) self.checkType(np.int16(2**15 - 1)) @common.requireNumpy def testCheckNumpyInt32(self): self.checkType(np.uint32(0)) self.checkTypeFail(np.uint32(2**32 - 1), exc=OverflowError) self.checkTypeFail(np.int32(-2**31), exc=OverflowError) self.checkTypeFail(np.int32(2**31 - 1), exc=OverflowError) @common.requireNumpy def testCheckNumpyInt64(self): #self.checkTypeFail(np.random.randint(-2**63,2**63-1, dtype=np.int64)) # FIXME OverflowError #self.checkType(np.uint64(np.random.randint(0,2**64-1, dtype=np.uint64))) # FIXME OverflowError # self.checkType(np.uint64(2**64-1)) self.checkTypeFail(np.int64(-2**63), exc=OverflowError) self.checkTypeFail(np.int64(2**63 - 1), exc=OverflowError) @common.requireNumpy def testCheckNumpyFloat32(self): self.checkTypeFail(np.float32(np.random.rand())) @common.requireNumpy def testCheckNumpyFloat64(self): self.checkTypeFail(np.float64(np.random.rand())) def checkArrayType(self, a, expected): # Check init ja = JArray(JShort)(a) self.assertElementsEqual(ja, expected) ja = JArray(JShort)(len(a)) ja[:] = a self.assertElementsEqual(ja, expected) return ja def checkArrayTypeFail(self, a): # Check init ja = JArray(JShort)(a) ja = JArray(JShort)(len(a)) ja[:] = a def testArrayConversion(self): a = [random.randint(-2**15, 2**15 - 1) for i in range(100)] jarr = self.checkArrayType(a, a) result = jarr[2:10] self.assertEqual(len(a[2:10]), len(result)) self.assertElementsAlmostEqual(a[2:10], result) # empty slice result = jarr[-1:3] expected = a[-1:3] self.assertElementsAlmostEqual(expected, result) result = jarr[3:-2] expected = a[3:-2] self.assertElementsAlmostEqual(expected, result) @common.requireNumpy def testArrayInitFromNPInt(self): a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int_) self.checkArrayType(a, a.astype(np.int16)) @common.requireNumpy def testArrayInitFromNPInt8(self): a = np.random.randint(-2**7, 2**7 - 1, size=100, dtype=np.int8) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt16(self): a = np.random.randint(-2**15, 2**15 - 1, size=100, dtype=np.int16) self.checkArrayType(a, a) @common.requireNumpy def testArrayInitFromNPInt32(self): a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int32) self.checkArrayType(a, a.astype(np.int16)) @common.requireNumpy def testArrayInitFromNPInt64(self): a = np.random.randint(-2**63, 2**63 - 1, size=100, dtype=np.int64) self.checkArrayType(a, a.astype(np.int16)) @common.requireNumpy def testArrayInitFromNPFloat32(self): a = np.random.random(100).astype(np.float32) self.checkArrayType(a, a.astype(np.int16)) @common.requireNumpy def testArrayInitFromNPFloat64(self): a = np.random.random(100).astype(np.float64) self.checkArrayType(a, a.astype(np.int16)) def testArraySetRange(self): ja = JArray(JShort)(3) ja[0:1] = [123] self.assertEqual(ja[0], 123) ja[0:1] = [-1] self.assertEqual(ja[0], -1) with self.assertRaises(TypeError): ja[0:1] = [1.000] with self.assertRaises(TypeError): ja[0:1] = [java.lang.Double(321)] with self.assertRaises(TypeError): ja[0:1] = [object()] def testArrayConversionFail(self): jarr = JArray(JShort)(VALUES) with self.assertRaises(TypeError): jarr[1] = 'a' def testArraySliceLength(self): jarr = JArray(JShort)(VALUES) jarr[1:2] = [1] with self.assertRaises(ValueError): jarr[1:2] = [1, 2, 3] def testArrayConversionInt(self): jarr = JArray(JShort)(VALUES) result = jarr[0: len(jarr)] self.assertElementsEqual(VALUES, result) result = jarr[2:10] self.assertElementsEqual(VALUES[2:10], result) def testArrayConversionError(self): jarr = JArray(JShort, 1)(10) with self.assertRaises(TypeError): jarr[1:2] = [dict()] # -1 is returned by python, if conversion fails also, ensure this works jarr[1:2] = [-1] def testArrayClone(self): array = JArray(JShort, 2)([[1, 2], [3, 4]]) carray = array.clone() # Verify the first dimension is cloned self.assertFalse(array.equals(carray)) # Copy is shallow self.assertTrue(array[0].equals(carray[0])) def testArrayGetSlice(self): contents = VALUES array = JArray(JShort)(contents) self.assertEqual(list(array[1:]), contents[1:]) self.assertEqual(list(array[:-1]), contents[:-1]) self.assertEqual(list(array[1:-1]), contents[1:-1]) def testArraySetSlice(self): contents = [1, 2, 3, 4] array = JArray(JShort)(contents) array[1:] = [5, 6, 7] contents[1:] = [5, 6, 7] self.assertEqual(list(array[:]), contents[:]) array[:-1] = [8, 9, 10] contents[:-1] = [8, 9, 10] self.assertEqual(list(array[:]), contents[:]) def testArrayGetSliceStep(self): contents = VALUES array = JArray(JShort)(contents) self.assertEqual(list(array[::2]), contents[::2]) self.assertEqual(list(array[::3]), contents[::3]) self.assertEqual(list(array[::4]), contents[::4]) self.assertEqual(list(array[::5]), contents[::5]) self.assertEqual(list(array[::6]), contents[::6]) self.assertEqual(list(array[::7]), contents[::7]) self.assertEqual(list(array[::8]), contents[::8]) self.assertEqual(list(array[1::3]), contents[1::3]) self.assertEqual(list(array[1:-2:3]), contents[1:-2:3]) def testArraySliceStepNeg(self): contents = VALUES array = JArray(JShort)(contents) self.assertEqual(list(array[::-1]), contents[::-1]) self.assertEqual(list(array[::-2]), contents[::-2]) self.assertEqual(list(array[::-3]), contents[::-3]) self.assertEqual(list(array[::-4]), contents[::-4]) self.assertEqual(list(array[::-5]), contents[::-5]) self.assertEqual(list(array[::-6]), contents[::-6]) self.assertEqual(list(array[2::-3]), contents[2::-3]) self.assertEqual(list(array[-2::-3]), contents[-2::-3]) def testArraySetArraySliceStep(self): contents = [1, 2, 3, 4, 5, 6] array = JArray(JShort)(contents) array[::2] = [5, 6, 7] contents[::2] = [5, 6, 7] self.assertEqual(list(array[:]), contents[:]) def testArrayEquals(self): contents = VALUES array = JArray(JShort)(contents) array2 = JArray(JShort)(contents) self.assertEqual(array, array) self.assertNotEqual(array, array2) def testArrayIter(self): contents = VALUES array = JArray(JShort)(contents) contents2 = [i for i in array] self.assertEqual(contents, contents2) def testArrayGetOutOfBounds(self): contents = [1, 2, 3, 4] array = JArray(JShort)(contents) with self.assertRaises(IndexError): array[5] self.assertEqual(array[-1], contents[-1]) self.assertEqual(array[-4], contents[-4]) with self.assertRaises(IndexError): array[-5] def testArraySetOutOfBounds(self): contents = [1, 2, 3, 4] array = JArray(JShort)(contents) with self.assertRaises(IndexError): array[5] = 1 array[-1] = 5 contents[-1] = 5 array[-4] = 6 contents[-4] = 6 self.assertEqual(list(array[:]), contents) with self.assertRaises(IndexError): array[-5] = 1 def testArraySliceCast(self): JA = JArray(JShort) ja = JA(VALUES) ja2 = ja[::2] jo = jpype.JObject(ja2, jpype.JObject) ja3 = jpype.JObject(jo, JA) self.assertEqual(type(jo), jpype.JClass("java.lang.Object")) self.assertEqual(type(ja2), JA) self.assertEqual(type(ja3), JA) self.assertEqual(list(ja2), list(ja3)) def testArrayReverse(self): n = list(VALUES) ja = JArray(JShort)(n) a = [i for i in reversed(ja)] n = [i for i in reversed(n)] self.assertEqual(a, n) def testArrayHash(self): ja = JArray(JShort)([1, 2, 3]) self.assertIsInstance(hash(ja), int) @common.requireNumpy def testArrayBufferDims(self): ja = JArray(JShort)(5) a = np.zeros((5, 2)) with self.assertRaisesRegex(TypeError, "incorrect"): ja[:] = a def testArrayBadItem(self): class q(object): def __int__(self): raise SystemError("nope") def __index__(self): raise SystemError("nope") ja = JArray(JShort)(5) a = [1, 2, q(), 3, 4] with self.assertRaisesRegex(SystemError, "nope"): ja[:] = a def testArrayBadDims(self): class q(bytes): # Lie about our length def __len__(self): return 5 a = q([1, 2, 3]) ja = JArray(JShort)(5) with self.assertRaisesRegex(ValueError, "Slice"): ja[:] = [1, 2, 3] with self.assertRaisesRegex(ValueError, "mismatch"): ja[:] = a jpype-1.6.0/test/jpypetest/test_jstring.py000066400000000000000000000127221501674766700207730ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype.types import * import common class JStringTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testAddself(self): a = jpype.JString("abc") a = a + "def" self.assertEqual(a, "abcdef") def testEq(self): a = jpype.JString("abc") b = jpype.JClass("java.lang.String")("abc") self.assertEqual(a, b) def testEqPy(self): a = jpype.JString("abc") self.assertEqual(a, "abc") def testNotEq(self): a = jpype.JString("abc") b = jpype.JClass("java.lang.String")("def") self.assertNotEqual(a, b) def testNotEqPy(self): a = jpype.JString("abc") self.assertNotEqual(a, "def") def testLen(self): a = jpype.JString("abc") self.assertEqual(len(a), 3) def testGetItem(self): a = jpype.JString("abc") self.assertEqual(a[0], 'a') self.assertEqual(a[1], 'b') self.assertEqual(a[2], 'c') self.assertEqual(a[-1], 'c') self.assertEqual(a[-2], 'b') self.assertEqual(a[-3], 'a') with self.assertRaises(IndexError): x = a[3] with self.assertRaises(IndexError): x = a[-4] def testLt(self): a = jpype.JString("abc") b = jpype.JString("def") self.assertTrue(a < b) self.assertFalse(b < a) self.assertFalse(b < "def") self.assertTrue(a < "def") def testLe(self): a = jpype.JString("abc") b = jpype.JString("def") self.assertTrue(a <= a) self.assertTrue(a <= b) self.assertFalse(b <= a) self.assertTrue(b <= "def") self.assertTrue(a <= "def") def testGt(self): a = jpype.JString("abc") b = jpype.JString("def") self.assertFalse(a < a) self.assertTrue(a < b) self.assertFalse(b < a) self.assertFalse(b < "def") self.assertTrue(a < "def") def testGe(self): a = jpype.JString("abc") b = jpype.JString("def") self.assertTrue(a >= a) self.assertFalse(a >= b) self.assertTrue(b >= a) self.assertTrue(b >= "def") self.assertFalse(a >= "def") def testContains(self): a = jpype.JString("abc") self.assertTrue("ab" in a) self.assertFalse("cd" in a) def testHash(self): a = jpype.JString("abc") self.assertEqual(hash(a), hash("abc")) def testRepr(self): a = jpype.JString("abc") self.assertEqual(repr(a), "'abc'") def testConversion(self): self.assertEqual(jpype.JString("AAAA"), "AAAA") self.assertEqual(jpype.JString(bytes([65, 65, 65, 65])), "AAAA") def testStringDictKey1(self): d = dict() d['foo'] = 'a' self.assertEqual(d[JString('foo')], 'a') def testStringDictKey2(self): d = dict() d[JString('foo')] = 'a' self.assertEqual(d['foo'], 'a') def testStringDictKey3(self): d = dict() d[JString('foo')] = 'a' self.assertEqual(d[JString('foo')], 'a') def testNullString(self): self.assertEqual(str(JObject(None, JString)), "null") def testNullCompare(self): jsn = JObject(None, JString) self.assertFalse("a" == jsn) self.assertTrue("a" != jsn) self.assertFalse(jsn == "a") self.assertTrue(jsn != "a") self.assertTrue(jsn == jsn) self.assertFalse(jsn != jsn) with self.assertRaises(ValueError): jsn < "a" with self.assertRaises(ValueError): jsn <= "a" with self.assertRaises(ValueError): jsn > "a" with self.assertRaises(ValueError): jsn >= "a" with self.assertRaises(ValueError): "a" < jsn with self.assertRaises(ValueError): "a" <= jsn with self.assertRaises(ValueError): "a" > jsn with self.assertRaises(ValueError): "a" >= jsn def testNullAdd(self): jsn = JObject(None, JString) with self.assertRaises(ValueError): jsn + "a" def testNullHash(self): jsn = JObject(None, JString) self.assertEqual(hash(jsn), hash(None)) def testEmptyHash(self): jsn = JObject('', JString) self.assertEqual(hash(jsn), hash('')) def testSlice(self): s = 'abcdefghijklmnop' s2 = JString(s) self.assertEqual(s[:], s2[:]) self.assertEqual(s[1:5], s2[1:5]) self.assertEqual(s[:5], s2[:5]) self.assertEqual(s[3:], s2[3:]) self.assertEqual(s[::-1], s2[::-1]) def testBad(self): bad = jpype.JClass("jpype.str.Bad")() self.assertEqual(str(bad), "null") jpype-1.6.0/test/jpypetest/test_jvmfinder.py000066400000000000000000000127431501674766700213020ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** # part of JPype1; author Martin K. Scherer; 2014 # from unittest import mock import os import pathlib import sys import unittest from unittest import mock from jpype._jvmfinder import (LinuxJVMFinder, JVMNotSupportedException, DarwinJVMFinder, WindowsJVMFinder) class JVMFinderTest(unittest.TestCase): """ test some methods to obtain a jvm. TODO: add windows (need to mock registry) """ def test_find_libjvm(self): """ test JVMFinder.find_libjvm does not return broken jvm implementation. """ walk_fake = [('jre', ('lib',), ()), ('jre/lib', ('amd64',), ()), ('jre/lib/amd64', # cacoa and jamvm are not supported. ('cacao', 'jamvm', 'server'), ()), ('jre/lib/amd64/cacao', ('',), ('libjvm.so',)), ('jre/lib/amd64/jamvm', ('',), ('libjvm.so',)), ('jre/lib/amd64/server', ('',), ('libjvm.so',)), ] with mock.patch('os.walk') as mockwalk: # contains broken and working jvms mockwalk.return_value = walk_fake finder = LinuxJVMFinder() p = finder.find_libjvm('arbitrary java home') # cacoa and javmvm should not be found self.assertEqual( p, os.path.join('jre/lib/amd64/server', 'libjvm.so'), 'wrong jvm returned') def test_find_libjvm_unsupported_jvm(self): """Checks for the case only unsupported JVM impls are being found.""" walk_fake = [('jre', ('lib',), ()), ('jre/lib', ('amd64',), ()), ('jre/lib/amd64', # cacoa and jamvm are not supported. ('cacao', 'jamvm', ), ()), ('jre/lib/amd64/cacao', ('',), ('libjvm.so',)), ('jre/lib/amd64/jamvm', ('',), ('libjvm.so',)), ] with mock.patch('os.walk') as mockwalk: # contains broken and working jvms mockwalk.return_value = walk_fake finder = LinuxJVMFinder() with self.assertRaises(JVMNotSupportedException): finder.find_libjvm('arbitrary java home') @mock.patch('os.walk') @mock.patch('os.path.exists') @mock.patch('os.path.realpath') def test_get_from_bin(self, mock_real_path, mock_path_exists, mock_os_walk): """ test _get_from_bin method (used in linux and darwin) '/usr/bin/java' => some jre/jdk path """ java_path = '/usr/lib/jvm/java-6-openjdk-amd64/bin/java' mock_os_walk.return_value = [ ('/usr/lib/jvm/java-6-openjdk-amd64/jre/lib/amd64/server', ('',), ('libjvm.so',))] mock_path_exists.return_value = True mock_real_path.return_value = '/usr/lib/jvm/java-6-openjdk-amd64/bin/java' finder = LinuxJVMFinder() p = finder._get_from_bin() self.assertEqual( p, os.path.join('/usr/lib/jvm/java-6-openjdk-amd64/jre/lib/amd64/server', 'libjvm.so')) @mock.patch('platform.mac_ver') def testDarwinBinary(self, mock_mac_ver): # this version has java_home binary mock_mac_ver.return_value = ('10.6.8', '', '') expected = '/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home\n' finder = DarwinJVMFinder() # fake check_output with mock.patch('subprocess.check_output') as mock_checkoutput: mock_checkoutput.return_value = expected p = finder._javahome_binary() self.assertEqual(p, expected.strip()) # this version has no java_home binary mock_mac_ver.return_value = ('10.5', '', '') p = finder._javahome_binary() self.assertEqual(p, None) def testLinuxGetFromBin(self): finder = LinuxJVMFinder() def f(s): return s with mock.patch('os.path') as pathmock, \ mock.patch('jpype._jvmfinder.JVMFinder.find_libjvm') as mockfind: pathmock.exists.return_value = True pathmock.realpath = f pathmock.abspath = f pathmock.dirname = os.path.dirname pathmock.join = os.path.join finder._get_from_bin() self.assertEqual( pathmock.dirname.mock_calls[0][1], (finder._java,)) @unittest.skipIf(sys.platform != "win", "only on windows") def testWindowsRegistry(self): finder = WindowsJVMFinder() jvm_home = pathlib.Path(finder.get_jvm_path()) assert jvm_home.exists() assert jvm_home.is_dir() if __name__ == '__main__': unittest.main() jpype-1.6.0/test/jpypetest/test_keywords.py000066400000000000000000000037051501674766700211630ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import keyword # From the standard library. import pytest import jpype import common @pytest.mark.parametrize( "identifier", list(keyword.kwlist) + [ '__del__', # Print is no longer a keyword in Python 3, but still protected to # avoid API breaking in JPype v1. 'print', '____', # Not allowed. ] ) def testPySafe__Keywords(identifier): safe = jpype._pykeywords.pysafe(identifier) if identifier.startswith("__"): assert safe is None else: assert isinstance(safe, str), f"Fail on keyword {identifier}" assert safe.endswith("_") @pytest.mark.parametrize( "identifier", [ 'notSpecial', '__notSpecial', 'notSpecial__', '_notSpecial_', '_not__special_', '__', '___', # Technically these are fine. ]) def testPySafe__NotKeywords(identifier): safe = jpype._pykeywords.pysafe(identifier) assert safe == identifier class AttributeTestCase(common.JPypeTestCase): def testPySafe(self): cls = jpype.JPackage("jpype").attr.TestKeywords self.assertTrue(hasattr(cls, "__leading_double_underscore")) self.assertFalse(hasattr(cls, "__dunder_name__")) jpype-1.6.0/test/jpypetest/test_lambdas.py000066400000000000000000000032031501674766700207100ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class LambdasTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) try: self.lambdas = jpype.JClass("jpype.lambda.Test1")() return except: pass raise common.unittest.SkipTest def testLambdasFunction(self): self.assertEqual(self.lambdas.getFunction().apply(1.0), 2.0) def testLambdasLambda(self): self.assertEqual(self.lambdas.getLambda().apply(1.0), 2.0) def testLambdasStr(self): func = self.lambdas.getFunction() self.assertIsInstance(str(func), str) def testLambdasEquals(self): func = self.lambdas.getFunction() self.assertEqual(func, func) def testLambdasNoteEquals(self): func = self.lambdas.getFunction() func2 = self.lambdas.getFunction() self.assertNotEqual(func, func2) jpype-1.6.0/test/jpypetest/test_leak.py000066400000000000000000000127561501674766700202360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import gc import sys from os import path import subrun import unittest try: import resource except ImportError: resource = None # type: ignore[assignment] def haveResource(): return resource is not None def hasRefCount(): try: sys.getrefcount(sys) return True except: return False class LeakChecker: def __init__(self): self.runtime = jpype.java.lang.Runtime.getRuntime() def memory_usage_resource(self): # The Python docs aren't clear on what the units are exactly, but # the Mac OS X man page for `getrusage(2)` describes the units as bytes. # The Linux man page isn't clear, but it seems to be equivalent to # the information from `/proc/self/status`, which is in kilobytes. if sys.platform == 'darwin': rusage_mp = 1 else: rusage_mp = 1024 return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * rusage_mp def freeResources(self): self.runtime.gc() # Garbage collect Java gc.collect() # Garbage collect Python? rss_memory = self.memory_usage_resource() jvm_total_mem = self.runtime.totalMemory() jvm_free_mem = self.runtime.freeMemory() num_gc_objects = len(gc.get_objects()) return (rss_memory, jvm_total_mem, jvm_free_mem) def memTest(self, func, size): # Returns true if there may be a leak # Note, some growth is possible due to loading of objects and classes, # Thus we will run it a few times to check the growth rate. rss_memory = list() jvm_total_mem = list() jvm_free_mem = list() grow0 = list() grow1 = list() (rss_memory0, jvm_total_mem0, jvm_free_mem0) = self.freeResources() success = 0 for j in range(10): for i in range(size): func() (rss_memory1, jvm_total_mem1, jvm_free_mem1) = self.freeResources() rss_memory.append(rss_memory1) jvm_total_mem.append(jvm_total_mem1) jvm_free_mem.append(jvm_free_mem1) growth0 = (rss_memory1 - rss_memory0) / (float(size)) growth1 = (jvm_total_mem1 - jvm_total_mem0) / (float(size)) rss_memory0 = rss_memory1 jvm_total_mem0 = jvm_total_mem1 jvm_free_mem0 = jvm_total_mem1 grow0.append(growth0) grow1.append(growth1) if (growth0 < 0) or (growth1 < 0): continue if (growth0 < 4) and (growth1 < 4): success += 1 if success > 3: return False print() for i in range(len(grow0)): print(' Pass%d: %f %f - %d %d %d' % (i, grow0[i], grow1[i], rss_memory[i], jvm_total_mem[i], jvm_free_mem[i])) print() return True @subrun.TestCase(individual=True) class LeakTestCase(unittest.TestCase): def setUp(self): root = path.dirname(path.abspath(path.dirname(__file__))) jpype.addClassPath(path.join(root, 'classes')) jvm_path = jpype.getDefaultJVMPath() classpath_arg = "-Djava.class.path=%s" classpath_arg %= jpype.getClassPath() jpype.startJVM(jvm_path, "-ea", # "-Xcheck:jni", "-Xmx256M", "-Xms16M", classpath_arg) def assertNotLeaky(self, function, counts=5000): lc = LeakChecker() assert not lc.memTest(function, counts), 'Potential leak found' @unittest.skipUnless(haveResource(), "resource not available") def testStringLeak(self): def stringFunc(): jpype.java.lang.String('aaaaaaaaaaaaaaaaa') self.assertNotLeaky(stringFunc) @unittest.skipUnless(haveResource(), "resource not available") def testStringLeak__str__(self): def stringFunc(): # Casting to a string includes a cache stage, we want to make sure # that the cache is tidying up properly. s = jpype.types.JString('aaaaaaaaaaaaaaaaa') str(s) self.assertNotLeaky(stringFunc) @unittest.skipUnless(haveResource(), "resource not available") def testClassLeak(self): def classFunc(): cls = jpype.JClass('java.lang.String') self.assertNotLeaky(classFunc) @unittest.skipUnless(haveResource(), "resource not available") def testCtorLeak(self): cls = jpype.JClass("java.lang.String") def func(): cls("test") self.assertNotLeaky(func) @unittest.skipUnless(haveResource(), "resource not available") def testInvokeLeak(self): jstr = jpype.JString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa") def func(): jstr.getBytes() self.assertNotLeaky(func) jpype-1.6.0/test/jpypetest/test_leak2.py000066400000000000000000000053411501674766700203100ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # *****************************************************************************import _jpype import jpype from jpype.types import * import sys import logging import time import common class Tracer(object): ctor = 0 dtor = 0 def __init__(self): Tracer.ctor += 1 def __del__(self): Tracer.dtor += 1 @staticmethod def reset(): Tracer.ctor = 0 Tracer.dtor = 0 @staticmethod def leaked(): return Tracer.ctor - Tracer.dtor @staticmethod def attach(obj): setattr(obj, "_trace", Tracer()) # This test finds reference counting leak by attaching an # object that lives as long as the wrapper is alive. # It can't detect Java reference counter leaks. class Leak2TestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.fixture = JClass('jpype.common.Fixture')() Tracer.reset() def testArrayCall(self): JA = JArray(JInt) def call(): inst = JA(100) Tracer.attach(inst) self.fixture.callObject(inst) for i in range(100): call() self.assertEqual(Tracer.leaked(), 0) def testArrayMemoryView(self): JA = JArray(JInt) def call(): inst = JA(100) Tracer.attach(inst) memoryview(inst) for i in range(100): call() self.assertEqual(Tracer.leaked(), 0) def testBufferCall(self): byte_buffer = jpype.JClass('java.nio.ByteBuffer') def call(): inst = byte_buffer.allocateDirect(10) Tracer.attach(inst) self.fixture.callObject(inst) for i in range(100): call() self.assertEqual(Tracer.leaked(), 0) def testBufferMemoryView(self): byte_buffer = jpype.JClass('java.nio.ByteBuffer') def call(): inst = byte_buffer.allocateDirect(10) Tracer.attach(inst) memoryview(inst) for i in range(100): call() self.assertEqual(Tracer.leaked(), 0) jpype-1.6.0/test/jpypetest/test_legacy.py000066400000000000000000000066751501674766700205710ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common import subrun import unittest import os def proxy(s): if not isinstance(s, str): raise TypeError("Fail") return s # This is a test case to exercise all of the paths that pass through # the string conversion to make sure all are exercised. @subrun.TestCase class LegacyTestCase(unittest.TestCase): @classmethod def setUpClass(cls): # Run with automatic string conversion jpype.startJVM(classpath=os.path.abspath( "test/classes"), convertStrings=True) def setUp(self): self._test = jpype.JClass("jpype.str.Test") self._intf = jpype.JClass("jpype.str.StringFunction") def testStaticField(self): s = self._test.staticField self.assertEqual(s, "staticField") self.assertIsInstance(s, str) def testMemberField(self): s = self._test().memberField self.assertEqual(s, "memberField") self.assertIsInstance(s, str) def testStaticMethod(self): s = self._test.staticCall() self.assertEqual(s, "staticCall") self.assertIsInstance(s, str) def testMemberMethod(self): s = self._test().memberCall() self.assertEqual(s, "memberCall") self.assertIsInstance(s, str) def testArrayItem(self): tc = ('apples', 'banana', 'cherries', 'dates', 'elderberry') for i in range(0, 5): s = self._test.array[i] self.assertEqual(s, tc[i]) self.assertIsInstance(s, str) def testArrayRange(self): tc = ('apples', 'banana', 'cherries', 'dates', 'elderberry') slc = self._test.array[1:-1] self.assertEqual(tuple(slc), tc[1:-1]) self.assertIsInstance(slc[1], str) def testProxy(self): p = jpype.JProxy([self._intf], dict={'call': proxy}) r = self._test().callProxy(p, "roundtrip") self.assertEqual(r, "roundtrip") self.assertIsInstance(r, str) def testNullBytes(self): s = self._test.callWithNullBytes() self.assertEqual(s, "call\u0000With\u0000Null\u0000Bytes") self.assertIsInstance(s, str) s = self._test.returnArgument("\u0394 16 byte encoded text\u0000with null\u0000 bytes \u1394") self.assertEqual(s, "\u0394 16 byte encoded text\u0000with null\u0000 bytes \u1394") self.assertIsInstance(s, str) s = self._test.returnArgument("\U0001F468\u200D\U0001F9B2 32 byte encoded text" "\u0000with null\u0000 bytes \U0001F468\u200D\U0001F9B2") self.assertEqual(s, "\U0001F468\u200D\U0001F9B2 32 byte encoded text" "\u0000with null\u0000 bytes \U0001F468\u200D\U0001F9B2") self.assertIsInstance(s, str) jpype-1.6.0/test/jpypetest/test_list.py000066400000000000000000000211211501674766700202570ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class JListTestCase(common.JPypeTestCase): """ Test for methods of java.lang.Map def __getitem__(self, ndx): if isinstance(ndx, slice): start = ndx.start stop = ndx.stop if start < 0: start = self.size() + start if stop < 0: stop = self.size() + stop return self.subList(start, stop) else: if ndx < 0: ndx = self.size() + ndx return self.get(ndx) def __setitem__(self, ndx, v): if isinstance(ndx, slice): start = ndx.start stop = ndx.stop if start < 0: start = self.size() + start if stop < 0: stop = self.size() + stop for i in range(start, stop): self.remove(start) if isinstance(v, collections.abc.Sequence): ndx = start for i in v: self.add(ndx, i) ndx += 1 else: if ndx < 0: ndx = self.size() + ndx self.set(ndx, v) """ def setUp(self): common.JPypeTestCase.setUp(self) self.cls = jpype.JClass('java.util.ArrayList') self.Arrays = jpype.JClass('java.util.Arrays') def testLen(self): obj = self.cls() obj.add(1) obj.add(2) obj.add(3) self.assertEqual(len(obj), 3) def testIter(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") self.assertEqual(tuple(i for i in obj), ('a', 'b', 'c')) def testGetItem(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") self.assertEqual(obj[1], 'b') def testGetItemSub(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") obj.add("d") self.assertEqual(tuple(i for i in obj[1:3]), ('b', 'c')) def testGetItemSlice(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") obj.add("d") self.assertEqual(tuple(obj[::1]), ('a', 'b', 'c', 'd')) self.assertEqual(tuple(obj[:-2]), ('a', 'b')) self.assertEqual(tuple(obj[-2:]), ('c', 'd')) with self.assertRaises(TypeError): obj[::2] with self.assertRaises(TypeError): obj[::-1] def testRemoveRange(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") obj.add("d") obj[1:3].clear() self.assertEqual(tuple(i for i in obj), ('a', 'd')) def testSetItem(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") obj[1] = 'changed' self.assertEqual(tuple(i for i in obj), ('a', 'changed', 'c')) def testDelItem(self): obj = self.cls() obj.add("a") obj.add("b") obj.add("c") del obj[1] self.assertElementsEqual(obj, ('a', 'c')) obj.add("a") obj.add("b") obj.add("c") del obj[1:3] self.assertElementsEqual(obj, ('a', 'b', 'c')) with self.assertRaises(TypeError): del obj[1.0] del obj[-1] self.assertElementsEqual(obj, ('a', 'b')) def testAddAll(self): obj = self.cls() obj.addAll(["a", "b", "c"]) self.assertEqual(tuple(i for i in obj), ('a', 'b', 'c')) obj.addAll(1, ["a", "b", "c"]) self.assertEqual(tuple(i for i in obj), ('a', 'a', 'b', 'c', 'b', 'c')) with self.assertRaises(TypeError): obj.addAll() with self.assertRaises(TypeError): obj.addAll(1, 2, 3) with self.assertRaises(TypeError): obj.addAll(1, 2) with self.assertRaises(TypeError): obj.addAll(1.0, ['a']) def testRemoveAll(self): obj = self.cls() obj.addAll(["a", "b", "c", "d", "e"]) obj.removeAll(["c", "d"]) self.assertEqual(tuple(i for i in obj), ('a', 'b', 'e')) def testRetainAll(self): obj = self.cls() obj.addAll(["a", "b", "c", "d", "e"]) obj.retainAll(["c", "d"]) self.assertEqual(tuple(i for i in obj), ('c', 'd')) def testInit(self): cls = jpype.JClass('java.util.ArrayList') obj = cls(self.Arrays.asList(['a', 'b', 'c'])) self.assertEqual(tuple(i for i in obj), ('a', 'b', 'c')) def testInsert(self): lst = ['A', 'B', 'C'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) lst.insert(0, '1') obj.insert(0, '1') lst.insert(3, '2') obj.insert(3, '2') lst.insert(-1, '3') obj.insert(-1, '3') self.assertElementsEqual(obj, lst) def testAppend(self): lst = ['A', 'B', 'C'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) obj.append('2') lst.append('2') lst[len(lst):] = ['3'] obj[len(obj):] = ['3'] self.assertElementsEqual(obj, lst) def testReverse(self): lst = ['A', 'B', 'C'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) obj.reverse() lst.reverse() self.assertElementsEqual(obj, lst) def testExtend(self): lst = ['A', 'B', 'C'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) obj.extend(range(0, 3)) lst.extend(range(0, 3)) self.assertElementsEqual(obj, lst) def testPop(self): lst = ['A', 'B', 'C', 'D', 'E'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) self.assertEqual(obj.pop(), lst.pop()) self.assertEqual(obj.pop(0), lst.pop(0)) self.assertEqual(obj.pop(2), lst.pop(2)) self.assertEqual(obj.pop(-1), lst.pop(-1)) self.assertElementsEqual(obj, lst) def testIAdd(self): lst = ['A', 'B', 'C'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) obj += 'D' lst += 'D' self.assertElementsEqual(obj, lst) def testAdd(self): lst = ['A', 'B', 'C'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) lst2 = lst + ['D'] obj2 = obj + ['D'] self.assertElementsEqual(obj, lst) self.assertElementsEqual(obj2, lst2) def testRemove(self): lst = ['A', 'B', 'C', 'D', 'E'] cls = jpype.JClass('java.util.ArrayList') obj = cls(lst) lst.remove('C') obj.remove('C') with self.assertRaises(ValueError): lst.remove('C') with self.assertRaises(ValueError): obj.remove(1) with self.assertRaises(ValueError): obj.remove('1') with self.assertRaises(ValueError): obj.remove(object()) self.assertElementsEqual(obj, lst) def testProtocol(self): from collections.abc import Sequence, MutableSequence List = jpype.JClass('java.util.List') ArrayList = jpype.JClass('java.util.ArrayList') LinkedList = jpype.JClass('java.util.LinkedList') self.assertTrue(issubclass(List, Sequence)) self.assertTrue(issubclass(List, MutableSequence)) self.assertTrue(issubclass(ArrayList, Sequence)) self.assertTrue(issubclass(ArrayList, MutableSequence)) self.assertTrue(issubclass(LinkedList, Sequence)) self.assertTrue(issubclass(LinkedList, MutableSequence)) def testConcat(self): ArrayList = jpype.JClass('java.util.ArrayList') l1 = ArrayList([1,2,3]) l2 = ArrayList([4,5,6]) l3 = l1 + l2 self.assertEqual(l3, ArrayList([1,2,3,4,5,6])) def testRepeat(self): ArrayList = jpype.JClass('java.util.ArrayList') l1 = ArrayList([1,2,3]) l2 = l1 * 3 print(l2) self.assertEqual(l2, ArrayList([1,2,3,1,2,3,1,2,3])) jpype-1.6.0/test/jpypetest/test_map.py000066400000000000000000000040151501674766700200640ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class JMapTestCase(common.JPypeTestCase): """ Test for methods of java.lang.Map """ def setUp(self): common.JPypeTestCase.setUp(self) def testLen(self): cls = jpype.JClass('java.util.HashMap') obj = cls() obj.put("a", 1) obj.put("b", 2) obj.put("c", 3) self.assertEqual(len(obj), 3) def testIter(self): cls = jpype.JClass('java.util.TreeMap') obj = cls() obj.put("a", 1) obj.put("b", 2) obj.put("c", 3) self.assertEqual(tuple(i for i in obj), ('a', 'b', 'c')) def testGetItem(self): cls = jpype.JClass('java.util.HashMap') obj = cls() obj.put("a", 1) obj.put("b", 2) obj.put("c", 3) self.assertEqual(obj['b'], 2) def testSetItem(self): cls = jpype.JClass('java.util.HashMap') obj = cls() obj.put("a", 1) obj.put("b", 2) obj.put("c", 3) obj['b'] = 5 self.assertEqual(obj['b'], 5) def testSetItem_2(self): cls = jpype.JClass('java.util.TreeMap') obj = cls() obj.put("a", 1) obj.put("b", 2) obj.put("c", 3) del obj['b'] self.assertEqual(tuple(i for i in obj), ('a', 'c')) jpype-1.6.0/test/jpypetest/test_module.py000066400000000000000000000075031501674766700206010ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** # Tests for module functionality including failures that cannot # be triggered in normal operations import _jpype import jpype import unittest import subrun import common import unittest.mock as mock @subrun.TestCase(individual=True) class ModuleStartTestCase(unittest.TestCase): def testNoJObject(self): with self.assertRaises(RuntimeError): import jpype del _jpype.JObject jpype.startJVM() def testNoJInterface(self): with self.assertRaises(RuntimeError): import jpype del _jpype.JInterface jpype.startJVM() def testNoJArray(self): with self.assertRaises(RuntimeError): import jpype del _jpype.JArray jpype.startJVM() def testNoJException(self): with self.assertRaises(RuntimeError): import jpype del _jpype.JException jpype.startJVM() def testNoJClassPre(self): with self.assertRaises(RuntimeError): import jpype del _jpype._jclassPre jpype.startJVM() def testNoJClassPost(self): with self.assertRaises(RuntimeError): import jpype del _jpype._jclassPost jpype.startJVM() def testNoMethodAnnotations(self): with self.assertRaises(RuntimeError): import jpype del _jpype.getMethodAnnotations jpype.startJVM() def testNoMethodCode(self): with self.assertRaises(RuntimeError): import jpype del _jpype.getMethodCode jpype.startJVM() def testShutdown(self): import jpype jpype.startJVM(convertStrings=False) jpype.shutdownJVM() class ModuleTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testIsPackage(self): self.assertTrue(_jpype.isPackage("java")) self.assertFalse(_jpype.isPackage("jva")) with self.assertRaises(TypeError): _jpype.isPackage(object()) def testGetClass(self): self.assertIsInstance(_jpype._getClass("java.lang.String"), _jpype._JClass) with self.assertRaises(TypeError): _jpype._getClass(object()) def testHasClass(self): self.assertTrue(_jpype._hasClass("java.lang.String")) with self.assertRaises(TypeError): _jpype._hasClass(object()) class JInitTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testJInit(self): with mock.patch("_jpype.isStarted") as started: started.return_value = False self.assertEqual(len(jpype._jinit.JInitializers), 0) A = [] def func(): A.append(1) jpype.onJVMStart(func) self.assertEqual(len(A), 0) self.assertEqual(jpype._jinit.JInitializers[0], func) started.return_value = True jpype.onJVMStart(func) self.assertEqual(len(A), 1) jpype._jinit.runJVMInitializers() self.assertEqual(len(A), 2) jpype-1.6.0/test/jpypetest/test_module2.py000066400000000000000000000044751501674766700206700ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import _jpype import common class ModuleTestCase2(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testMonitorOnNull(self): value = jpype.JObject(None) with self.assertRaises(TypeError): _jpype._JMonitor(value) def testMonitorOnString(self): value = jpype.JString("foo") with self.assertRaises(TypeError): _jpype._JMonitor(value) def testMonitorOnPrim(self): value = jpype.JInt(1) with self.assertRaises(TypeError): _jpype._JMonitor(value) def testMonitorInitBad(self): with self.assertRaises(TypeError): _jpype._JMonitor() def testMonitorInitBad2(self): with self.assertRaises(TypeError): _jpype._JMonitor(None) def testMonitorStr(self): obj = jpype.java.lang.Object() monitor = _jpype._JMonitor(obj) self.assertIsInstance(str(monitor), str) def testProxyInitBad(self): with self.assertRaises(TypeError): _jpype._JProxy(None) def testProxyInitBad2(self): with self.assertRaises(TypeError): _jpype._JProxy(None, None, None) def testProxyInitBad3(self): with self.assertRaises(TypeError): _jpype._JProxy(None, None, tuple([None, None])) def testProxyNoInterfaces(self): with self.assertRaises(TypeError): proxy = _jpype._JProxy(None, None, tuple()) def testValueStr(self): obj = jpype.JClass("java.lang.Object")() self.assertIsInstance(str(obj), str) jpype-1.6.0/test/jpypetest/test_mro.py000066400000000000000000000020321501674766700201010ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from jpype.types import * from jpype import java import sys import common class MroTestCase(common.JPypeTestCase): def testMro(self): C = JClass('jpype.mro.C') def testMultipleInterfaces(self): j = JClass("jpype.mro.MultipleInterfaces") myinstance = j() jpype-1.6.0/test/jpypetest/test_number.py000066400000000000000000000050361501674766700206030ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common import random import _jpype import jpype from jpype import java from jpype.types import * try: import numpy as np except ImportError: pass class JNumberTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.cls = JClass("jpype.common.Fixture") self.fixture = self.cls() def testJavaPrimitives(self): self.assertIsInstance( self.fixture.callNumber(JByte(1)), java.lang.Byte) self.assertIsInstance( self.fixture.callNumber(JShort(1)), java.lang.Short) self.assertIsInstance( self.fixture.callNumber(JInt(1)), java.lang.Integer) self.assertIsInstance( self.fixture.callNumber(JLong(1)), java.lang.Long) self.assertIsInstance( self.fixture.callNumber(JFloat(1)), java.lang.Float) self.assertIsInstance(self.fixture.callNumber( JDouble(1)), java.lang.Double) def testPythonPrimitives(self): self.assertIsInstance(self.fixture.callNumber(1), java.lang.Long) self.assertIsInstance(self.fixture.callNumber(1.0), java.lang.Double) @common.requireNumpy def testNumpyPrimitives(self): self.assertIsInstance( self.fixture.callNumber(np.int8(1)), java.lang.Byte) self.assertIsInstance(self.fixture.callNumber( np.int16(1)), java.lang.Short) self.assertIsInstance(self.fixture.callNumber( np.int32(1)), java.lang.Integer) self.assertIsInstance(self.fixture.callNumber( np.int64(1)), java.lang.Long) self.assertIsInstance(self.fixture.callNumber( np.float32(1)), java.lang.Float) self.assertIsInstance(self.fixture.callNumber( np.float64(1)), java.lang.Double) jpype-1.6.0/test/jpypetest/test_numeric.py000066400000000000000000000121431501674766700207520ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import _jpype import jpype from jpype import JPackage, java from jpype.types import * import common class NumericTestCase(common.JPypeTestCase): def testMathAbs(self): self.assertEqual(java.lang.Math.abs(-10), 10) def testDoubleConversion(self): f = java.lang.Float.MAX_VALUE * 2 self.assertTrue(JClass("jpype.numeric.NumericTest").doubleIsTwiceMaxFloat(f)) def testDoubleIsProperlyConverted(self): self.assertTrue(java.lang.Double.POSITIVE_INFINITY != 0.0) self.assertTrue(java.lang.Double.MAX_VALUE != 0.0) self.assertTrue(java.lang.Double.NaN != 0.0) self.assertTrue(java.lang.Double.NEGATIVE_INFINITY != 0.0) def testCompareNullLong(self): null = JObject(None, java.lang.Integer) self.assertEqual(null, None) self.assertNotEqual(null, object()) with self.assertRaisesRegex(TypeError, "null"): null > 0 with self.assertRaisesRegex(TypeError, "null"): null < 0 with self.assertRaisesRegex(TypeError, "null"): null >= 0 with self.assertRaisesRegex(TypeError, "null"): null <= 0 def testCompareNullFloat(self): null = JObject(None, java.lang.Double) self.assertEqual(null, None) self.assertNotEqual(null, object()) with self.assertRaisesRegex(TypeError, "null"): null > 0 with self.assertRaisesRegex(TypeError, "null"): null < 0 with self.assertRaisesRegex(TypeError, "null"): null >= 0 with self.assertRaisesRegex(TypeError, "null"): null <= 0 def testHashNull(self): null = JObject(None, java.lang.Integer) self.assertEqual(hash(null), hash(None)) null = JObject(None, java.lang.Float) self.assertEqual(hash(null), hash(None)) @common.requireInstrumentation def testJPNumber_new(self): _jpype.fault("PyJPNumber_new") class MyNum(_jpype._JNumberLong, internal=True): pass with self.assertRaisesRegex(SystemError, "fault"): JInt(1) with self.assertRaises(TypeError): MyNum(1) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): JInt(1) JInt(1) @common.requireInstrumentation def testJPNumberLong_int(self): ji = JInt(1) _jpype.fault("PyJPNumberLong_int") with self.assertRaisesRegex(SystemError, "fault"): int(ji) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): int(ji) int(ji) @common.requireInstrumentation def testJPNumberLong_float(self): ji = JInt(1) _jpype.fault("PyJPNumberLong_float") with self.assertRaisesRegex(SystemError, "fault"): float(ji) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): float(ji) float(ji) @common.requireInstrumentation def testJPNumberLong_str(self): ji = JInt(1) _jpype.fault("PyJPNumberLong_str") with self.assertRaisesRegex(SystemError, "fault"): str(ji) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): str(ji) str(ji) @common.requireInstrumentation def testJPNumberLong_repr(self): ji = JInt(1) _jpype.fault("PyJPNumberLong_repr") with self.assertRaisesRegex(SystemError, "fault"): repr(ji) _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): repr(ji) repr(ji) @common.requireInstrumentation def testJPNumberLong_compare(self): ji = JInt(1) _jpype.fault("PyJPNumberLong_compare") with self.assertRaisesRegex(SystemError, "fault"): ji == 1 _jpype.fault("PyJPModule_getContext") with self.assertRaisesRegex(SystemError, "fault"): ji == 1 ji == 1 @common.requireInstrumentation def testJPNumberLong_hash(self): ji = JInt(1) _jpype.fault("PyJPNumberLong_hash") with self.assertRaises(SystemError): hash(ji) _jpype.fault("PyJPModule_getContext") with self.assertRaises(SystemError): hash(ji) hash(ji) jpype-1.6.0/test/jpypetest/test_objectwrapper.py000066400000000000000000000144471501674766700221700ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * from jpype import java import common #import os #import sys class ObjectWrapperTestCase(common.JPypeTestCase): def testCallOverloads(self): # build the harness h = JClass("jpype.objectwrapper.Test1")() o = java.lang.Integer(1) self.assertEqual(h.Method1(JObject(o, java.lang.Number)), 1) self.assertEqual(h.Method1(o), 2) self.assertEqual(h.Method1(JObject(java.lang.Integer(1), java.lang.Object)), 3) self.assertEqual(h.Method1(JString("")), 4) def testDefaultTypeNameString(self): self.assertEqual(type(JObject("123")), jpype.JClass("java.lang.String")) def testDefaultTypeNameBoolean(self): self.assertEqual(type(JObject(True)), jpype.JClass("java.lang.Boolean")) self.assertEqual(type(JObject(False)), jpype.JClass("java.lang.Boolean")) def testPassingClassTypeSucceeds(self): h = JClass("jpype.objectwrapper.Test1")() # Select a convenient java.lang.Class object class_obj = h.getClass() # Check that funneling Class obj through java doesn't convert to null result = h.ReturnObject(class_obj) self.assertEqual(class_obj, result) self.assertNotEqual(result, None) def testWrapJavaClass(self): o = java.lang.String self.assertEqual(type(JObject(o)), jpype.JClass("java.lang.Class")) def testWrapJavaObject(self): o = java.lang.String("foo") self.assertEqual(type(JObject(o)), jpype.JClass("java.lang.String")) def testWrapJavaObjectCast(self): o = java.lang.String("foo") c = java.lang.Object self.assertEqual(type(JObject(o, c)), jpype.JClass("java.lang.Object")) def testWrapJavaObjectCastFail(self): o = java.lang.Object() c = java.lang.Math with self.assertRaises(TypeError): f = JObject(o, c) def testWrapJavaPrimitiveCast(self): c = java.lang.Object self.assertEqual(type(JObject("foo", c)), jpype.JClass("java.lang.Object")) self.assertEqual(type(JObject(True, c)), jpype.JClass("java.lang.Object")) self.assertEqual(type(JObject(False, c)), jpype.JClass("java.lang.Object")) self.assertEqual(type(JObject(1, c)), jpype.JClass("java.lang.Object")) self.assertEqual(type(JObject(1.0, c)), jpype.JClass("java.lang.Object")) def testWrapJavaPrimitiveBox(self): self.assertEqual(type(JObject("foo", JString)), jpype.JClass("java.lang.String")) self.assertEqual(type(JObject(1, JInt)), jpype.JClass("java.lang.Integer")) self.assertEqual(type(JObject(1, JLong)), jpype.JClass("java.lang.Long")) self.assertEqual(type(JObject(1.0, JFloat)), jpype.JClass("java.lang.Float")) self.assertEqual(type(JObject(1.0, JDouble)), jpype.JClass("java.lang.Double")) self.assertEqual(type(JObject(1, JShort)), jpype.JClass("java.lang.Short")) def testJObjectBadType(self): class Fred(object): pass with self.assertRaises(TypeError): jpype.JObject(1, Fred) def testJObjectUnknownObject(self): class Fred(object): pass with self.assertRaises(TypeError): jpype.JObject(Fred()) def testObjectEq(self): C1 = jpype.JClass("java.lang.StringBuffer") C2 = jpype.JClass("java.lang.Exception") O1 = C1() O2 = C2() N1 = jpype.JObject(None, C1) N2 = jpype.JObject(None, C2) V = jpype.JInt(1) # Test dissimilar objects self.assertTrue(O1 != O2) self.assertFalse(O1 == O2) self.assertTrue(O2 != O1) self.assertFalse(O2 == O1) # test Nulls self.assertTrue(N1 == N2) self.assertFalse(N1 != N2) self.assertTrue(N1 == None) self.assertFalse(N1 != None) self.assertTrue(None == N1) self.assertFalse(None != N1) # test primitives self.assertFalse(O1 == V) self.assertFalse(V == O1) self.assertFalse(O2 == V) self.assertFalse(V == O2) # test null to primitives self.assertFalse(N1 == V) self.assertFalse(V == N1) self.assertFalse(N2 == V) self.assertFalse(V == N2) # def testMakeSureWeCanLoadAllClasses(self): # def get_system_jars(): # for dirpath,_,files in os.walk(jpype.java.lang.System.getProperty("java.home")): # for file in files: # if file.endswith('.jar'): # yield (os.path.join(dirpath,file)) # for jar in get_system_jars(): # classes = [x.getName() for x in jpype.java.util.jar.JarFile(jar).entries() if x.getName().endswith('.class')] # classes = [x.replace('/','.')[:-6] for x in classes] # for clazz in classes: # try: # jpype.JClass(clazz) # except jpype.JavaException as exception: # if not 'not found' in exception.message(): # print(clazz) # print (exception.message()) # #print (exception.stacktrace()) # except: # print(sys.exc_info()[0]) # pass jpype-1.6.0/test/jpypetest/test_opts.py000077500000000000000000000012041501674766700202740ustar00rootroot00000000000000import sys import jpype import common # This is an example of how to pass arguments to pytest. # Options are defined in the file conftest.py and # passed the a fixture function. This fixture function # is then applied to each TestCase class. # # class OptsTestCase(common.JPypeTestCase): # # def setUp(self): # common.JPypeTestCase.setUp(self) # # def testOpts(self): # self.assertTrue(hasattr(self, "_jar")) # self.assertTrue(hasattr(self, "_convertStrings")) # # def testConvertStrings(self): # self.assertTrue(self._convertStrings) # # def testJar(self): # self.assertEqual(self._jar, "foo") jpype-1.6.0/test/jpypetest/test_overloads.py000066400000000000000000000342461501674766700213160ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype import JString, java, JArray, JClass, JByte, JShort, JInt, JLong, JFloat, JDouble, JChar, JBoolean, JObject import sys import time import common java = jpype.java class MyClass: def fun1(self): pass def fun2(self, *a): pass def fun3(self, a=1): pass def fun4(self, a): pass def fun1(): pass def fun2(*a): pass def fun3(a=1): pass def fun4(a): pass if __name__ == '__main__': jpype.startJVM() from java.lang import Runnable mc = MyClass() cb = Runnable @ fun1 cb = Runnable @ fun2 cb = Runnable @ fun3 #cb = Runnable @ fun4 cb = Runnable @ mc.fun1 cb = Runnable @ mc.fun2 cb = Runnable @ mc.fun3 cb = Runnable @ mc.fun4 class OverloadTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.__jp = self.jpype.overloads self._aclass = JClass('jpype.overloads.Test1$A') self._bclass = JClass('jpype.overloads.Test1$B') self._cclass = JClass('jpype.overloads.Test1$C') self._a = self._aclass() self._b = self._bclass() self._c = self._cclass() self._i1impl = JClass('jpype.overloads.Test1$I1Impl')() self._i2impl = JClass('jpype.overloads.Test1$I2Impl')() self._i3impl = JClass('jpype.overloads.Test1$I3Impl')() self._i4impl = JClass('jpype.overloads.Test1$I4Impl')() self._i5impl = JClass('jpype.overloads.Test1$I5Impl')() self._i6impl = JClass('jpype.overloads.Test1$I6Impl')() self._i7impl = JClass('jpype.overloads.Test1$I7Impl')() self._i8impl = JClass('jpype.overloads.Test1$I8Impl')() def testMostSpecificInstanceMethod(self): test1 = self.__jp.Test1() self.assertEqual('A', test1.testMostSpecific(self._a)) self.assertEqual('B', test1.testMostSpecific(self._b)) self.assertEqual('B', test1.testMostSpecific(self._c)) self.assertEqual('B', test1.testMostSpecific(None)) def testForceOverloadResolution(self): test1 = self.__jp.Test1() self.assertEqual('A', test1.testMostSpecific( JObject(self._c, self._aclass))) self.assertEqual('B', test1.testMostSpecific( JObject(self._c, self._bclass))) # JObject wrapper forces exact matches #self.assertRaisesRegex(TypeError, 'No matching overloads found', test1.testMostSpecific, JObject(self._c, self._cclass)) self.assertEqual('A', test1.testMostSpecific( JObject(self._c, 'jpype.overloads.Test1$A'))) self.assertEqual('B', test1.testMostSpecific( JObject(self._c, 'jpype.overloads.Test1$B'))) # JObject wrapper forces exact matches #self.assertRaisesRegex(TypeError, 'No matching overloads found', test1.testMostSpecific, JObject(self._c, 'jpype.overloads.Test1$C')) def testVarArgsCall(self): test1 = self.__jp.Test1() self.assertEqual('A,B...', test1.testVarArgs(self._a, [])) self.assertEqual('A,B...', test1.testVarArgs(self._a, None)) self.assertEqual('A,B...', test1.testVarArgs(self._a, [self._b])) self.assertEqual('A,A...', test1.testVarArgs(self._a, [self._a])) self.assertEqual('A,B...', test1.testVarArgs( self._a, [self._b, self._b])) self.assertEqual('B,B...', test1.testVarArgs( self._b, [self._b, self._b])) self.assertEqual('B,B...', test1.testVarArgs( self._c, [self._c, self._b])) self.assertEqual('B,B...', test1.testVarArgs( self._c, JArray(self._cclass, 1)([self._c, self._c]))) self.assertEqual('B,B...', test1.testVarArgs(self._c, None)) self.assertEqual('B,B...', test1.testVarArgs(None, None)) def testPrimitive(self): test1 = self.__jp.Test1() intexpectation = 'long' # FIXME it is not possible to determine if this is bool/char/byte currently #self.assertEqual(intexpectation, test1.testPrimitive(5)) #self.assertEqual('long', test1.testPrimitive(2**31)) self.assertEqual('byte', test1.testPrimitive(JByte(5))) self.assertEqual('Byte', test1.testPrimitive(java.lang.Byte(5))) self.assertEqual('short', test1.testPrimitive(JShort(5))) self.assertEqual('Short', test1.testPrimitive(java.lang.Short(5))) self.assertEqual('int', test1.testPrimitive(JInt(5))) self.assertEqual('Integer', test1.testPrimitive(java.lang.Integer(5))) self.assertEqual('long', test1.testPrimitive(JLong(5))) self.assertEqual('Long', test1.testPrimitive(java.lang.Long(5))) self.assertEqual('float', test1.testPrimitive(JFloat(5))) self.assertEqual('Float', test1.testPrimitive(java.lang.Float(5.0))) self.assertEqual('double', test1.testPrimitive(JDouble(5))) self.assertEqual('Double', test1.testPrimitive(java.lang.Double(5.0))) self.assertEqual('boolean', test1.testPrimitive(JBoolean(5))) self.assertEqual('Boolean', test1.testPrimitive(java.lang.Boolean(5))) self.assertEqual('char', test1.testPrimitive(JChar('5'))) self.assertEqual('Character', test1.testPrimitive( java.lang.Character('5'))) def testInstanceVsClassMethod(self): # this behaviour is different than the one in java, so maybe we should change it? test1 = self.__jp.Test1() self.assertEqual( 'static B', self.__jp.Test1.testInstanceVsClass(self._c)) self.assertEqual('instance A', test1.testInstanceVsClass(self._c)) # here what would the above resolve to in java self.assertEqual( 'static B', self.__jp.Test1.testJavaInstanceVsClass()) def testInterfaces1(self): test1 = self.__jp.Test1() self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test1.testInterfaces1, self._i4impl) self.assertEqual('I2', test1.testInterfaces1( JObject(self._i4impl, 'jpype.overloads.Test1$I2'))) self.assertEqual('I3', test1.testInterfaces1( JObject(self._i4impl, 'jpype.overloads.Test1$I3'))) def testInterfaces2(self): test1 = self.__jp.Test1() self.assertEqual('I4', test1.testInterfaces2(self._i4impl)) self.assertEqual('I2', test1.testInterfaces2( JObject(self._i4impl, 'jpype.overloads.Test1$I2'))) self.assertEqual('I3', test1.testInterfaces2( JObject(self._i4impl, 'jpype.overloads.Test1$I3'))) def testInterfaces3(self): test1 = self.__jp.Test1() self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test1.testInterfaces3, self._i8impl) self.assertEqual('I4', test1.testInterfaces3(self._i6impl)) self.assertRaisesRegex( TypeError, 'No matching overloads found', test1.testInterfaces3, self._i3impl) def testInterfaces4(self): test1 = self.__jp.Test1() self.assertEqual('I8', test1.testInterfaces4(None)) self.assertEqual('I1', test1.testInterfaces4(self._i1impl)) self.assertEqual('I2', test1.testInterfaces4(self._i2impl)) self.assertEqual('I3', test1.testInterfaces4(self._i3impl)) self.assertEqual('I4', test1.testInterfaces4(self._i4impl)) self.assertEqual('I5', test1.testInterfaces4(self._i5impl)) self.assertEqual('I6', test1.testInterfaces4(self._i6impl)) self.assertEqual('I7', test1.testInterfaces4(self._i7impl)) self.assertEqual('I8', test1.testInterfaces4(self._i8impl)) def testClassVsObject(self): test1 = self.__jp.Test1() self.assertEqual('Object', test1.testClassVsObject(self._i4impl)) self.assertEqual('Object', test1.testClassVsObject(1)) self.assertEqual('Class', test1.testClassVsObject(None)) self.assertEqual('Class', test1.testClassVsObject( JClass('jpype.overloads.Test1$I4Impl'))) self.assertEqual('Class', test1.testClassVsObject( JClass('jpype.overloads.Test1$I3'))) def testStringArray(self): test1 = self.__jp.Test1() self.assertEqual('Object', test1.testStringArray(self._i4impl)) self.assertEqual('Object', test1.testStringArray(1)) self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test1.testStringArray, None) self.assertEqual('String', test1.testStringArray('somestring')) self.assertEqual('String[]', test1.testStringArray([])) self.assertEqual('String[]', test1.testStringArray(['a', 'b'])) self.assertEqual('String[]', test1.testStringArray( JArray(java.lang.String, 1)(['a', 'b']))) self.assertEqual('Object', test1.testStringArray( JArray(JInt, 1)([1, 2]))) def testListVSArray(self): test1 = self.__jp.Test1() self.assertEqual('String[]', test1.testListVSArray(['a', 'b'])) self.assertEqual('List', test1.testListVSArray( jpype.java.util.Arrays.asList(['a', 'b']))) def testDefaultMethods(self): try: testdefault = JClass('jpype.overloads.Test1$DefaultC')() except: pass else: self.assertEqual('B', testdefault.defaultMethod()) def testFunctionalInterfacesWithDifferentSignatures(self): test2 = self.__jp.Test2() self.assertEqual('NoArgs', test2.testFunctionalInterfaces(lambda: 'NoArgs')) self.assertEqual('SingleArg', test2.testFunctionalInterfaces(lambda a: 'SingleArg')) self.assertEqual('TwoArg', test2.testFunctionalInterfaces(lambda a, b: 'TwoArg')) def testFunctionalInterfacesWithDefaults(self): def my_fun(x, y=None): return 'my_fun' def my_fun_vargs(x, *vargs): return 'my_fun_vargs' def my_fun_kwargs(x, **kwargs): return 'my_fun_kwargs' def my_fun_kw(*, keyword_arg=None): return 'my_fun_kw' class my_fun_class: def __call__(self): return 'my_fun_class' test2 = self.__jp.Test2() self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun) self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_vargs) self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_class) self.assertEqual('SingleArg', test2.testFunctionalInterfaces(my_fun_kwargs)) self.assertEqual('NoArgs', test2.testFunctionalInterfaces(my_fun_kw)) def testRunnable(self): Runnable = jpype.JClass("java.lang.Runnable") mc = MyClass() # These should work cb = Runnable @ fun1 cb = Runnable @ fun2 cb = Runnable @ fun3 cb = Runnable @ mc.fun1 cb = Runnable @ mc.fun2 cb = Runnable @ mc.fun3 # These should fail with self.assertRaises(TypeError): cb = Runnable @ fun4 with self.assertRaises(TypeError): cb = Runnable @ mc.fun4 def testDerivedStatic(self): Boolean = jpype.JClass("java.lang.Boolean") Object = jpype.JClass("java.lang.Object") DerivedTest = JClass("jpype.overloads.DerivedTest") Base = DerivedTest.Base() Derived = DerivedTest.Derived() self.assertEqual(DerivedTest.testStatic(True, Base), 1) self.assertEqual(DerivedTest.testStatic("that", Base), 2) self.assertEqual(DerivedTest.testStatic(True, Derived), 1) self.assertEqual(DerivedTest.testStatic("that", Derived), 2) self.assertEqual(DerivedTest.testStatic(jpype.JBoolean(True), Base), 1) self.assertEqual(DerivedTest.testStatic(jpype.JBoolean(True), Derived), 1) self.assertEqual(DerivedTest.testStatic(jpype.JObject(True, Boolean), Derived), 2) self.assertEqual(DerivedTest.testStatic(jpype.JObject(True, Object), Derived), 2) def testDerivedMember(self): Boolean = jpype.JClass("java.lang.Boolean") Object = jpype.JClass("java.lang.Object") DerivedTest = JClass("jpype.overloads.DerivedTest") Base = DerivedTest.Base() Derived = DerivedTest.Derived() obj = DerivedTest() self.assertEqual(obj.testMember(True, Base), 3) self.assertEqual(obj.testMember("that", Base), 4) self.assertEqual(obj.testMember(True, Derived), 3) self.assertEqual(obj.testMember("that", Derived), 4) self.assertEqual(obj.testMember(jpype.JBoolean(True), Base), 3) self.assertEqual(obj.testMember(jpype.JBoolean(True), Derived), 3) self.assertEqual(obj.testMember(jpype.JObject(True, Boolean), Derived), 4) self.assertEqual(obj.testMember(jpype.JObject(True, Object), Derived), 4) def testDerivedSub(self): Boolean = jpype.JClass("java.lang.Boolean") Object = jpype.JClass("java.lang.Object") DerivedTest = JClass("jpype.overloads.DerivedTest") Base = DerivedTest.Base() Derived = DerivedTest.Derived() obj = DerivedTest.Sub() self.assertEqual(obj.testMember(True, Base), 3) self.assertEqual(obj.testMember("that", Base), 4) self.assertEqual(obj.testMember(True, Derived), 3) self.assertEqual(obj.testMember("that", Derived), 4) self.assertEqual(obj.testMember(jpype.JBoolean(True), Base), 3) self.assertEqual(obj.testMember(jpype.JBoolean(True), Derived), 3) self.assertEqual(obj.testMember(jpype.JObject(True, Boolean), Derived), 4) self.assertEqual(obj.testMember(jpype.JObject(True, Object), Derived), 4) jpype-1.6.0/test/jpypetest/test_pickle.py000066400000000000000000000111301501674766700205520ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import os import pickle import sys import common import jpype from jpype import java from jpype.pickle import JPickler, JUnpickler def dump(fname): with open(fname, "rb") as fd: data = fd.read() out = ["%02x" % i for i in data] print("Pickle fail", " ".join(out), file=sys.stderr) class PickleTestCase(common.JPypeTestCase): def setUp(self): super(PickleTestCase, self).setUp() self.filename = "test.pic" def tearDown(self): try: os.unlink(self.filename) except OSError: pass def testString(self): try: s = java.lang.String("test") with open(self.filename, "wb") as fd: JPickler(fd).dump(s) with open(self.filename, "rb") as fd: s2 = JUnpickler(fd).load() except pickle.UnpicklingError: dump(self.filename) self.assertEqual(s, s2) def testString2(self): try: s1 = java.lang.String("test1") s2 = java.lang.String("test2") with open(self.filename, "wb") as fd: pickler = JPickler(fd) pickler.dump(s1) pickler.dump(s2) with open(self.filename, "rb") as fd: unpickler = JUnpickler(fd) s1_ = unpickler.load() s2_ = unpickler.load() self.assertEqual(s1, s1_) self.assertEqual(s2, s2_) except pickle.UnpicklingError: dump(self.filename) def testList(self): s = java.util.ArrayList() s.add("test") s.add("this") try: with open(self.filename, "wb") as fd: JPickler(fd).dump(s) with open(self.filename, "rb") as fd: s2 = JUnpickler(fd).load() except pickle.UnpicklingError: dump(self.filename) self.assertEqual(s2.get(0), "test") self.assertEqual(s2.get(1), "this") def testMixed(self): d = {"array": java.util.ArrayList(), "string": java.lang.String("food")} try: with open(self.filename, "wb") as fd: JPickler(fd).dump(d) with open(self.filename, "rb") as fd: d2 = JUnpickler(fd).load() except pickle.UnpicklingError: dump(self.filename) self.assertEqual(d2['string'], "food") self.assertIsInstance(d2['array'], java.util.ArrayList) def testMultiObject(self): """Regression test for https://github.com/jpype-project/jpype/issues/1201 Issue occurs when a python object contains multiple java objects above a certain size. The issue occurs because of a buffer overflow in ``native/java/org/jpype/pickle/ByteBufferInputStream.java``. """ JString = jpype.JClass("java.lang.String") composite_object = {"a": JString('A' * 512), "b": JString('B' * 512)} with open(self.filename, "wb") as fd: JPickler(fd).dump(composite_object) with open(self.filename, "rb") as fd: loaded_object = JUnpickler(fd).load() assert loaded_object['a'] == str(composite_object['a']) assert loaded_object['b'] == str(composite_object['b']) def testByteBufferInputStream(self): JByteBufferInputStream = jpype.JClass("org.jpype.pickle.ByteBufferInputStream") stream = JByteBufferInputStream() bb = jpype.JClass("java.nio.ByteBuffer").allocate(10) stream.put(b"abc") stream.put(b"def") assert stream.read() == ord('a') assert stream.read(bb.array()) == 5 assert bytes(bb.array()) == b'bcdef\x00\x00\x00\x00\x00' def testFail(self): s = java.lang.Object() with self.assertRaises(java.io.NotSerializableException): with open(self.filename, "wb") as fd: JPickler(fd).dump(s) jpype-1.6.0/test/jpypetest/test_properties.py000066400000000000000000000111771501674766700215120ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import subrun import unittest import jpype @subrun.TestCase class PropertiesTestCase(unittest.TestCase): @classmethod def setUpClass(cls): import jpype.beans jpype.startJVM(classpath="test/classes", convertStrings=False) def setUp(self): self._bean = jpype.JClass('jpype.properties.TestBean')() def testPropertyPublicMethodOverlap(self): self._bean.setProperty1("val") self.assertEqual("getsetval", self._bean.getProperty1()) def testPublicMethodPropertyOverlap(self): self.assertEqual("method", self._bean.property1()) def testPropertyProtectedMethodOverlapInvisibleAttribute(self): self._bean.property2 = "val" self.assertEqual("getsetval", self._bean.property2) def testProtectedMethodPropertyOverlapInvisibleAttribute(self): self.assertFalse(hasattr(self._bean.property2, '__call__')) def testPropertyProtectedMethodOverlapAttribute(self): self._bean.property3 = "val" self.assertEqual("getsetval", self._bean.property3) def testProtectedMethodPropertyOverlapAttribute(self): self.assertFalse(hasattr(self._bean.property3, '__call__')) def testPropertyProtectedMethodOverlapAttributeSet(self): self._bean.setProperty3("val") self.assertEqual("getsetval", self._bean.property3) def testPropertyProtectedMethodOverlapAttributeGet(self): self._bean.property3 = "val" self.assertEqual("getsetval", self._bean.getProperty3()) def testPrivateAttributeNoThreeCharacterMethodMatchCollision(self): self._bean.property4 = "val" self.assertEqual("abcval", self._bean.abcProperty4()) def testPropertyOnlySetter(self): self._bean.property5 = "val" self.assertEqual("returnsetval", self._bean.returnProperty5()) def testPropertyOnlySetterSet(self): self._bean.setProperty5("val") with self.assertRaises(AttributeError): self.assertEqual("setval", self._bean.property5) def testPropertyDifferentAttribute(self): self._bean.property6 = "val" self.assertEqual("getsetval", self._bean.property6) self.assertEqual("setval", self._bean.property7) def testProertyDifferentAttributeSet(self): self._bean.setProperty6("val") self.assertEqual("getsetval", self._bean.property6) self.assertEqual("setval", self._bean.property7) def testHasProperties(self): cls = jpype.JClass("jpype.properties.TestBean") obj = cls() self.assertTrue(isinstance(cls.__dict__['propertyMember'], property)) self.assertTrue(isinstance(cls.__dict__['readOnly'], property)) self.assertTrue(isinstance(cls.__dict__['writeOnly'], property)) self.assertTrue(isinstance(cls.__dict__['with_'], property)) def testPropertyMember(self): obj = jpype.JClass("jpype.properties.TestBean")() obj.propertyMember = "q" self.assertEqual(obj.propertyMember, "q") def testPropertyKeyword(self): obj = jpype.JClass("jpype.properties.TestBean")() obj.with_ = "a" self.assertEqual(obj.with_, "a") self.assertEqual(obj.m5, "a") def testPropertyReadOnly(self): # Test readonly obj = jpype.JClass("jpype.properties.TestBean")() obj.m3 = "b" self.assertEqual(obj.readOnly, "b") with self.assertRaises(AttributeError): obj.readOnly = "c" def testPropertyWriteOnly(self): # Test writeonly obj = jpype.JClass("jpype.properties.TestBean")() obj.writeOnly = "c" self.assertEqual(obj.m4, "c") with self.assertRaises(AttributeError): x = obj.writeOnly def testNoProperties(self): cls = jpype.JClass("jpype.properties.TestBean") with self.assertRaises(KeyError): cls.__dict__['failure1'] with self.assertRaises(KeyError): cls.__dict__['failure2'] jpype-1.6.0/test/jpypetest/test_proxy.py000066400000000000000000000433531501674766700205000ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import contextlib import sys from jpype import * import _jpype import common import subrun def _testMethod1(): return 33 def _testMethod2(): return 32 def _testMethod3(): return "Foo" class C: def testMethod1(self): return 43 def testMethod2(self): return 42 def testMethod3(self): return "Bar" def write(self, bytes, start, length): return bytes, start, length class ThreadCallbackImpl: def __init__(self): self.values = [] def notifyValue(self, val): self.values.append(val) class ProxyTestCase(common.JPypeTestCase): def setUp(self): super(ProxyTestCase, self).setUp() self.package = JPackage("jpype.proxy") self._triggers = self.package.ProxyTriggers def testProxyDecl(self): d = { 'testMethod1': _testMethod1, 'testMethod2': _testMethod2, } itf1 = self.package.TestInterface1 itf2 = self.package.TestInterface2 proxy = JProxy(itf1, dict=d) proxy = JProxy([itf1], dict=d) proxy = JProxy([itf1, itf2], dict=d) proxy = JProxy("jpype.proxy.TestInterface1", dict=d) proxy = JProxy(["jpype.proxy.TestInterface1"], dict=d) def testNotImplemented(self): itf1 = self.package.TestInterface1 proxy = JObject(JProxy(itf1, dict={}), itf1) with self.assertRaises(JClass("java.lang.NoSuchMethodError")): proxy.testMethod1() def testProxyRoundTrip(self): @JImplements(java.lang.Runnable) class MyRun(object): @JOverride def run(self): pass al = JClass('java.util.ArrayList')() runner = MyRun() al.add(runner) self.assertIs(al.get(0), runner) def testProxyLeak(self): @JImplements(java.lang.Runnable) class MyRun(object): @JOverride def run(self): pass al = JClass('java.util.ArrayList')() runner = MyRun() al.add(runner) initial = sys.getrefcount(runner) for i in range(0, 10): self.assertIs(al.get(0), runner) final = sys.getrefcount(runner) self.assertEqual(initial, final) def testProxyDeclFail1(self): itf1 = self.package.TestInterface1 # Missing required arguments with self.assertRaisesRegex(TypeError, "must be specified"): proxy = JProxy(itf1) def testProxyDeclFail2(self): itf1 = self.package.TestInterface1 # Not a str, JClass, or str with self.assertRaisesRegex(TypeError, "is not a Java interface"): proxy = JProxy(int(1), dict={}) def testProxyImplementsForm1(self): itf1 = self.package.TestInterface1 itf2 = self.package.TestInterface2 @JImplements(itf1) class MyImpl(object): @JOverride def testMethod1(self): pass def testDefault1(self): itf1 = self.package.TestInterface1 @JImplements(itf1) class MyImpl(object): @JOverride def testMethod1(self): pass obj = itf1@MyImpl() self.assertEqual(obj.testDefault(), 1234) def testDefault2(self): itf1 = self.package.TestInterface1 @JImplements(itf1) class MyImpl(object): @JOverride def testMethod1(self): pass @JOverride def testDefault(self): return 5678 obj = itf1@MyImpl() self.assertEqual(obj.testDefault(), 5678) def testProxyImplementsForm2(self): itf1 = self.package.TestInterface1 itf2 = self.package.TestInterface2 @JImplements(itf1, itf2) class MyImpl(object): @JOverride def testMethod1(self): pass @JOverride def testMethod2(self): pass @JOverride def write(self): pass def testProxyImplementsForm3(self): @JImplements("jpype.proxy.TestInterface1") class MyImpl(object): @JOverride def testMethod1(self): pass def testProxyImplementsForm4(self): @JImplements("jpype.proxy.TestInterface1", "jpype.proxy.TestInterface2") class MyImpl(object): @JOverride def testMethod1(self): pass @JOverride def testMethod2(self): pass @JOverride def write(self): pass def testProxyImplementsFail1(self): with self.assertRaisesRegex(TypeError, "least one Java interface"): # Empty interfaces @JImplements() class MyImpl(object): @JOverride def testMethod1(self): pass def testProxyImplementsFail2(self): with self.assertRaisesRegex(TypeError, "is not a Java interface"): # Wrong type @JImplements(int(1)) class MyImpl(object): @JOverride def testMethod1(self): pass def testProxyImplementsFail3(self): with self.assertRaisesRegex(NotImplementedError, "requires method"): # Missing implementation @JImplements("jpype.proxy.TestInterface1") class MyImpl(object): def testMethod1(self): pass def testProxyImplementsFail4(self): with self.assertRaisesRegex(TypeError, "is not a Java interface"): # extends @JImplements("java.lang.StringBuilder") class MyImpl(object): def testMethod1(self): pass def testProxyWithDict(self): d = { 'testMethod1': _testMethod1, 'testMethod2': _testMethod2, } itf1 = self.package.TestInterface1 itf2 = self.package.TestInterface2 proxy = JProxy([itf1, itf2], dict=d) result = self._triggers.testProxy(proxy) expected = ['Test Method1 = 33', 'Test Method2 = 32'] self.assertSequenceEqual(result, expected) def testProxyWithDictInherited(self): d = { 'testMethod2': _testMethod2, 'testMethod3': _testMethod3, } itf3 = self.package.TestInterface3 proxy = JProxy(itf3, dict=d) result = self._triggers.testProxy(proxy) expected = ['Test Method2 = 32', 'Test Method3 = Foo'] self.assertSequenceEqual(result, expected) def testProxyWithInst(self): itf3 = self.package.TestInterface3 c = C() proxy = JProxy(itf3, inst=c) result = self._triggers.testProxy(proxy) expected = ['Test Method2 = 42', 'Test Method3 = Bar'] self.assertSequenceEqual(result, expected) def testProxyWithThread(self): itf = self.package.TestThreadCallback tcb = ThreadCallbackImpl() proxy = JProxy(itf, inst=tcb) self._triggers().testProxyWithThread(proxy) self.assertEqual(tcb.values, ['Waiting for thread start', '1', '2', '3', 'Thread finished']) def testProxyWithArguments(self): itf2 = self.package.TestInterface2 c = C() proxy = JProxy(itf2, inst=c) result = self._triggers().testCallbackWithParameters(proxy) bytes, start, length = result self.assertSequenceEqual(bytes, [1, 2, 3, 4]) self.assertEqual(start, 12) self.assertEqual(length, 13) def testProxyWithMultipleInterface(self): itf1 = self.package.TestInterface1 itf2 = self.package.TestInterface2 c = C() proxy = JProxy([itf1, itf2], inst=c) try: result = self._triggers.testProxy(proxy) except Exception as ex: raise ex expected = ['Test Method1 = 43', 'Test Method2 = 42'] self.assertSequenceEqual(result, expected) def testProxyWithMultipleInterfaceInherited(self): itf2 = self.package.TestInterface2 itf3 = self.package.TestInterface3 c = C() proxy = JProxy([itf2, itf3], inst=c) result = self._triggers().testCallbackWithParameters(proxy) bytes, start, length = result self.assertSequenceEqual(bytes, [1, 2, 3, 4]) self.assertEqual(start, 12) self.assertEqual(length, 13) def testProxyJObjectCast(self): jrun = JClass('java.lang.Runnable') jobj = JClass("java.lang.Object") @JImplements(jrun) class MyClass(object): @JOverride def run(self): pass self.assertIsInstance(JObject(MyClass(), jrun), jrun) self.assertIsInstance(JObject(MyClass()), jobj) def testProxyConvert(self): # This was tests that arguments and "self" both # convert to the same object TestInterface5 = JClass("jpype.proxy.TestInterface5") ProxyTriggers = JClass("jpype.proxy.ProxyTriggers") @JImplements(TestInterface5) class Bomb(object): @JOverride def equals(self, other): return type(self) == type(other) b = Bomb() t = ProxyTriggers() self.assertTrue(t.testEquals(b)) def testProxyBoth(self): myobj = object() def myimpl(x): self.assertEqual(myobj, x) return 33 d = { 'testMethod1': myimpl, } itf1 = self.package.TestInterface1 jobj = itf1@JProxy(inst=myobj, dict=d, intf=itf1) self.assertEqual(jobj.testMethod1(), 33) def testValid(self): @JImplements("java.util.Comparator") class ValidComparator(object): @JOverride def compare(self, _o1, _o2): return 0 @JOverride def equals(self, _obj): return True arr = JArray(JString)(["a", "b"]) java.util.Arrays.sort(arr, ValidComparator()) def testRaisePython(self): @JImplements("java.util.Comparator") class RaiseException(object): def __init__(self, exc): self.exc = exc @JOverride def compare(self, _o1, _o2): raise self.exc("nobody expects the Python exception!") @JOverride def equals(self, _obj): return True arr = JArray(JString)(["a", "b"]) with self.assertRaises(TypeError): java.util.Arrays.sort(arr, RaiseException(TypeError)) with self.assertRaises(ValueError): java.util.Arrays.sort(arr, RaiseException(ValueError)) with self.assertRaises(SyntaxError): java.util.Arrays.sort(arr, RaiseException(SyntaxError)) with self.assertRaises(java.lang.RuntimeException): java.util.Arrays.sort( arr, RaiseException(java.lang.RuntimeException)) def testBad(self): @JImplements("java.util.Comparator") class TooManyParams(object): @JOverride def compare(self, _o1, _o2, _o3): return 0 @JOverride def equals(self, _obj): return True @JImplements("java.util.Comparator") class TooFewParams(object): @JOverride def compare(self, _o1): return 0 @JOverride def equals(self, _obj): return True arr = JArray(JString)(["a", "b"]) with self.assertRaises(TypeError): java.util.Arrays.sort(arr, TooManyParams()) with self.assertRaises(TypeError): java.util.Arrays.sort(arr, TooFewParams()) def testUnwrap(self): fixture = JClass("jpype.common.Fixture")() @JImplements("java.io.Serializable") class Q(object): pass q = Q() self.assertEqual(JProxy.unwrap(q), q) q2 = fixture.callObject(q) self.assertEqual(JProxy.unwrap(q2), q) class R(object): pass r = R() s = JProxy("java.io.Serializable", inst=r) self.assertEqual(JProxy.unwrap(s), r) s2 = fixture.callObject(s) self.assertEqual(JProxy.unwrap(s2), r) def testConvert(self): fixture = JClass("jpype.common.Fixture")() class R(object): pass r = R() # Verify with not asked to auto convert it doesn't s = JProxy("java.io.Serializable", inst=r) s2 = fixture.callObject(s) self.assertEqual(s2, s) self.assertNotEqual(s2, r) # Verify that when asked to auto convert it does s = JProxy("java.io.Serializable", inst=r, convert=True) s2 = fixture.callObject(s) self.assertNotEqual(s2, s) self.assertEqual(s2, r) def testMethods(self): fixture = JClass("jpype.common.Fixture")() @JImplements("java.io.Serializable") class R(object): pass r = R() s = JObject(r) # Check if Java will be able to work with this object safely self.assertTrue(s.equals(s)) self.assertIsInstance(s.getClass(), java.lang.Class) self.assertIsInstance(s.toString(), java.lang.String) self.assertIsInstance(s.hashCode(), int) def testMethods2(self): fixture = JClass("jpype.common.Fixture")() class R(object): pass r = JProxy("java.io.Serializable", inst=R()) s = JObject(r) # Check if Java will be able to work with this object safely self.assertTrue(s.equals(s)) self.assertIsInstance(s.getClass(), java.lang.Class) self.assertIsInstance(s.toString(), java.lang.String) self.assertIsInstance(s.hashCode(), int) def testDeferredCheckingFailure(self): @JImplements("java.lang.Runnable", deferred=True) class MyImpl(object): pass with self.assertRaisesRegex(NotImplementedError, "requires method"): MyImpl() def testDeferredCheckingValid(self): @JImplements("java.lang.Runnable", deferred=True) class MyImpl(object): @JOverride def run(self): pass assert isinstance(MyImpl(), MyImpl) def testWeak(self): hc = java.lang.System.identityHashCode @JImplements("java.io.Serializable") class MyObj(object): pass def f(): obj = MyObj() jobj = JObject(obj) i = hc(obj) return i, obj i0, obj = f() i1 = hc(obj) # These should be the same if unless the reference was broken self.assertEqual(i0, i1) def testFunctional(self): def SupplierFunc(): return 561 js = JObject(SupplierFunc, "java.util.function.Supplier") self.assertEqual(js.get(), 561) def testFunctionalLambda(self): js = JObject(lambda x: 2 * x, "java.util.function.DoubleUnaryOperator") self.assertEqual(js.applyAsDouble(1), 2.0) def testBadImplements(self): with self.assertRaises(TypeError): @JImplements("java.lang.Runnable") def MyImpl(object): @JOverride def run(self): pass def testInternal(self): with self.assertRaises(TypeError): _jpype._JProxy(None, None, None) with self.assertRaises(TypeError): _jpype._JProxy(None, None, []) with self.assertRaises(TypeError): _jpype._JProxy(None, None, [type]) @subrun.TestCase(individual=True) class TestProxyDefinitionWithoutJVM(common.JPypeTestCase): def setUp(self): # Explicitly *don't* call the parent setUp as this would start the JVM. pass @contextlib.contextmanager def assertRaisesJVMNotRunning(self): with self.assertRaisesRegex( RuntimeError, "Java Virtual Machine is not running"): yield def testDeferredCheckingNeedsJVM(self): @JImplements("java.lang.Runnable", deferred=True) class MyImpl(object): pass with self.assertRaisesJVMNotRunning(): MyImpl() def testNotDeferredChecking(self): with self.assertRaisesJVMNotRunning(): @JImplements("java.lang.Runnable", deferred=False) class MyImpl(object): pass def testDeferredDefault(self): with self.assertRaisesJVMNotRunning(): @JImplements("java.lang.Runnable") class MyImpl(object): pass def testValidDeferredJVMNotRunning(self): @JImplements("java.lang.Runnable", deferred=True) class MyImpl(object): @JOverride def run(self): pass with self.assertRaisesJVMNotRunning(): MyImpl() def testValidDeferredJVMRunning(self): @JImplements("java.lang.Runnable", deferred=True) class MyImpl(object): @JOverride def run(self): pass startJVM() assert isinstance(MyImpl(), MyImpl) jpype-1.6.0/test/jpypetest/test_proxy_multithreaded.py000066400000000000000000000113321501674766700234030ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from jpype import * import common import time class C: def testMethodInt(self): return 5 def testMethodObject(self): return JPackage("jpype.proxy").ReturnObject(3) def testMethodString(self): return "Hallo" def testMethodList(self, noOfValues): responses = list() for i in range(0, noOfValues): responses.append(JPackage("jpype.proxy").ReturnObject(i)) return java.util.Arrays.asList(responses) class ThreadCallbackImpl: def __init__(self): self.values = [] def notifyValue(self, val): self.values.append(val) class ProxyMultiThreadedTestCase(common.JPypeTestCase): def setUp(self): super(ProxyMultiThreadedTestCase, self).setUp() self.package = JPackage("jpype.proxy") self.executor = None def tearDown(self): if self.executor is not None: self.executor.shutdown() def testProxyWithSingleThreadExecution(self): """Test multiple proxy calls with a single threaded executor. The Java part will only call one proxy method (longer running methods) at a time. The methods should run correctly. """ proxy = JProxy(self.package.TestInterface4, inst=C()) self.executor = self.package.ProxyExecutor(1) self.executor.registerProxy(proxy, 10) # register proxy executions # threads self.executor.runExecutor() result = self.executor.waitForExecutedTasks() expected = self.executor.getExpectedTasks() self.assertEqual(result, expected, "Executed Tasks should be the same.") def testProxyWithMultipleThreadExecutionSingleCallbackInstance(self): """Test multiple proxy calls with different threads at the same time. The Java part will call the same callback instance from different threads at a time. Jpype library will throw an exception and the proxy method won't be called: java.lang.RuntimeException: Python exception thrown: AttributeError: 'NoneType' object has no attribute 'getName' at jpype.JPypeInvocationHandler.hostInvoke(Native Method) at jpype.JPypeInvocationHandler.invoke( JPypeInvocationHandler.java:10) ... """ proxy = JProxy(self.package.TestInterface4, inst=C()) self.executor = self.package.ProxyExecutor(10) self.executor.registerProxy(proxy, 100) # register proxy executions # threads self.executor.runExecutor() result = self.executor.waitForExecutedTasks() expected = self.executor.getExpectedTasks() self.assertEqual(result, expected, "Executed Tasks should be the same.") def testProxyWithMultipleThreadExecutionMultiCallbackInstances(self): """Test multiple proxy calls with different threads at the same time. The Java part will call the proxy method from different callback instances and from different threads at a time. Jpype library will throw an exception and the proxy method won't be called: java.lang.RuntimeException: Python exception thrown: AttributeError: 'NoneType' object has no attribute 'getName' at jpype.JPypeInvocationHandler.hostInvoke(Native Method) at jpype.JPypeInvocationHandler.invoke( JPypeInvocationHandler.java:10) ... """ self.executor = self.package.ProxyExecutor(10) for i in range(0, 5): proxy = JProxy(self.package.TestInterface4, inst=C()) self.executor.registerProxy(proxy, 15) # register proxy executions # threads self.executor.runExecutor() result = self.executor.waitForExecutedTasks() expected = self.executor.getExpectedTasks() self.assertEqual(result, expected, "Executed Tasks should be the same.") jpype-1.6.0/test/jpypetest/test_ref.py000066400000000000000000000043641501674766700200720ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import _jpype import jpype from jpype import JImplements, JOverride from jpype.types import * import common class ReferenceQueueTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.refqueue = jpype.JClass( 'org.jpype.ref.JPypeReferenceQueue').getInstance() def testAccess(self): # Make sure we can get the instance self.assertTrue(self.refqueue != None) def testRunning(self): # Get the queue instance self.assertTrue(self.refqueue.isRunning()) def testRefs(self): # This routine will exercise each of the clean up paths once fixture = JClass("jpype.common.Fixture")() def f(): # Create a proxy to test the proxy path @JImplements("java.util.function.Supplier") class MySupplier(object): @JOverride def get(self): # Send a Python exc to trigger Python ref path raise RuntimeError("foo") try: u = MySupplier() fixture.callSupplier(u) except RuntimeError as ex: pass f() # Force a direct byffer and then trash it b = bytearray([1, 2, 3]) _jpype.convertToDirectBuffer(b, False) # Then force a GC to clean it up jpype.java.lang.System.gc() # We can't check the results here as the GC may chose not # to run which would trigger a failure jpype-1.6.0/test/jpypetest/test_reflect.py000066400000000000000000000055611501674766700207420ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype from jpype import JPackage, JArray, JByte, java import common class ReflectCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.test1 = jpype.JClass('jpype.overloads.Test1')() self.Reflect = jpype.JClass('jpype.reflect.ReflectionTest') self.Annotation = jpype.JClass('jpype.reflect.Annotation') def testClass(self): t = jpype.JClass('java.lang.Object') obj = self.Reflect() self.assertEqual('Class', self.test1.testClassVsObject(self.Reflect)) self.assertEqual( 'Class', self.test1.testClassVsObject(self.Reflect.class_)) self.assertEqual( 'Class', self.test1.testClassVsObject(obj.getClass())) self.assertEqual( 'Class', self.test1.testClassVsObject(obj.__class__.class_)) def testAnnotation(self): method = self.Reflect.class_.getMethod('annotatedMethod') annotation = method.getAnnotation(self.Annotation) self.assertEqual('annotation', annotation.value()) def testCallPublicMethod(self): method = self.Reflect.class_.getMethod('publicMethod') obj = self.Reflect() self.assertIsNotNone(obj) self.assertIsNotNone(method) self.assertEqual('public', method.invoke(obj)) def testCallPrivateMethod(self): method = self.Reflect.class_.getDeclaredMethod('privateMethod') method.setAccessible(True) obj = self.Reflect() self.assertIsNotNone(obj) self.assertIsNotNone(method) self.assertEqual('private', method.invoke(obj)) def testAccessPublicField(self): field = self.Reflect.class_.getField('publicField') obj = self.Reflect() self.assertIsNotNone(obj) self.assertIsNotNone(field) self.assertEqual('public', field.get(obj)) def testAccessPrivateField(self): field = self.Reflect.class_.getDeclaredField('privateField') obj = self.Reflect() field.setAccessible(True) self.assertIsNotNone(obj) self.assertIsNotNone(field) self.assertEqual('private', field.get(obj)) jpype-1.6.0/test/jpypetest/test_repr.py000066400000000000000000000040341501674766700202600ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype from jpype.types import * from jpype import JPackage, java import common class ReprTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testClass(self): cls = JClass("java.lang.String") self.assertIsInstance(str(cls), str) self.assertIsInstance(repr(cls), str) def testMethod(self): JS = JClass("java.lang.String") method = JS.substring self.assertIsInstance(str(method), str) self.assertIsInstance(repr(method), str) def testField(self): JS = JClass("java.lang.String") field = JS.__dict__['CASE_INSENSITIVE_ORDER'] self.assertIsInstance(str(JS.substring), str) self.assertIsInstance(repr(JS.substring), str) def testMonitor(self): JI = JClass("java.lang.Integer") with jpype.synchronized(JI) as monitor: self.assertIsInstance(str(monitor), str) self.assertIsInstance(repr(monitor), str) def testArray(self): array = JArray(JInt)([1, 2, 3]) self.assertIsInstance(str(array), str) self.assertIsInstance(repr(array), str) def testObject(self): obj = JObject("abc", JObject) self.assertIsInstance(str(obj), str) self.assertIsInstance(repr(obj), str) jpype-1.6.0/test/jpypetest/test_serial.py000066400000000000000000000032401501674766700205650ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** from jpype import JException, java, JProxy, JClass import os import sys import tempfile import traceback import common class SerializationTestCase(common.JPypeTestCase): def setUp(self): super(SerializationTestCase, self).setUp() self.tempname = tempfile.mktemp() def tearDown(self): os.remove(self.tempname) def testSerialize(self): o = JClass("jpype.serial.SerializationTest")() tmp = self.tempname fos = java.io.FileOutputStream(tmp) oos = java.io.ObjectOutputStream(fos) oos.writeObject(o) oos.flush() oos.close() fos.close() # The following cannto work because JPype has no way to simulate the "caller's ClassLoader" # def testDeSerialize(self): # fis = java.io.FileInputStream(self.tempname) # ois = java.io.ObjectInputStream(fis) # # o = ois.readObject() # ois.close() # fis.close() jpype-1.6.0/test/jpypetest/test_shutdown.py000066400000000000000000000046371501674766700211740ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import unittest import jpype import subrun @subrun.TestCase class ShutdownTest(unittest.TestCase): @classmethod def setUpClass(cls): jpype.startJVM(convertStrings=False) # Create some resources cls.jstr = jpype.java.lang.String("good morning") cls.jobj = jpype.java.lang.Object() cls.jcls = jpype.JClass("java.lang.String") cls.jarray = jpype.JArray(jpype.JInt)([1, 2, 3, 4]) # Then blow everything up jpype.shutdownJVM() def testArrayGet(self): with self.assertRaises(jpype.JVMNotRunning): type(self).jarray[0] def testArraySet(self): with self.assertRaises(jpype.JVMNotRunning): type(self).jarray[0] = 1 def testArrayGetSlice(self): with self.assertRaises(jpype.JVMNotRunning): type(self).jarray[0:2] def testArraySetSlice(self): with self.assertRaises(jpype.JVMNotRunning): type(self).jarray[0:2] = [1, 2] def testArrayStr(self): with self.assertRaises(jpype.JVMNotRunning): str(type(self).jarray) def testClassCtor(self): with self.assertRaises(jpype.JVMNotRunning): obj = type(self).jcls() def testObjectInvoke(self): with self.assertRaises(jpype.JVMNotRunning): type(self).jobj.wait() def testObjectStr(self): with self.assertRaises(jpype.JVMNotRunning): str(type(self).jobj) def testStringInvoke(self): with self.assertRaises(jpype.JVMNotRunning): type(self).jstr.substring(1) def testStringStr(self): with self.assertRaises(jpype.JVMNotRunning): str(type(self).jstr) jpype-1.6.0/test/jpypetest/test_signals.py000066400000000000000000000053421501674766700207530ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import os import signal import sys import threading import unittest import jpype import subrun @subrun.TestCase class SignalsTest(unittest.TestCase): @classmethod def setUpClass(cls): # set up signal handling before starting jpype cls.sigint_event = threading.Event() cls.sigterm_event = threading.Event() def sigint_handler(sig, frame): cls.sigint_event.set() def sigterm_handler(sig, frame): cls.sigterm_event.set() signal.signal(signal.SIGINT, sigint_handler) signal.signal(signal.SIGTERM, sigterm_handler) # start jpype with interrupt=False to pass back control to python jpype.startJVM(interrupt=False) def setUp(self): if sys.platform == "win32": raise unittest.SkipTest("signals test not applicable on windows") self.sigint_event.clear() self.sigterm_event.clear() def testSigInt(self): os.kill(os.getpid(), signal.SIGINT) # the test is executed in the main thread. The signal cannot interrupt the threading.Event.wait() call # so asserting the return value of `wait` does not work. # However, after returning from the wait, the control should go to the signal handler, and the next `is_set` # call will reflect the actual value of the flag. self.sigint_event.wait(0.1) self.assertTrue(self.sigint_event.is_set()) self.assertFalse(self.sigterm_event.is_set()) def testSigTerm(self): os.kill(os.getpid(), signal.SIGTERM) self.sigterm_event.wait(0.1) if sys.version_info < (3, 10): # python versions below 3.10 do not support PyErr_SetInterruptEx # so SIGTERM will be sent as SIGINT to the interpreter self.assertTrue(self.sigint_event.is_set()) self.assertFalse(self.sigterm_event.is_set()) else: self.assertTrue(self.sigterm_event.is_set()) self.assertFalse(self.sigint_event.is_set()) jpype-1.6.0/test/jpypetest/test_sql_generic.py000066400000000000000000000101771501674766700216100ustar00rootroot00000000000000# This file is Public Domain and may be used without restrictions. import jpype.dbapi2 as dbapi2 import common import time class SQLModuleTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def assertIsSubclass(self, a, b): self.assertTrue(issubclass(a, b), "`%s` is not a subclass of `%s`" % (a.__name__, b.__name__)) def testConstants(self): self.assertEqual(dbapi2.apilevel, "2.0") self.assertEqual(dbapi2.threadsafety, 2) self.assertEqual(dbapi2.paramstyle, "qmark") def testExceptions(self): self.assertIsSubclass(dbapi2.Warning, Exception) self.assertIsSubclass(dbapi2.Error, Exception) self.assertIsSubclass(dbapi2.InterfaceError, dbapi2.Error) self.assertIsSubclass(dbapi2.DatabaseError, dbapi2.Error) self.assertIsSubclass(dbapi2._SQLException, dbapi2.Error) self.assertIsSubclass(dbapi2.DataError, dbapi2.DatabaseError) self.assertIsSubclass(dbapi2.OperationalError, dbapi2.DatabaseError) self.assertIsSubclass(dbapi2.IntegrityError, dbapi2.DatabaseError) self.assertIsSubclass(dbapi2.InternalError, dbapi2.DatabaseError) self.assertIsSubclass(dbapi2.InternalError, dbapi2.DatabaseError) self.assertIsSubclass(dbapi2.ProgrammingError, dbapi2.DatabaseError) self.assertIsSubclass(dbapi2.NotSupportedError, dbapi2.DatabaseError) def testConnectionExceptions(self): cx = dbapi2.Connection self.assertEqual(cx.Warning, dbapi2.Warning) self.assertEqual(cx.Error, dbapi2.Error) self.assertEqual(cx.InterfaceError, dbapi2.InterfaceError) self.assertEqual(cx.DatabaseError, dbapi2.DatabaseError) self.assertEqual(cx.DataError, dbapi2.DataError) self.assertEqual(cx.OperationalError, dbapi2.OperationalError) self.assertEqual(cx.IntegrityError, dbapi2.IntegrityError) self.assertEqual(cx.InternalError, dbapi2.InternalError) self.assertEqual(cx.InternalError, dbapi2.InternalError) self.assertEqual(cx.ProgrammingError, dbapi2.ProgrammingError) self.assertEqual(cx.NotSupportedError, dbapi2.NotSupportedError) def test_Date(self): d1 = dbapi2.Date(2002, 12, 25) # noqa F841 d2 = dbapi2.DateFromTicks( # noqa F841 time.mktime((2002, 12, 25, 0, 0, 0, 0, 0, 0)) ) # Can we assume this? API doesn't specify, but it seems implied # self.assertEqual(str(d1),str(d2)) def test_Time(self): t1 = dbapi2.Time(13, 45, 30) # noqa F841 t2 = dbapi2.TimeFromTicks( # noqa F841 time.mktime((2001, 1, 1, 13, 45, 30, 0, 0, 0)) ) # Can we assume this? API doesn't specify, but it seems implied # self.assertEqual(str(t1),str(t2)) def test_Timestamp(self): t1 = dbapi2.Timestamp(2002, 12, 25, 13, 45, 30) # noqa F841 t2 = dbapi2.TimestampFromTicks( # noqa F841 time.mktime((2002, 12, 25, 13, 45, 30, 0, 0, 0)) ) # Can we assume this? API doesn't specify, but it seems implied # self.assertEqual(str(t1),str(t2)) def test_Binary(self): b = dbapi2.Binary(b"Something") b = dbapi2.Binary(b"") # noqa F841 def test_STRING(self): self.assertTrue(hasattr(dbapi2, "STRING"), "module.STRING must be defined") def test_BINARY(self): self.assertTrue( hasattr(dbapi2, "BINARY"), "module.BINARY must be defined." ) def test_NUMBER(self): self.assertTrue( hasattr(dbapi2, "NUMBER"), "module.NUMBER must be defined." ) def test_DATETIME(self): self.assertTrue( hasattr(dbapi2, "DATETIME"), "module.DATETIME must be defined." ) def test_ROWID(self): self.assertTrue(hasattr(dbapi2, "ROWID"), "module.ROWID must be defined.") class SQLTablesTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testStr(self): for i in dbapi2._types: self.assertIsInstance(str(i), str) def testRepr(self): for i in dbapi2._types: self.assertIsInstance(repr(i), str) jpype-1.6.0/test/jpypetest/test_sql_h2.py000066400000000000000000001541201501674766700205020ustar00rootroot00000000000000# This file is Public Domain and may be used without restrictions, # because nobody should have to waste their lives typing this again. import pytest import jpype from jpype.types import * from jpype import java import jpype.dbapi2 as dbapi2 import common import datetime import decimal import threading java = jpype.java try: import zlib except ImportError: zlib = None # type: ignore[assignment] db_name = "jdbc:h2:mem:testdb" def setUpModule(module): from common import java_version version = java_version() if version[0] == 1 and version[1] == 8: pytest.skip("jdk8 unsupported", allow_module_level=True) class ConnectTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def testConnect(self): cx = dbapi2.connect(db_name) self.assertIsInstance(cx, dbapi2.Connection) def testClose(self): cx = dbapi2.connect(db_name) cx.close() # Closing twice is an error with self.assertRaises(dbapi2.ProgrammingError): cx.close() def testScope(self): with dbapi2.connect(db_name) as cx: pass with self.assertRaises(dbapi2.ProgrammingError): cx.close() def testAdapters(self): cx = dbapi2.connect(db_name) self.assertEqual(cx.adapters, dbapi2._default_adapters) cx = dbapi2.connect(db_name, adapters=None) self.assertEqual(cx.adapters, {}) m = {} cx = dbapi2.connect(db_name, adapters=m) self.assertEqual(cx.adapters, m) def testConverters(self): cx = dbapi2.connect(db_name) self.assertEqual(cx.converters, dbapi2._default_converters) cx = dbapi2.connect(db_name, converters=None) self.assertEqual(cx.converters, {}) m = {} cx = dbapi2.connect(db_name, converters=m) self.assertEqual(cx.converters, m) class ConnectionTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def test_commit(self): with dbapi2.connect(db_name) as cx: # Commit must work without a command cx.commit() def test_rollback(self): with dbapi2.connect(db_name) as cx: # Commit must work without a command cx.rollback() def test_cursor(self): with dbapi2.connect(db_name) as cx: self.assertIsInstance(cx.cursor(), dbapi2.Cursor) def testAdapters(self): with dbapi2.connect(db_name) as cx: m = {'foo': 1} cx.adapters = m self.assertEqual(cx._adapters, m) with self.assertRaises(TypeError): cx.adapters = object() def testConverters(self): with dbapi2.connect(db_name) as cx: m = {'foo': 1} cx.converters = m self.assertEqual(cx._converters, m) with self.assertRaises(TypeError): cx.converters = object() def testClosedClass(self): with dbapi2.connect(db_name) as cx: with self.assertRaises(AttributeError): cx.seters = object() def testAutocommitFail(self): with dbapi2.connect(db_name) as cx: cx.autocommit = True with self.assertRaises(dbapi2.NotSupportedError): cx.commit() with self.assertRaises(dbapi2.NotSupportedError): cx.rollback() def test_typeinfo(self): with dbapi2.connect(db_name) as cx: ti = cx.typeinfo self.assertIsInstance(ti, dict) for p, v in ti.items(): self.assertIsInstance(p, str) self.assertIsInstance(v, dbapi2.JDBCType) with self.assertRaises(dbapi2.ProgrammingError): ti = cx.typeinfo def test_connection(self): with dbapi2.connect(db_name) as cx: c = cx.connection self.assertIsInstance(c, java.sql.Connection) def test_getters(self): with dbapi2.connect(db_name) as cx: cx.getters = dbapi2.GETTERS_BY_NAME self.assertEqual(cx._getters, dbapi2.GETTERS_BY_NAME) self.assertEqual(cx.getters, dbapi2.GETTERS_BY_NAME) cx.getters = dbapi2.GETTERS_BY_TYPE self.assertEqual(cx._getters, dbapi2.GETTERS_BY_TYPE) self.assertEqual(cx.getters, dbapi2.GETTERS_BY_TYPE) def test_setters(self): with dbapi2.connect(db_name) as cx: cx.setters = dbapi2.SETTERS_BY_META self.assertEqual(cx._setters, dbapi2.SETTERS_BY_META) self.assertEqual(cx.setters, dbapi2.SETTERS_BY_META) cx.setters = dbapi2.SETTERS_BY_TYPE self.assertEqual(cx._setters, dbapi2.SETTERS_BY_TYPE) self.assertEqual(cx.setters, dbapi2.SETTERS_BY_TYPE) def test_adapters(self): with dbapi2.connect(db_name) as cx: self.assertEqual(cx.adapters, dbapi2._default_adapters) cx.adapters = None self.assertEqual(cx.adapters, {}) m = {} cx.adapters = m self.assertEqual(cx.adapters, m) with self.assertRaises(dbapi2.InterfaceError): cx.adapters = object() def test_converters(self): with dbapi2.connect(db_name) as cx: self.assertEqual(cx.converters, dbapi2._default_converters) cx.converters = None self.assertEqual(cx.converters, {}) m = {} cx.converters = m self.assertEqual(cx.converters, m) with self.assertRaises(dbapi2.InterfaceError): cx.converters = object() class CursorTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table booze") except dbapi2.Error: pass try: cur.execute("drop table barflys") except dbapi2.Error: pass def testCursorIsolation(self): with dbapi2.connect(db_name) as cx: # Make sure cursors created from the same connection have # the documented transaction isolation level cur1 = cx.cursor() cur2 = cx.cursor() cur1.execute("create table booze (name varchar(20))") cur1.execute("insert into booze values ('Victoria Bitter')") cur2.execute("select name from booze") booze = cur2.fetchall() self.assertEqual(len(booze), 1) self.assertEqual(len(booze[0]), 1) self.assertEqual(booze[0][0], "Victoria Bitter") def testDescription(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") self.assertEqual( cur.description, None, "cursor.description should be none after executing a " "statement that can return no rows (such as create table)", ) cur.execute("select name from booze") self.assertEqual( len(cur.description), 1, "cursor.description describes too many columns" ) self.assertEqual( len(cur.description[0]), 7, "cursor.description[x] tuples must have 7 elements", ) self.assertEqual( cur.description[0][0].lower(), "name", "cursor.description[x][0] must return column name", ) self.assertEqual( cur.description[0][1], dbapi2.STRING, "cursor.description[x][1] must return column type. Got %r" % cur.description[0][1], ) # Make sure self.description gets reset cur.execute("create table barflys (name varchar(20))") self.assertEqual( cur.description, None, "cursor.description not being set to None when executing " "no-result statements (eg. create table)", ) def testRowcount(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") # This was dropped from the specification # self.assertEqual( # cur.rowcount, # -1, # "cursor.rowcount should be -1 after executing no-result " "statements", # ) cur.execute("insert into booze values ('Victoria Bitter')") self.assertEqual( cur.rowcount, 1, "cursor.rowcount should == number or rows inserted, or " "set to -1 after executing an insert statement", ) cur.execute("select name from booze") self.assertEqual( cur.rowcount, -1, "cursor.rowcount should == number of rows returned, or " "set to -1 after executing a select statement", ) cur.execute("create table barflys (name varchar(20))") # THis test does not match jdbc parameters # self.assertEqual( # cur.rowcount, # -1, # "cursor.rowcount not being reset to -1 after executing " # "no-result statements", # ) def testClose(self): cx = dbapi2.connect(db_name) try: cur = cx.cursor() finally: cx.close() # cursor.execute should raise an Error if called after connection # closed with self.assertRaises(dbapi2.Error): cur.execute("create table booze (name varchar(20))") # connection.commit should raise an Error if called after connection' # closed.' self.assertRaises(dbapi2.Error, cx.commit) # connection.close should raise an Error if called more than once self.assertRaises(dbapi2.Error, cx.close) def test_None(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") cur.execute("insert into booze values (NULL)") cur.execute("select name from booze") r = cur.fetchall() self.assertEqual(len(r), 1) self.assertEqual(len(r[0]), 1) self.assertEqual(r[0][0], None, "NULL value not returned as None") def testArraysize(self): # Not much here - rest of the tests for this are in test_fetchmany with dbapi2.connect(db_name) as cx, cx.cursor() as cur: self.assertTrue( hasattr(cur, "arraysize"), "cursor.arraysize must be defined" ) def _paraminsert(self, cur): cur.execute("create table booze (name varchar(20))") cur.execute("insert into booze values ('Victoria Bitter')") self.assertTrue(cur.rowcount in (-1, 1)) cur.execute("insert into booze values (?)", ("Cooper's",)) self.assertTrue(cur.rowcount in (-1, 1)) cur.execute("select name from booze") res = cur.fetchall() self.assertEqual(len(res), 2, "cursor.fetchall returned too few rows") beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual( beers[0], "Cooper's", "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", ) self.assertEqual( beers[1], "Victoria Bitter", "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", ) def test_execute(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: self._paraminsert(cur) def test_executeBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: with self.assertRaises(dbapi2.InterfaceError): cur.execute("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cur.execute(object()) with self.assertRaises(dbapi2.ProgrammingError): cur.execute("inert into booze values (?)", [["?"]]) def test_setinputsizes(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.setinputsizes((25,)) self._paraminsert(cur) # Make sure cursor still works def test_setoutputsize_basic(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.setoutputsize(1000) cur.setoutputsize(2000, 0) self._paraminsert(cur) # Make sure the cursor still works def test_executemany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") largs = [("Cooper's",), ("Boag's",)] margs = [{"beer": "Cooper's"}, {"beer": "Boag's"}] cur.executemany("insert into booze values (?)", largs) self.assertEqual( cur.rowcount, 2, "insert using cursor.executemany set cursor.rowcount to " "incorrect value %r" % cur.rowcount, ) cur.execute("select name from booze") res = cur.fetchall() self.assertEqual( len(res), 2, "cursor.fetchall retrieved incorrect number of rows" ) beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual(beers[0], "Boag's", "incorrect data retrieved") self.assertEqual(beers[1], "Cooper's", "incorrect data retrieved") def test_executemanyGenerator(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: def mygen(ls): for i in ls: yield [i] cu.execute("create table booze (name varchar(20))") cu.executemany("insert into booze values (?)", mygen(self.samples)) cu.execute("select * from booze") f = cu.fetchall() for i, v in enumerate(self.samples): self.assertEqual(f[i][0], v) def test_executemanyIterator(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: class myiter: def __init__(self, ls): self.ls = ls self.i = 0 def __iter__(self): return self def __next__(self): if self.i == len(self.ls): raise StopIteration rc = [self.ls[self.i]] self.i += 1 return rc cu.execute("create table booze (name varchar(20))") cu.executemany("insert into booze values (?)", myiter(self.samples)) cu.execute("select * from booze") f = cu.fetchall() for i, v in enumerate(self.samples): self.assertEqual(f[i][0], v) def test_executemanyBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze (name varchar(20))") with self.assertRaises(TypeError): cu.executemany("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cu.executemany("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cu.executemany(object(), []) cu.executemany("insert into booze values (?)", None) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("inert into booze values (?)", [['?']]) def test_fetchone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchone should raise an Error if called before # executing a select-type query self.assertRaises(dbapi2.ProgrammingError, cur.fetchone) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("create table booze (name varchar(20))") self.assertRaises(dbapi2.Error, cur.fetchone) cur.execute("select name from booze") self.assertEqual( cur.fetchone(), None, "cursor.fetchone should return None if a query retrieves " "no rows", ) self.assertEqual(cur.rowcount, -1) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("insert into booze values ('Victoria Bitter')") self.assertRaises(dbapi2.Error, cur.fetchone) cur.execute("select name from booze") r = cur.fetchone() self.assertEqual( len(r), 1, "cursor.fetchone should have retrieved a single row" ) self.assertEqual( r[0], "Victoria Bitter", "cursor.fetchone retrieved incorrect data" ) self.assertEqual( cur.fetchone(), None, "cursor.fetchone should return None if no more rows available", ) self.assertEqual(cur.rowcount, -1) samples = [ "Carlton Cold", "Carlton Draft", "Mountain Goat", "Redback", "Victoria Bitter", "XXXX", ] def _populate(self): """ Return a list of sql commands to setup the DB for the fetch tests. """ populate = [ "insert into booze values ('{}')".format(s) for s in self.samples ] return populate def test_fetchmany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchmany should raise an Error if called without # issuing a query self.assertRaises(dbapi2.ProgrammingError, cur.fetchmany, 4) cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) cur.execute("select name from booze") r = cur.fetchmany() self.assertEqual( len(r), 1, "cursor.fetchmany retrieved incorrect number of rows, " "default of arraysize is one.", ) cur.arraysize = 10 r = cur.fetchmany(3) # Should get 3 rows self.assertEqual( len(r), 3, "cursor.fetchmany retrieved incorrect number of rows" ) r = cur.fetchmany(4) # Should get 2 more self.assertEqual( len(r), 2, "cursor.fetchmany retrieved incorrect number of rows" ) r = cur.fetchmany(4) # Should be an empty sequence self.assertEqual( len(r), 0, "cursor.fetchmany should return an empty sequence after " "results are exhausted", ) self.assertEqual(cur.rowcount, -1) # Same as above, using cursor.arraysize cur.arraysize = 4 cur.execute("select name from booze") r = cur.fetchmany() # Should get 4 rows self.assertEqual( len(r), 4, "cursor.arraysize not being honoured by fetchmany" ) r = cur.fetchmany() # Should get 2 more self.assertEqual(len(r), 2) r = cur.fetchmany() # Should be an empty sequence self.assertEqual(len(r), 0) self.assertEqual(cur.rowcount, -1) cur.arraysize = 6 cur.execute("select name from booze") rows = cur.fetchmany() # Should get all rows self.assertTrue(cur.rowcount in (-1, 6)) self.assertEqual(len(rows), 6) self.assertEqual(len(rows), 6) rows = [r[0] for r in rows] rows.sort() # Make sure we get the right data back out for i in range(0, 6): self.assertEqual( rows[i], self.samples[i], "incorrect data retrieved by cursor.fetchmany", ) rows = cur.fetchmany() # Should return an empty list self.assertEqual( len(rows), 0, "cursor.fetchmany should return an empty sequence if " "called after the whole result set has been fetched", ) self.assertEqual(cur.rowcount, -1) cur.execute("create table barflys (name varchar(20))") cur.execute("select name from barflys") r = cur.fetchmany() # Should get empty sequence self.assertEqual( len(r), 0, "cursor.fetchmany should return an empty sequence if " "query retrieved no rows", ) def test_fetchall(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchall should raise an Error if called # without executing a query that may return rows (such # as a select) self.assertRaises(dbapi2.Error, cur.fetchall) cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) # cursor.fetchall should raise an Error if called # after executing a statement that cannot return rows self.assertRaises(dbapi2.ProgrammingError, cur.fetchall) cur.execute("select name from booze") rows = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows), len(self.samples), "cursor.fetchall did not retrieve all rows", ) rows = [r[0] for r in rows] rows.sort() for i in range(0, len(self.samples)): self.assertEqual( rows[i], self.samples[i], "cursor.fetchall retrieved incorrect rows" ) rows = cur.fetchall() self.assertEqual( len(rows), 0, "cursor.fetchall should return an empty list if called " "after the whole result set has been fetched", ) self.assertEqual(cur.rowcount, -1) cur.execute("create table barflys (name varchar(20))") cur.execute("select name from barflys") rows = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows), 0, "cursor.fetchall should return an empty list if " "a select query returns no rows", ) def test_mixedfetch(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) cur.execute("select name from booze") self.assertEqual(cur.rowcount, -1) rows1 = cur.fetchone() rows23 = cur.fetchmany(2) rows4 = cur.fetchone() rows56 = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows23), 2, "fetchmany returned incorrect number of rows" ) self.assertEqual( len(rows56), 2, "fetchall returned incorrect number of rows" ) rows = [rows1[0]] rows.extend([rows23[0][0], rows23[1][0]]) rows.append(rows4[0]) rows.extend([rows56[0][0], rows56[1][0]]) rows.sort() for i in range(0, len(self.samples)): self.assertEqual( rows[i], self.samples[i], "incorrect data retrieved or inserted" ) def test_nextset(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze (name varchar(20))") booze = ["Wiskey", ] cu.execute("insert into booze(name) values (?)", booze) self.assertEqual(cu.rowcount, 1) cu.execute("select * from booze; select * from booze") self.assertEqual(cu.fetchone(), booze) nxt = cu.nextset() self.assertTrue(nxt in (True, None)) # H2 does not support multiple result sets if nxt: self.assertEqual(cu.fetchone(), booze) def test_lastrowid(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(id identity auto_increment not null, name varchar(255), PRIMARY KEY (id))") cu.execute("insert into booze(name) values('hello')", keys=True) id0 = cu.lastrowid self.assertIsInstance(id0, int) # Call more than once id1 = cu.lastrowid self.assertIsInstance(id1, int) cx.commit() cu.execute("insert into booze(name) values('there')", keys=True) id2 = cu.lastrowid self.assertIsInstance(id2, int) cu.execute("select * from booze") f = cu.fetchall() self.assertEqual(cu.lastrowid, None) self.assertEqual(f[0][0], id0) self.assertEqual(f[1][0], id2) def test_lastrowidMany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(id identity auto_increment not null, name varchar(255), PRIMARY KEY (id))") cu.executemany("insert into booze(name) values(?)", [['Redback'], ['Fat Yak']], keys=True) ids = cu.lastrowid self.assertIsInstance(ids, list) self.assertEqual(len(ids), 2) cu.execute("select * from booze") f = cu.fetchall() self.assertEqual(f[0][0], ids[0]) self.assertEqual(f[1][0], ids[1]) def test_iter(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: ls = [['Tooheys Old'], ['Nine Tales Original Amber Ale'], ['Dogbolter']] cu.execute("create table booze(name varchar(255))") cu.executemany("insert into booze(name) values(?)", ls, keys=True) # make sure we throw if there is no result set with self.assertRaises(dbapi2.ProgrammingError): for booze in cu: pass cu.execute("select * from booze") for i, booze in enumerate(cu): self.assertEqual(ls[i], booze) def testNoAdapter(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name) values(?)", [object()]) def testBadParameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name) values(?)", object()) def test_callproc(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: r = cu.callproc("lower", ("FOO",)) self.assertEqual(len(r), 1) self.assertEqual(r[0], "FOO") r = cu.fetchall() self.assertEqual(len(r), 1) self.assertEqual(len(r[0]), 1) self.assertEqual(r[0][0], "foo") def test_callprocBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): r = cu.callproc("not_there", ("FOO",)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc(object(), ("FOO",)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc("lower", (object(),)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc("lower", object()) def test_close(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.close() with self.assertRaises(dbapi2.ProgrammingError): cu.close() def test_parameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): p = cu.parameters cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) p = cu.parameters self.assertEqual(p[0][0:2], ('VARCHAR', dbapi2.VARCHAR)) def test_resultSet(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) cu.execute("select * from booze") rs = cu.resultSet self.assertIsInstance(rs, java.sql.ResultSet) def test_fetchClosed(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) cu.execute("select * from booze") cu.close() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchmany() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchall() self.assertEqual(cu.resultSet, None) def test_executeBadParameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: def mygen(ls): for i in ls: yield i cu.execute("create table booze(name varchar(50), price integer)") with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze(name,price) values(?,?)", [('Hahn Super Dry', 2), ('Coors',)]) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", {'name': 'Budweiser', 'price': 10}) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze(name,price) values(?,?)", mygen([['Budweiser']])) cu.execute("insert into booze(name,price) values(?,?)", mygen(['Pabst Blue Robot', 20000])) with self.assertRaises(dbapi2.ProgrammingError): cu.execute("insert into booze(name,price) values(?,?)", mygen(['Budweiser'])) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", 'Budweiser') with self.assertRaises(dbapi2.ProgrammingError): cu.execute("insert into booze(name,price) values(?,?)", mygen(['Budweiser', 2, 'no'])) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", object()) @common.unittest.skipUnless(zlib, "requires zlib") class AdapterTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testOnConnector(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(255))") cx.adapters[object] = lambda x: "none" cu.execute("insert into test(name) values(?)", [object()]) cu.execute("select * from test") f = cu.fetchall() self.assertEqual(f[0][0], "none") @common.unittest.skipUnless(zlib, "requires zlib") class ConverterTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass @staticmethod def convert(s): return zlib.decompress(s) def testConvertersBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone(converters=[]) cu.execute("select name from test") with self.assertRaises(dbapi2.InterfaceError): cu.fetchone(converters=[int]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone(converters=[str, str]) def testConvertersNone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(converters=None) self.assertIsInstance(f[0], java.lang.String) cu.execute("select name from test") def testConvertersPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(converters=[str]) self.assertIsInstance(f[0], str) cu.execute("select name from test") def test_fetchoneTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(types=[dbapi2.STRING]) self.assertIsInstance(f[0], str) def test_fetchmanyTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchmany(types=[dbapi2.STRING]) self.assertIsInstance(f[0][0], str) def test_fetchallTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchall(types=[dbapi2.STRING]) self.assertIsInstance(f[0][0], str) def testTypesPositionalBAD(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): f = cu.fetchone(types=[dbapi2.STRING, dbapi2.STRING]) with self.assertRaises(dbapi2.ProgrammingError): f = cu.fetchone(types=[]) @common.unittest.skipUnless(zlib, "requires zlib") class GettersTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass class TransactionsTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testTransactionRollbackCreate(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("begin") cu.execute("create table test(name VARCHAR(10))") cx.rollback() # with self.assertRaises(dbapi2.ProgrammingError): cu.execute("select * from test") def testTransactionRollbackInsert(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("create table test(name VARCHAR(10))") cx.commit() cu.execute("begin") cu.execute("insert into test(name) values('alice')") cx.rollback() # Alice should go away result = cu.execute("select * from test").fetchall() self.assertEqual(result, []) def testTransactionRollbackClose(self): with dbapi2.connect(db_name) as cx2: with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('alice')") cx.commit() cu.execute("begin") cu.execute("insert into test(name) values('bob')") result = cu.execute("select * from test").fetchall() self.assertEqual(result, [['alice'], ['bob']]) # Bob should go away with cx2.cursor() as cu: result = cu.execute("select * from test").fetchall() self.assertEqual(result, [['alice']]) class TypeTestCase(common.JPypeTestCase): """ This test is db dependent, but needed to check all code paths. """ def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testTime(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME TIME)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('12:05:10')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.time) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = jpype.java.sql.Time(1, 2, 3) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.time(1, 2, 3)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], jpype.java.sql.Time) cu.execute("delete from test") # Test with dbapi2.Time t1 = dbapi2.Time(6, 21, 32) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.time(6, 21, 32)) def testDate(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME DATE)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('2012-02-05')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.date) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = java.sql.Date(1972 - 1900, 4 - 1, 1) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.date(1972, 4, 1)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], java.sql.Date) cu.execute("delete from test") # Test with dbapi2.Date t1 = dbapi2.Date(2020, 5, 21) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.date(2020, 5, 21)) def testTimestamp(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME TIMESTAMP)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('2012-02-05 12:02:45.123')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.date) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = java.sql.Timestamp(1972 - 1900, 4 - 1, 1, 12, 2, 45, 123456000) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.datetime(1972, 4, 1, 12, 2, 45, 123456)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], java.sql.Timestamp) cu.execute("delete from test") # Test with dbapi2.Date t1 = dbapi2.Timestamp(2020, 5, 21, 3, 4, 5, 123122000) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.datetime(2020, 5, 21, 3, 4, 5, 123122)) def _testInt(self, tp, desc, jtype, null=True): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [123]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 123) self.assertIsInstance(f[0], int) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], 123) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") if null: cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testTinyInt(self): self._testInt('TINYINT', ('NAME', 'TINYINT'), jpype.JShort) def testBigInt(self): self._testInt('BIGINT', ('NAME', 'BIGINT'), jpype.JLong) def testIdentity(self): self._testInt('IDENTITY', ('NAME', 'BIGINT'), jpype.JLong, False) def testInteger(self): self._testInt('INTEGER', ('NAME', 'INTEGER'), jpype.JInt) def testSmallInt(self): self._testInt('SMALLINT', ('NAME', 'SMALLINT'), jpype.JShort) def _testFloat(self, tp, desc, jtype): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [1.25]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], float) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testFloat(self): self._testFloat('FLOAT', ('NAME', 'DOUBLE'), jpype.JDouble) def testDouble(self): self._testFloat('DOUBLE', ('NAME', 'DOUBLE'), jpype.JDouble) def testReal(self): self._testFloat('REAL', ('NAME', 'REAL'), jpype.JFloat) def _testNumeric(self, tp, desc, jtype): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s(5,2))" % tp) cu.execute("insert into test(NAME) values(?)", [1.25]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], decimal.Decimal) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], decimal.Decimal(1.25)) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testNumeric(self): self._testNumeric('NUMERIC', ('NAME', 'DECIMAL'), java.math.BigDecimal) def testDecimal(self): self._testNumeric('DECIMAL', ('NAME', 'DECIMAL'), java.math.BigDecimal) def _testBinary(self, tp, desc, jtype): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) v = bytes([1, 2, 3]) cu.execute("insert into test(NAME) values(?)", [v]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], v) self.assertIsInstance(f[0], bytes) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(bytes(f[0]), v) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testBinary(self): self._testBinary('BINARY(3)', ('NAME', 'VARBINARY'), JArray(JByte)) def testBlob(self): self._testBinary('BLOB', ('NAME', 'BLOB'), JArray(JByte)) def testLongVarBinary(self): self._testBinary('LONGVARBINARY', ('NAME', 'VARBINARY'), JArray(JByte)) def testVarBinary(self): self._testBinary('VARBINARY(10)', ('NAME', 'VARBINARY'), JArray(JByte)) def _testChars(self, tp, desc, jtype, v="hello"): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [v]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], v) self.assertIsInstance(f[0], str) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], v) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testChar(self): self._testChars('CHAR', ('NAME', 'CHAR'), java.lang.String, 'a') def testVarChar(self): self._testChars('VARCHAR', ('NAME', 'VARCHAR'), java.lang.String) def testLongVarChar(self): self._testChars('LONGVARCHAR', ('NAME', 'VARCHAR'), java.lang.String) def testClob(self): self._testChars('CLOB', ('NAME', 'CLOB'), java.lang.String) def testBoolean(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME BOOLEAN)") cu.execute("insert into test(NAME) values(?)", [True]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], True) self.assertIsInstance(f[0], int) self.assertEqual(cu.description[0][0:2], ('NAME', 'BOOLEAN')) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], True) self.assertIsInstance(f[0], type(True)) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) class OtherTestCase(common.JPypeTestCase): """ This test is db dependent, but needed to check all code paths. """ def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testJSON(self): got = [] class MyType(dbapi2.JDBCType): def get(self, rs, column, st): got.append(column) return dbapi2.JDBCType.get(self, rs, column, st) # Register a new type JSON = MyType("JSON", None) with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_TYPE) as cx, cx.cursor() as cu: cu.execute("create table test(name JSON)") cu.execute("insert into test(name) values('{age: 31}')") f = cu.execute("select * from test").fetchone() self.assertEqual(got, [1]) with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(name JSON)") cu.execute("insert into test(name) values('{age: 31}')") f = cu.execute("select * from test").fetchone() self.assertEqual(got, [1, 1]) del dbapi2._registry[JSON] class ThreadingTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def _testThread(self, cu, cmd, args, exc): error = [] def run(): try: cmd(*args) except Exception as ex: error.append(ex) t = threading.Thread(target=run) t.start() t.join() self.assertEqual(len(error), 1) with self.assertRaises(exc): raise error[0] def test_execute(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") self._testThread(cu, cu.execute, ("insert into test(name) values ('a')",), dbapi2.ProgrammingError) def test_executemany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") self._testThread(cu, cu.executemany, ("insert into test(name) values ('?')", [[1]]), dbapi2.ProgrammingError) def test_fetchone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchone, (), dbapi2.ProgrammingError) def test_fetchmany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchmany, (), dbapi2.ProgrammingError) def test_fetchall(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchall, (), dbapi2.ProgrammingError) def test_close(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.close, (), dbapi2.ProgrammingError) def test_callproc(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.callproc, ("lower", ["FOO"]), dbapi2.ProgrammingError) jpype-1.6.0/test/jpypetest/test_sql_hsqldb.py000066400000000000000000001513301501674766700214460ustar00rootroot00000000000000# This file is Public Domain and may be used without restrictions, # because noone should have to waste their lives typing this again. import _jpype import jpype from jpype.types import * from jpype import java import jpype.dbapi2 as dbapi2 import common import time import datetime import decimal import threading java = jpype.java try: import zlib except ImportError: zlib = None # type: ignore[assignment] db_name = "jdbc:hsqldb:mem:myDb" #db_name = "jdbc:derby:memory:myDb" #first = "jdbc:derby:memory:myDb;create=True" def setUpModule(module): from common import java_version import pytest version = java_version() if version[0] == 1 and version[1] == 8: pytest.skip("jdk8 unsupported", allow_module_level=True) @common.unittest.skipUnless(zlib, "requires zlib") class ConnectTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def testConnect(self): cx = dbapi2.connect(db_name) self.assertIsInstance(cx, dbapi2.Connection) def testClose(self): cx = dbapi2.connect(db_name) cx.close() # Closing twice is an error with self.assertRaises(dbapi2.ProgrammingError): cx.close() def testScope(self): with dbapi2.connect(db_name) as cx: pass with self.assertRaises(dbapi2.ProgrammingError): cx.close() def testAdapters(self): cx = dbapi2.connect(db_name) self.assertEqual(cx.adapters, dbapi2._default_adapters) cx = dbapi2.connect(db_name, adapters=None) self.assertEqual(cx.adapters, {}) m = {} cx = dbapi2.connect(db_name, adapters=m) self.assertEqual(cx.adapters, m) def testConverters(self): cx = dbapi2.connect(db_name) self.assertEqual(cx.converters, dbapi2._default_converters) cx = dbapi2.connect(db_name, converters=None) self.assertEqual(cx.converters, {}) m = {} cx = dbapi2.connect(db_name, converters=m) self.assertEqual(cx.converters, m) class ConnectionTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def test_commit(self): with dbapi2.connect(db_name) as cx: # Commit must work without a command cx.commit() def test_rollback(self): with dbapi2.connect(db_name) as cx: # Commit must work without a command cx.rollback() def test_cursor(self): with dbapi2.connect(db_name) as cx: self.assertIsInstance(cx.cursor(), dbapi2.Cursor) def testAdapters(self): with dbapi2.connect(db_name) as cx: m = {'foo': 1} cx.adapters = m self.assertEqual(cx._adapters, m) with self.assertRaises(TypeError): cx.adapters = object() def testConverters(self): with dbapi2.connect(db_name) as cx: m = {'foo': 1} cx.converters = m self.assertEqual(cx._converters, m) with self.assertRaises(TypeError): cx.converters = object() def testClosedClass(self): with dbapi2.connect(db_name) as cx: with self.assertRaises(AttributeError): cx.seters = object() def testAutocommitFail(self): with dbapi2.connect(db_name) as cx: cx.autocommit = True with self.assertRaises(dbapi2.NotSupportedError): cx.commit() with self.assertRaises(dbapi2.NotSupportedError): cx.rollback() def test_typeinfo(self): with dbapi2.connect(db_name) as cx: ti = cx.typeinfo self.assertIsInstance(ti, dict) for p, v in ti.items(): self.assertIsInstance(p, str) self.assertIsInstance(v, dbapi2.JDBCType) with self.assertRaises(dbapi2.ProgrammingError): ti = cx.typeinfo def test_connection(self): with dbapi2.connect(db_name) as cx: c = cx.connection self.assertIsInstance(c, java.sql.Connection) def test_getters(self): with dbapi2.connect(db_name) as cx: cx.getters = dbapi2.GETTERS_BY_NAME self.assertEqual(cx._getters, dbapi2.GETTERS_BY_NAME) self.assertEqual(cx.getters, dbapi2.GETTERS_BY_NAME) cx.getters = dbapi2.GETTERS_BY_TYPE self.assertEqual(cx._getters, dbapi2.GETTERS_BY_TYPE) self.assertEqual(cx.getters, dbapi2.GETTERS_BY_TYPE) def test_setters(self): with dbapi2.connect(db_name) as cx: cx.setters = dbapi2.SETTERS_BY_META self.assertEqual(cx._setters, dbapi2.SETTERS_BY_META) self.assertEqual(cx.setters, dbapi2.SETTERS_BY_META) cx.setters = dbapi2.SETTERS_BY_TYPE self.assertEqual(cx._setters, dbapi2.SETTERS_BY_TYPE) self.assertEqual(cx.setters, dbapi2.SETTERS_BY_TYPE) def test_adapters(self): with dbapi2.connect(db_name) as cx: self.assertEqual(cx.adapters, dbapi2._default_adapters) cx.adapters = None self.assertEqual(cx.adapters, {}) m = {} cx.adapters = m self.assertEqual(cx.adapters, m) with self.assertRaises(dbapi2.InterfaceError): cx.adapters = object() def test_converters(self): with dbapi2.connect(db_name) as cx: self.assertEqual(cx.converters, dbapi2._default_converters) cx.converters = None self.assertEqual(cx.converters, {}) m = {} cx.converters = m self.assertEqual(cx.converters, m) with self.assertRaises(dbapi2.InterfaceError): cx.converters = object() class CursorTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table booze") except dbapi2.Error: pass try: cur.execute("drop table barflys") except dbapi2.Error: pass def testCursorIsolation(self): with dbapi2.connect(db_name) as cx: # Make sure cursors created from the same connection have # the documented transaction isolation level cur1 = cx.cursor() cur2 = cx.cursor() cur1.execute("create table booze (name varchar(20))") cur1.execute("insert into booze values ('Victoria Bitter')") cur2.execute("select name from booze") booze = cur2.fetchall() self.assertEqual(len(booze), 1) self.assertEqual(len(booze[0]), 1) self.assertEqual(booze[0][0], "Victoria Bitter") def testDescription(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") self.assertEqual( cur.description, None, "cursor.description should be none after executing a " "statement that can return no rows (such as create table)", ) cur.execute("select name from booze") self.assertEqual( len(cur.description), 1, "cursor.description describes too many columns" ) self.assertEqual( len(cur.description[0]), 7, "cursor.description[x] tuples must have 7 elements", ) self.assertEqual( cur.description[0][0].lower(), "name", "cursor.description[x][0] must return column name", ) self.assertEqual( cur.description[0][1], dbapi2.STRING, "cursor.description[x][1] must return column type. Got %r" % cur.description[0][1], ) # Make sure self.description gets reset cur.execute("create table barflys (name varchar(20))") self.assertEqual( cur.description, None, "cursor.description not being set to None when executing " "no-result statements (eg. create table)", ) def testRowcount(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") # This was dropped from the specification # self.assertEqual( # cur.rowcount, # -1, # "cursor.rowcount should be -1 after executing no-result " "statements", # ) cur.execute("insert into booze values ('Victoria Bitter')") self.assertEqual( cur.rowcount, 1, "cursor.rowcount should == number or rows inserted, or " "set to -1 after executing an insert statement", ) cur.execute("select name from booze") self.assertEqual( cur.rowcount, -1, "cursor.rowcount should == number of rows returned, or " "set to -1 after executing a select statement", ) cur.execute("create table barflys (name varchar(20))") # THis test does not match jdbc parameters # self.assertEqual( # cur.rowcount, # -1, # "cursor.rowcount not being reset to -1 after executing " # "no-result statements", # ) def testClose(self): cx = dbapi2.connect(db_name) try: cur = cx.cursor() finally: cx.close() # cursor.execute should raise an Error if called after connection # closed with self.assertRaises(dbapi2.Error): cur.execute("create table booze (name varchar(20))") # connection.commit should raise an Error if called after connection' # closed.' self.assertRaises(dbapi2.Error, cx.commit) # connection.close should raise an Error if called more than once self.assertRaises(dbapi2.Error, cx.close) def test_None(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") cur.execute("insert into booze values (NULL)") cur.execute("select name from booze") r = cur.fetchall() self.assertEqual(len(r), 1) self.assertEqual(len(r[0]), 1) self.assertEqual(r[0][0], None, "NULL value not returned as None") def testArraysize(self): # Not much here - rest of the tests for this are in test_fetchmany with dbapi2.connect(db_name) as cx, cx.cursor() as cur: self.assertTrue( hasattr(cur, "arraysize"), "cursor.arraysize must be defined" ) def _paraminsert(self, cur): cur.execute("create table booze (name varchar(20))") cur.execute("insert into booze values ('Victoria Bitter')") self.assertTrue(cur.rowcount in (-1, 1)) cur.execute("insert into booze values (?)", ("Cooper's",)) self.assertTrue(cur.rowcount in (-1, 1)) cur.execute("select name from booze") res = cur.fetchall() self.assertEqual(len(res), 2, "cursor.fetchall returned too few rows") beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual( beers[0], "Cooper's", "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", ) self.assertEqual( beers[1], "Victoria Bitter", "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", ) def test_execute(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: self._paraminsert(cur) def test_executeBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: with self.assertRaises(dbapi2.InterfaceError): cur.execute("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cur.execute(object()) with self.assertRaises(dbapi2.ProgrammingError): cur.execute("inert into booze values (?)", [["?"]]) def test_setinputsizes(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.setinputsizes((25,)) self._paraminsert(cur) # Make sure cursor still works def test_setoutputsize_basic(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.setoutputsize(1000) cur.setoutputsize(2000, 0) self._paraminsert(cur) # Make sure the cursor still works def test_executemany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") largs = [("Cooper's",), ("Boag's",)] margs = [{"beer": "Cooper's"}, {"beer": "Boag's"}] cur.executemany("insert into booze values (?)", largs) self.assertEqual( cur.rowcount, 2, "insert using cursor.executemany set cursor.rowcount to " "incorrect value %r" % cur.rowcount, ) cur.execute("select name from booze") res = cur.fetchall() self.assertEqual( len(res), 2, "cursor.fetchall retrieved incorrect number of rows" ) beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual(beers[0], "Boag's", "incorrect data retrieved") self.assertEqual(beers[1], "Cooper's", "incorrect data retrieved") def test_executemanyGenerator(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: def mygen(ls): for i in ls: yield [i] cu.execute("create table booze (name varchar(20))") cu.executemany("insert into booze values (?)", mygen(self.samples)) cu.execute("select * from booze") f = cu.fetchall() for i, v in enumerate(self.samples): self.assertEqual(f[i][0], v) def test_executemanyIterator(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: class myiter: def __init__(self, ls): self.ls = ls self.i = 0 def __iter__(self): return self def __next__(self): if self.i == len(self.ls): raise StopIteration rc = [self.ls[self.i]] self.i += 1 return rc cu.execute("create table booze (name varchar(20))") cu.executemany("insert into booze values (?)", myiter(self.samples)) cu.execute("select * from booze") f = cu.fetchall() for i, v in enumerate(self.samples): self.assertEqual(f[i][0], v) def test_executemanyBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze (name varchar(20))") with self.assertRaises(TypeError): cu.executemany("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cu.executemany("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cu.executemany(object(), []) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze values (?)", None) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("inert into booze values (?)", [['?']]) def test_fetchone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchone should raise an Error if called before # executing a select-type query self.assertRaises(dbapi2.ProgrammingError, cur.fetchone) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("create table booze (name varchar(20))") self.assertRaises(dbapi2.Error, cur.fetchone) cur.execute("select name from booze") self.assertEqual( cur.fetchone(), None, "cursor.fetchone should return None if a query retrieves " "no rows", ) self.assertEqual(cur.rowcount, -1) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("insert into booze values ('Victoria Bitter')") self.assertRaises(dbapi2.Error, cur.fetchone) cur.execute("select name from booze") r = cur.fetchone() self.assertEqual( len(r), 1, "cursor.fetchone should have retrieved a single row" ) self.assertEqual( r[0], "Victoria Bitter", "cursor.fetchone retrieved incorrect data" ) self.assertEqual( cur.fetchone(), None, "cursor.fetchone should return None if no more rows available", ) self.assertEqual(cur.rowcount, -1) samples = [ "Carlton Cold", "Carlton Draft", "Mountain Goat", "Redback", "Victoria Bitter", "XXXX", ] def _populate(self): """ Return a list of sql commands to setup the DB for the fetch tests. """ populate = [ "insert into booze values ('{}')".format(s) for s in self.samples ] return populate def test_fetchmany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchmany should raise an Error if called without # issuing a query self.assertRaises(dbapi2.ProgrammingError, cur.fetchmany, 4) cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) cur.execute("select name from booze") r = cur.fetchmany() self.assertEqual( len(r), 1, "cursor.fetchmany retrieved incorrect number of rows, " "default of arraysize is one.", ) cur.arraysize = 10 r = cur.fetchmany(3) # Should get 3 rows self.assertEqual( len(r), 3, "cursor.fetchmany retrieved incorrect number of rows" ) r = cur.fetchmany(4) # Should get 2 more self.assertEqual( len(r), 2, "cursor.fetchmany retrieved incorrect number of rows" ) r = cur.fetchmany(4) # Should be an empty sequence self.assertEqual( len(r), 0, "cursor.fetchmany should return an empty sequence after " "results are exhausted", ) self.assertEqual(cur.rowcount, -1) # Same as above, using cursor.arraysize cur.arraysize = 4 cur.execute("select name from booze") r = cur.fetchmany() # Should get 4 rows self.assertEqual( len(r), 4, "cursor.arraysize not being honoured by fetchmany" ) r = cur.fetchmany() # Should get 2 more self.assertEqual(len(r), 2) r = cur.fetchmany() # Should be an empty sequence self.assertEqual(len(r), 0) self.assertEqual(cur.rowcount, -1) cur.arraysize = 6 cur.execute("select name from booze") rows = cur.fetchmany() # Should get all rows self.assertTrue(cur.rowcount in (-1, 6)) self.assertEqual(len(rows), 6) self.assertEqual(len(rows), 6) rows = [r[0] for r in rows] rows.sort() # Make sure we get the right data back out for i in range(0, 6): self.assertEqual( rows[i], self.samples[i], "incorrect data retrieved by cursor.fetchmany", ) rows = cur.fetchmany() # Should return an empty list self.assertEqual( len(rows), 0, "cursor.fetchmany should return an empty sequence if " "called after the whole result set has been fetched", ) self.assertEqual(cur.rowcount, -1) cur.execute("create table barflys (name varchar(20))") cur.execute("select name from barflys") r = cur.fetchmany() # Should get empty sequence self.assertEqual( len(r), 0, "cursor.fetchmany should return an empty sequence if " "query retrieved no rows", ) def test_fetchall(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchall should raise an Error if called # without executing a query that may return rows (such # as a select) self.assertRaises(dbapi2.Error, cur.fetchall) cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) # cursor.fetchall should raise an Error if called # after executing a statement that cannot return rows self.assertRaises(dbapi2.ProgrammingError, cur.fetchall) cur.execute("select name from booze") rows = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows), len(self.samples), "cursor.fetchall did not retrieve all rows", ) rows = [r[0] for r in rows] rows.sort() for i in range(0, len(self.samples)): self.assertEqual( rows[i], self.samples[i], "cursor.fetchall retrieved incorrect rows" ) rows = cur.fetchall() self.assertEqual( len(rows), 0, "cursor.fetchall should return an empty list if called " "after the whole result set has been fetched", ) self.assertEqual(cur.rowcount, -1) cur.execute("create table barflys (name varchar(20))") cur.execute("select name from barflys") rows = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows), 0, "cursor.fetchall should return an empty list if " "a select query returns no rows", ) def test_mixedfetch(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) cur.execute("select name from booze") self.assertEqual(cur.rowcount, -1) rows1 = cur.fetchone() rows23 = cur.fetchmany(2) rows4 = cur.fetchone() rows56 = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows23), 2, "fetchmany returned incorrect number of rows" ) self.assertEqual( len(rows56), 2, "fetchall returned incorrect number of rows" ) rows = [rows1[0]] rows.extend([rows23[0][0], rows23[1][0]]) rows.append(rows4[0]) rows.extend([rows56[0][0], rows56[1][0]]) rows.sort() for i in range(0, len(self.samples)): self.assertEqual( rows[i], self.samples[i], "incorrect data retrieved or inserted" ) def test_nextset(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze (name varchar(20))") booze = ["Wiskey", ] cu.execute("insert into booze(name) values (?)", booze) self.assertEqual(cu.rowcount, 1) with self.assertRaises(dbapi2.ProgrammingError): cu.execute("select * from booze; select * from booze") def test_lastrowid(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(id INTEGER IDENTITY PRIMARY KEY, name varchar(255))") cu.execute("insert into booze(name) values('hello')", keys=True) id0 = cu.lastrowid self.assertIsInstance(id0, int) # Call more than once id1 = cu.lastrowid self.assertIsInstance(id1, int) cx.commit() cu.execute("insert into booze(name) values('there')", keys=True) id2 = cu.lastrowid self.assertIsInstance(id2, int) cu.execute("select * from booze") f = cu.fetchall() self.assertEqual(cu.lastrowid, None) self.assertEqual(f[0][0], id0) self.assertEqual(f[1][0], id2) def test_lastrowidMany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(id INTEGER IDENTITY PRIMARY KEY, name varchar(255))") cu.executemany("insert into booze(name) values(?)", [['Redback'], ['Fat Yak']], keys=True) ids = cu.lastrowid self.assertIsInstance(ids, list) self.assertEqual(len(ids), 2) cu.execute("select * from booze") f = cu.fetchall() self.assertEqual(f[0][0], ids[0]) self.assertEqual(f[1][0], ids[1]) def test_iter(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: ls = [['Tooheys Old'], ['Nine Tales Original Amber Ale'], ['Dogbolter']] cu.execute("create table booze(name varchar(255))") cu.executemany("insert into booze(name) values(?)", ls, keys=True) # make sure we throw if there is no result set with self.assertRaises(dbapi2.ProgrammingError): for booze in cu: pass cu.execute("select * from booze") for i, booze in enumerate(cu): self.assertEqual(ls[i], booze) def testNoAdapter(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name) values(?)", [object()]) def testBadParameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name) values(?)", object()) def test_callproc(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: r = cu.callproc("lower", ("FOO",)) self.assertEqual(len(r), 1) self.assertEqual(r[0], "FOO") r = cu.fetchall() self.assertEqual(len(r), 1) self.assertEqual(len(r[0]), 1) self.assertEqual(r[0][0], "foo") def test_callprocBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): r = cu.callproc("not_there", ("FOO",)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc(object(), ("FOO",)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc("lower", (object(),)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc("lower", object()) def test_close(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.close() with self.assertRaises(dbapi2.ProgrammingError): cu.close() def test_parameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): p = cu.parameters cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) p = cu.parameters self.assertEqual(p[0][0:2], ('VARCHAR', dbapi2.VARCHAR)) def test_resultSet(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) cu.execute("select * from booze") rs = cu.resultSet self.assertIsInstance(rs, java.sql.ResultSet) def test_fetchClosed(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) cu.execute("select * from booze") cu.close() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchmany() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchall() self.assertEqual(cu.resultSet, None) def test_executeBadParameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: def mygen(ls): for i in ls: yield i cu.execute("create table booze(name varchar(50), price integer)") with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze(name,price) values(?,?)", [('Hahn Super Dry', 2), ('Coors',)]) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", {'name': 'Budweiser', 'price': 10}) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze(name,price) values(?,?)", mygen([['Budweiser']])) cu.execute("insert into booze(name,price) values(?,?)", mygen(['Pabst Blue Robot', 20000])) with self.assertRaises(dbapi2.ProgrammingError): cu.execute("insert into booze(name,price) values(?,?)", mygen(['Budweiser'])) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", 'Budweiser') with self.assertRaises(dbapi2.ProgrammingError): cu.execute("insert into booze(name,price) values(?,?)", mygen(['Budweiser', 2, 'no'])) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", object()) @common.unittest.skipUnless(zlib, "requires zlib") class AdapterTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testOnConnector(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(255))") cx.adapters[object] = lambda x: "none" cu.execute("insert into test(name) values(?)", [object()]) cu.execute("select * from test") f = cu.fetchall() self.assertEqual(f[0][0], "none") @common.unittest.skipUnless(zlib, "requires zlib") class ConverterTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass @staticmethod def convert(s): return zlib.decompress(s) def testConvertersBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone(converters=[]) cu.execute("select name from test") with self.assertRaises(dbapi2.InterfaceError): cu.fetchone(converters=[int]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone(converters=[str, str]) def testConvertersNone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(converters=None) self.assertIsInstance(f[0], java.lang.String) cu.execute("select name from test") def testConvertersPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(converters=[str]) self.assertIsInstance(f[0], str) cu.execute("select name from test") def test_fetchoneTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(types=[dbapi2.STRING]) self.assertIsInstance(f[0], str) def test_fetchmanyTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchmany(types=[dbapi2.STRING]) self.assertIsInstance(f[0][0], str) def test_fetchallTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchall(types=[dbapi2.STRING]) self.assertIsInstance(f[0][0], str) def testTypesPositionalBAD(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): f = cu.fetchone(types=[dbapi2.STRING, dbapi2.STRING]) with self.assertRaises(dbapi2.ProgrammingError): f = cu.fetchone(types=[]) @common.unittest.skipUnless(zlib, "requires zlib") class GettersTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass @common.unittest.skip # type: ignore class TransactionsTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testTransactionRollbackCreate(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("begin") cu.execute("create table test(name VARCHAR(10))") cx.rollback() # with self.assertRaises(dbapi2.ProgrammingError): cu.execute("select * from test") def testTransactionRollbackInsert(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("create table test(name VARCHAR(10))") cx.commit() cu.execute("begin") cu.execute("insert into test(name) values('alice')") cx.rollback() # Alice should go away result = cu.execute("select * from test").fetchall() self.assertEqual(result, []) def testTransactionRollbackClose(self): with dbapi2.connect(db_name) as cx2: with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('alice')") cx.commit() cu.execute("begin") cu.execute("insert into test(name) values('bob')") result = cu.execute("select * from test").fetchall() self.assertEqual(result, [['alice'], ['bob']]) # Bob should go away with cx2.cursor() as cu: result = cu.execute("select * from test").fetchall() self.assertEqual(result, [['alice']]) class TypeTestCase(common.JPypeTestCase): """ This test is db dependent, but needed to check all code paths. """ def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testTime(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME TIME)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('12:05:10')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.time) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = jpype.java.sql.Time(1, 2, 3) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.time(1, 2, 3)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], jpype.java.sql.Time) cu.execute("delete from test") # Test with dbapi2.Time t1 = dbapi2.Time(6, 21, 32) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.time(6, 21, 32)) def testDate(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME DATE)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('2012-02-05')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.date) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = java.sql.Date(1972 - 1900, 4 - 1, 1) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.date(1972, 4, 1)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], java.sql.Date) cu.execute("delete from test") # Test with dbapi2.Date t1 = dbapi2.Date(2020, 5, 21) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.date(2020, 5, 21)) def testTimestamp(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME TIMESTAMP)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('2012-02-05 12:02:45.123')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.date) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = java.sql.Timestamp(1972 - 1900, 4 - 1, 1, 12, 2, 45, 123456000) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.datetime(1972, 4, 1, 12, 2, 45, 123456)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], java.sql.Timestamp) cu.execute("delete from test") # Test with dbapi2.Date t1 = dbapi2.Timestamp(2020, 5, 21, 3, 4, 5, 123122000) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.datetime(2020, 5, 21, 3, 4, 5, 123122)) def _testInt(self, tp, desc, jtype, null=True): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [123]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 123) self.assertIsInstance(f[0], int) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], 123) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") if null: cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testTinyInt(self): self._testInt('TINYINT', ('NAME', 'TINYINT'), jpype.JShort) def testBigInt(self): self._testInt('BIGINT', ('NAME', 'BIGINT'), jpype.JLong) def testIdentity(self): self._testInt('IDENTITY', ('NAME', 'INTEGER'), jpype.JInt, False) def testInteger(self): self._testInt('INTEGER', ('NAME', 'INTEGER'), jpype.JInt) def testSmallInt(self): self._testInt('SMALLINT', ('NAME', 'SMALLINT'), jpype.JShort) def _testFloat(self, tp, desc, jtype): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [1.25]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], float) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testFloat(self): self._testFloat('FLOAT', ('NAME', 'DOUBLE'), jpype.JDouble) def testDouble(self): self._testFloat('DOUBLE', ('NAME', 'DOUBLE'), jpype.JDouble) def testReal(self): self._testFloat('REAL', ('NAME', 'DOUBLE'), jpype.JDouble) def _testNumeric(self, tp, desc, jtype): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s(5,2))" % tp) cu.execute("insert into test(NAME) values(?)", [1.25]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], decimal.Decimal) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], decimal.Decimal(1.25)) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testNumeric(self): self._testNumeric('NUMERIC', ('NAME', 'NUMERIC'), java.math.BigDecimal) def testDecimal(self): self._testNumeric('DECIMAL', ('NAME', 'DECIMAL'), java.math.BigDecimal) def _testBinary(self, tp, desc, jtype): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) v = bytes([1, 2, 3]) cu.execute("insert into test(NAME) values(?)", [v]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], v) self.assertIsInstance(f[0], bytes) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(bytes(f[0]), v) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testBinary(self): self._testBinary('BINARY(3)', ('NAME', 'BINARY'), JArray(JByte)) def testBlob(self): self._testBinary('BLOB', ('NAME', 'BLOB'), JArray(JByte)) def testLongVarBinary(self): self._testBinary('LONGVARBINARY', ('NAME', 'VARBINARY'), JArray(JByte)) def testVarBinary(self): self._testBinary('VARBINARY(10)', ('NAME', 'VARBINARY'), JArray(JByte)) def _testChars(self, tp, desc, jtype, v="hello"): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [v]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], v) self.assertIsInstance(f[0], str) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], v) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testChar(self): self._testChars('CHAR', ('NAME', 'CHARACTER'), java.lang.String, 'a') def testVarChar(self): self._testChars('VARCHAR(10)', ('NAME', 'VARCHAR'), java.lang.String) def testLongVarChar(self): self._testChars('LONGVARCHAR', ('NAME', 'VARCHAR'), java.lang.String) def testClob(self): self._testChars('CLOB', ('NAME', 'CLOB'), java.lang.String) def testBoolean(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(NAME BOOLEAN)") cu.execute("insert into test(NAME) values(?)", [True]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], True) self.assertIsInstance(f[0], int) self.assertEqual(cu.description[0][0:2], ('NAME', 'BOOLEAN')) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], True) self.assertIsInstance(f[0], type(True)) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) class ThreadingTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def _testThread(self, cu, cmd, args, exc): error = [] def run(): try: cmd(*args) except Exception as ex: error.append(ex) t = threading.Thread(target=run) t.start() t.join() self.assertEqual(len(error), 1) with self.assertRaises(exc): raise error[0] def test_execute(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") self._testThread(cu, cu.execute, ("insert into test(name) values ('a')",), dbapi2.ProgrammingError) def test_executemany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") self._testThread(cu, cu.executemany, ("insert into test(name) values ('?')", [[1]]), dbapi2.ProgrammingError) def test_fetchone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchone, (), dbapi2.ProgrammingError) def test_fetchmany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchmany, (), dbapi2.ProgrammingError) def test_fetchall(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchall, (), dbapi2.ProgrammingError) def test_close(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.close, (), dbapi2.ProgrammingError) def test_callproc(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.callproc, ("lower", ["FOO"]), dbapi2.ProgrammingError) jpype-1.6.0/test/jpypetest/test_sql_sqlite.py000066400000000000000000001543401501674766700214760ustar00rootroot00000000000000# This file is Public Domain and may be used without restrictions, # because nobody should have to waste their lives typing this again. import datetime import decimal import threading import pytest import common import jpype import jpype.dbapi2 as dbapi2 from jpype import java from jpype.types import JArray, JByte try: import zlib except ImportError: zlib = None # type: ignore[assignment] db_name = "jdbc:sqlite::memory:" def setUpModule(module): from common import java_version version = java_version() if version[0] == 1 and version[1] == 8: pytest.skip("jdk8 unsupported", allow_module_level=True) class ConnectTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def testConnect(self): cx = dbapi2.connect(db_name) self.assertIsInstance(cx, dbapi2.Connection) def testClose(self): cx = dbapi2.connect(db_name) cx.close() # Closing twice is an error with self.assertRaises(dbapi2.ProgrammingError): cx.close() def testScope(self): with dbapi2.connect(db_name) as cx: pass with self.assertRaises(dbapi2.ProgrammingError): cx.close() def testAdapters(self): cx = dbapi2.connect(db_name) self.assertEqual(cx.adapters, dbapi2._default_adapters) cx = dbapi2.connect(db_name, adapters=None) self.assertEqual(cx.adapters, {}) m = {} cx = dbapi2.connect(db_name, adapters=m) self.assertEqual(cx.adapters, m) def testConverters(self): cx = dbapi2.connect(db_name) self.assertEqual(cx.converters, dbapi2._default_converters) cx = dbapi2.connect(db_name, converters=None) self.assertEqual(cx.converters, {}) m = {} cx = dbapi2.connect(db_name, converters=m) self.assertEqual(cx.converters, m) class ConnectionTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def test_commit(self): with dbapi2.connect(db_name) as cx: # Commit must work without a command cx.commit() def test_rollback(self): with dbapi2.connect(db_name) as cx: # Commit must work without a command cx.rollback() def test_cursor(self): with dbapi2.connect(db_name) as cx: self.assertIsInstance(cx.cursor(), dbapi2.Cursor) def testAdapters(self): with dbapi2.connect(db_name) as cx: m = {'foo': 1} cx.adapters = m self.assertEqual(cx._adapters, m) with self.assertRaises(TypeError): cx.adapters = object() def testConverters(self): with dbapi2.connect(db_name) as cx: m = {'foo': 1} cx.converters = m self.assertEqual(cx._converters, m) with self.assertRaises(TypeError): cx.converters = object() def testClosedClass(self): with dbapi2.connect(db_name) as cx: with self.assertRaises(AttributeError): cx.seters = object() def testAutocommitFail(self): with dbapi2.connect(db_name) as cx: cx.autocommit = True with self.assertRaises(dbapi2.NotSupportedError): cx.commit() with self.assertRaises(dbapi2.NotSupportedError): cx.rollback() def test_typeinfo(self): with dbapi2.connect(db_name) as cx: ti = cx.typeinfo self.assertIsInstance(ti, dict) for p, v in ti.items(): self.assertIsInstance(p, str) self.assertIsInstance(v, dbapi2.JDBCType) with self.assertRaises(dbapi2.ProgrammingError): ti = cx.typeinfo def test_connection(self): with dbapi2.connect(db_name) as cx: c = cx.connection self.assertIsInstance(c, java.sql.Connection) def test_getters(self): with dbapi2.connect(db_name) as cx: cx.getters = dbapi2.GETTERS_BY_NAME self.assertEqual(cx._getters, dbapi2.GETTERS_BY_NAME) self.assertEqual(cx.getters, dbapi2.GETTERS_BY_NAME) cx.getters = dbapi2.GETTERS_BY_TYPE self.assertEqual(cx._getters, dbapi2.GETTERS_BY_TYPE) self.assertEqual(cx.getters, dbapi2.GETTERS_BY_TYPE) def test_setters(self): with dbapi2.connect(db_name) as cx: cx.setters = dbapi2.SETTERS_BY_META self.assertEqual(cx._setters, dbapi2.SETTERS_BY_META) self.assertEqual(cx.setters, dbapi2.SETTERS_BY_META) cx.setters = dbapi2.SETTERS_BY_TYPE self.assertEqual(cx._setters, dbapi2.SETTERS_BY_TYPE) self.assertEqual(cx.setters, dbapi2.SETTERS_BY_TYPE) def test_adapters(self): with dbapi2.connect(db_name) as cx: self.assertEqual(cx.adapters, dbapi2._default_adapters) cx.adapters = None self.assertEqual(cx.adapters, {}) m = {} cx.adapters = m self.assertEqual(cx.adapters, m) with self.assertRaises(dbapi2.InterfaceError): cx.adapters = object() def test_converters(self): with dbapi2.connect(db_name) as cx: self.assertEqual(cx.converters, dbapi2._default_converters) cx.converters = None self.assertEqual(cx.converters, {}) m = {} cx.converters = m self.assertEqual(cx.converters, m) with self.assertRaises(dbapi2.InterfaceError): cx.converters = object() class CursorTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table booze") except dbapi2.Error: pass try: cur.execute("drop table barflys") except dbapi2.Error: pass def testCursorIsolation(self): with dbapi2.connect(db_name) as cx: # Make sure cursors created from the same connection have # the documented transaction isolation level cur1 = cx.cursor() cur2 = cx.cursor() cur1.execute("create table booze (name varchar(20))") cur1.execute("insert into booze values ('Victoria Bitter')") cur2.execute("select name from booze") booze = cur2.fetchall() self.assertEqual(len(booze), 1) self.assertEqual(len(booze[0]), 1) self.assertEqual(booze[0][0], "Victoria Bitter") def testDescription(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") self.assertEqual( cur.description, None, "cursor.description should be none after executing a " "statement that can return no rows (such as create table)", ) cur.execute("select name from booze") self.assertEqual( len(cur.description), 1, "cursor.description describes too many columns" ) self.assertEqual( len(cur.description[0]), 7, "cursor.description[x] tuples must have 7 elements", ) self.assertEqual( cur.description[0][0].lower(), "name", "cursor.description[x][0] must return column name", ) self.assertEqual( cur.description[0][1], dbapi2.STRING, "cursor.description[x][1] must return column type. Got %r" % cur.description[0][1], ) # Make sure self.description gets reset cur.execute("create table barflys (name varchar(20))") self.assertEqual( cur.description, None, "cursor.description not being set to None when executing " "no-result statements (eg. create table)", ) def testRowcount(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") # This was dropped from the specification # self.assertEqual( # cur.rowcount, # -1, # "cursor.rowcount should be -1 after executing no-result " "statements", # ) cur.execute("insert into booze values ('Victoria Bitter')") self.assertEqual( cur.rowcount, 1, "cursor.rowcount should == number or rows inserted, or " "set to -1 after executing an insert statement", ) cur.execute("select name from booze") self.assertEqual( cur.rowcount, -1, "cursor.rowcount should == number of rows returned, or " "set to -1 after executing a select statement", ) cur.execute("create table barflys (name varchar(20))") # THis test does not match jdbc parameters # self.assertEqual( # cur.rowcount, # -1, # "cursor.rowcount not being reset to -1 after executing " # "no-result statements", # ) def testClose(self): cx = dbapi2.connect(db_name) try: cur = cx.cursor() finally: cx.close() # cursor.execute should raise an Error if called after connection # closed with self.assertRaises(dbapi2.Error): cur.execute("create table booze (name varchar(20))") # connection.commit should raise an Error if called after connection' # closed.' self.assertRaises(dbapi2.Error, cx.commit) # connection.close should raise an Error if called more than once self.assertRaises(dbapi2.Error, cx.close) def test_None(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") cur.execute("insert into booze values (NULL)") cur.execute("select name from booze") r = cur.fetchall() self.assertEqual(len(r), 1) self.assertEqual(len(r[0]), 1) self.assertEqual(r[0][0], None, "NULL value not returned as None") def testArraysize(self): # Not much here - rest of the tests for this are in test_fetchmany with dbapi2.connect(db_name) as cx, cx.cursor() as cur: self.assertTrue( hasattr(cur, "arraysize"), "cursor.arraysize must be defined" ) def _paraminsert(self, cur): cur.execute("create table booze (name varchar(20))") cur.execute("insert into booze values ('Victoria Bitter')") self.assertTrue(cur.rowcount in (-1, 1)) cur.execute("insert into booze values (?)", ("Cooper's",)) self.assertTrue(cur.rowcount in (-1, 1)) cur.execute("select name from booze") res = cur.fetchall() self.assertEqual(len(res), 2, "cursor.fetchall returned too few rows") beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual( beers[0], "Cooper's", "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", ) self.assertEqual( beers[1], "Victoria Bitter", "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", ) def test_execute(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: self._paraminsert(cur) def test_executeBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: with self.assertRaises(dbapi2.InterfaceError): cur.execute("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cur.execute(object()) with self.assertRaises(dbapi2.ProgrammingError): cur.execute("inert into booze values (?)", [["?"]]) def test_setinputsizes(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.setinputsizes((25,)) self._paraminsert(cur) # Make sure cursor still works def test_setoutputsize_basic(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.setoutputsize(1000) cur.setoutputsize(2000, 0) self._paraminsert(cur) # Make sure the cursor still works def test_executemany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") largs = [("Cooper's",), ("Boag's",)] margs = [{"beer": "Cooper's"}, {"beer": "Boag's"}] cur.executemany("insert into booze values (?)", largs) self.assertEqual( cur.rowcount, 2, "insert using cursor.executemany set cursor.rowcount to " "incorrect value %r" % cur.rowcount, ) cur.execute("select name from booze") res = cur.fetchall() self.assertEqual( len(res), 2, "cursor.fetchall retrieved incorrect number of rows" ) beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual(beers[0], "Boag's", "incorrect data retrieved") self.assertEqual(beers[1], "Cooper's", "incorrect data retrieved") def test_executemanyGenerator(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: def mygen(ls): for i in ls: yield [i] cu.execute("create table booze (name varchar(20))") cu.executemany("insert into booze values (?)", mygen(self.samples)) cu.execute("select * from booze") f = cu.fetchall() for i, v in enumerate(self.samples): self.assertEqual(f[i][0], v) def test_executemanyIterator(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: class myiter: def __init__(self, ls): self.ls = ls self.i = 0 def __iter__(self): return self def __next__(self): if self.i == len(self.ls): raise StopIteration rc = [self.ls[self.i]] self.i += 1 return rc cu.execute("create table booze (name varchar(20))") cu.executemany("insert into booze values (?)", myiter(self.samples)) cu.execute("select * from booze") f = cu.fetchall() for i, v in enumerate(self.samples): self.assertEqual(f[i][0], v) def test_executemanyBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze (name varchar(20))") with self.assertRaises(TypeError): cu.executemany("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cu.executemany("insert into booze values (?)", object()) with self.assertRaises(dbapi2.InterfaceError): cu.executemany(object(), []) cu.executemany("insert into booze values (?)", None) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("inert into booze values (?)", [['?']]) def test_fetchone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchone should raise an Error if called before # executing a select-type query self.assertRaises(dbapi2.ProgrammingError, cur.fetchone) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("create table booze (name varchar(20))") self.assertRaises(dbapi2.Error, cur.fetchone) cur.execute("select name from booze") self.assertEqual( cur.fetchone(), None, "cursor.fetchone should return None if a query retrieves " "no rows", ) self.assertEqual(cur.rowcount, 0) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("insert into booze values ('Victoria Bitter')") self.assertRaises(dbapi2.Error, cur.fetchone) cur.execute("select name from booze") r = cur.fetchone() self.assertEqual( len(r), 1, "cursor.fetchone should have retrieved a single row" ) self.assertEqual( r[0], "Victoria Bitter", "cursor.fetchone retrieved incorrect data" ) self.assertEqual( cur.fetchone(), None, "cursor.fetchone should return None if no more rows available", ) self.assertEqual(cur.rowcount, -1) samples = [ "Carlton Cold", "Carlton Draft", "Mountain Goat", "Redback", "Victoria Bitter", "XXXX", ] def _populate(self): """ Return a list of sql commands to setup the DB for the fetch tests. """ populate = [ "insert into booze values ('{}')".format(s) for s in self.samples ] return populate def test_fetchmany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchmany should raise an Error if called without # issuing a query self.assertRaises(dbapi2.ProgrammingError, cur.fetchmany, 4) cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) cur.execute("select name from booze") r = cur.fetchmany() self.assertEqual( len(r), 1, "cursor.fetchmany retrieved incorrect number of rows, " "default of arraysize is one.", ) cur.arraysize = 10 r = cur.fetchmany(3) # Should get 3 rows self.assertEqual( len(r), 3, "cursor.fetchmany retrieved incorrect number of rows" ) r = cur.fetchmany(4) # Should get 2 more self.assertEqual( len(r), 2, "cursor.fetchmany retrieved incorrect number of rows" ) r = cur.fetchmany(4) # Should be an empty sequence self.assertEqual( len(r), 0, "cursor.fetchmany should return an empty sequence after " "results are exhausted", ) self.assertEqual(cur.rowcount, -1) # Same as above, using cursor.arraysize cur.arraysize = 4 cur.execute("select name from booze") r = cur.fetchmany() # Should get 4 rows self.assertEqual( len(r), 4, "cursor.arraysize not being honoured by fetchmany" ) r = cur.fetchmany() # Should get 2 more self.assertEqual(len(r), 2) r = cur.fetchmany() # Should be an empty sequence self.assertEqual(len(r), 0) self.assertEqual(cur.rowcount, -1) cur.arraysize = 6 cur.execute("select name from booze") rows = cur.fetchmany() # Should get all rows self.assertTrue(cur.rowcount in (-1, 6)) self.assertEqual(len(rows), 6) self.assertEqual(len(rows), 6) rows = [r[0] for r in rows] rows.sort() # Make sure we get the right data back out for i in range(0, 6): self.assertEqual( rows[i], self.samples[i], "incorrect data retrieved by cursor.fetchmany", ) rows = cur.fetchmany() # Should return an empty list self.assertEqual( len(rows), 0, "cursor.fetchmany should return an empty sequence if " "called after the whole result set has been fetched", ) self.assertEqual(cur.rowcount, -1) cur.execute("create table barflys (name varchar(20))") cur.execute("select name from barflys") r = cur.fetchmany() # Should get empty sequence self.assertEqual( len(r), 0, "cursor.fetchmany should return an empty sequence if " "query retrieved no rows", ) def test_fetchall(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: # cursor.fetchall should raise an Error if called # without executing a query that may return rows (such # as a select) self.assertRaises(dbapi2.Error, cur.fetchall) cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) # cursor.fetchall should raise an Error if called # after executing a statement that cannot return rows self.assertRaises(dbapi2.ProgrammingError, cur.fetchall) cur.execute("select name from booze") rows = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows), len(self.samples), "cursor.fetchall did not retrieve all rows", ) rows = [r[0] for r in rows] rows.sort() for i in range(0, len(self.samples)): self.assertEqual( rows[i], self.samples[i], "cursor.fetchall retrieved incorrect rows" ) rows = cur.fetchall() self.assertEqual( len(rows), 0, "cursor.fetchall should return an empty list if called " "after the whole result set has been fetched", ) self.assertEqual(cur.rowcount, -1) cur.execute("create table barflys (name varchar(20))") cur.execute("select name from barflys") rows = cur.fetchall() self.assertEqual(cur.rowcount, 1) self.assertEqual( len(rows), 0, "cursor.fetchall should return an empty list if " "a select query returns no rows", ) def test_mixedfetch(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: cur.execute("create table booze (name varchar(20))") for sql in self._populate(): cur.execute(sql) cur.execute("select name from booze") self.assertEqual(cur.rowcount, -1) rows1 = cur.fetchone() rows23 = cur.fetchmany(2) rows4 = cur.fetchone() rows56 = cur.fetchall() self.assertEqual(cur.rowcount, -1) self.assertEqual( len(rows23), 2, "fetchmany returned incorrect number of rows" ) self.assertEqual( len(rows56), 2, "fetchall returned incorrect number of rows" ) rows = [rows1[0]] rows.extend([rows23[0][0], rows23[1][0]]) rows.append(rows4[0]) rows.extend([rows56[0][0], rows56[1][0]]) rows.sort() for i in range(0, len(self.samples)): self.assertEqual( rows[i], self.samples[i], "incorrect data retrieved or inserted" ) def test_nextset(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze (name varchar(20))") booze = ["Wiskey", ] cu.execute("insert into booze(name) values (?)", booze) self.assertEqual(cu.rowcount, 1) cu.execute("select * from booze; select * from booze") self.assertEqual(cu.fetchone(), booze) nxt = cu.nextset() self.assertTrue(nxt in (True, None)) # H2 does not support multiple result sets if nxt: self.assertEqual(cu.fetchone(), booze) @common.unittest.skip # type: ignore def test_lastrowid(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(id INTEGER IDENTITY PRIMARY KEY, name varchar(255))") cu.execute("insert into booze(name) values('hello')", keys=True) id0 = cu.lastrowid self.assertIsInstance(id0, int) # Call more than once id1 = cu.lastrowid self.assertIsInstance(id1, int) cx.commit() cu.execute("insert into booze(name) values('there')", keys=True) id2 = cu.lastrowid self.assertIsInstance(id2, int) cu.execute("select * from booze") f = cu.fetchall() self.assertEqual(cu.lastrowid, None) # sqlite has weird behaviors self.assertEqual(f[0][0], id0) self.assertEqual(f[1][0], id2) @common.unittest.skip # type: ignore def test_lastrowidMany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(id INTEGER IDENTITY PRIMARY KEY, name varchar(255))") cu.executemany("insert into booze(name) values(?)", [['Redback'], ['Fat Yak']], keys=True) ids = cu.lastrowid self.assertIsInstance(ids, list) self.assertEqual(len(ids), 2) cu.execute("select * from booze") f = cu.fetchall() self.assertEqual(f[0][0], ids[0]) self.assertEqual(f[1][0], ids[1]) def test_iter(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: ls = [['Tooheys Old'], ['Nine Tales Original Amber Ale'], ['Dogbolter']] cu.execute("create table booze(name varchar(255))") cu.executemany("insert into booze(name) values(?)", ls, keys=True) # make sure we throw if there is no result set with self.assertRaises(dbapi2.ProgrammingError): for booze in cu: pass cu.execute("select * from booze") for i, booze in enumerate(cu): self.assertEqual(ls[i], booze) def testNoAdapter(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name) values(?)", [object()]) def testBadParameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name) values(?)", object()) def test_callproc(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): r = cu.callproc("lower", ("FOO",)) @common.unittest.skip # type: ignore def test_callprocBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): r = cu.callproc("not_there", ("FOO",)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc(object(), ("FOO",)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc("lower", (object(),)) with self.assertRaises(dbapi2.InterfaceError): r = cu.callproc("lower", object()) def test_close(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.close() with self.assertRaises(dbapi2.ProgrammingError): cu.close() def test_parameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: with self.assertRaises(dbapi2.ProgrammingError): p = cu.parameters cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) p = cu.parameters self.assertEqual(p[0][0:2], ('VARCHAR', dbapi2.VARCHAR)) def test_resultSet(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) cu.execute("select * from booze") rs = cu.resultSet self.assertIsInstance(rs, java.sql.ResultSet) def test_fetchClosed(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table booze(name varchar(50))") cu.execute("insert into booze(name) values(?)", ('Hahn Super Dry',)) cu.execute("select * from booze") cu.close() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchmany() with self.assertRaises(dbapi2.ProgrammingError): cu.fetchall() self.assertEqual(cu.resultSet, None) def test_executeBadParameters(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: def mygen(ls): for i in ls: yield i cu.execute("create table booze(name varchar(50), price integer)") with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze(name,price) values(?,?)", [('Hahn Super Dry', 2), ('Coors',)]) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", {'name': 'Budweiser', 'price': 10}) with self.assertRaises(dbapi2.ProgrammingError): cu.executemany("insert into booze(name,price) values(?,?)", mygen([['Budweiser']])) cu.execute("insert into booze(name,price) values(?,?)", mygen(['Pabst Blue Robot', 20000])) with self.assertRaises(dbapi2.ProgrammingError): cu.execute("insert into booze(name,price) values(?,?)", mygen(['Budweiser'])) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", 'Budweiser') with self.assertRaises(dbapi2.ProgrammingError): cu.execute("insert into booze(name,price) values(?,?)", mygen(['Budweiser', 2, 'no'])) with self.assertRaises(dbapi2.InterfaceError): cu.execute("insert into booze(name,price) values(?,?)", object()) @common.unittest.skipUnless(zlib, "requires zlib") class AdapterTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testOnConnector(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(255))") cx.adapters[object] = lambda x: "none" cu.execute("insert into test(name) values(?)", [object()]) cu.execute("select * from test") f = cu.fetchall() self.assertEqual(f[0][0], "none") @common.unittest.skipUnless(zlib, "requires zlib") class ConverterTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass @staticmethod def convert(s): return zlib.decompress(s) def testConvertersBad(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone(converters=[]) cu.execute("select name from test") with self.assertRaises(dbapi2.InterfaceError): cu.fetchone(converters=[int]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): cu.fetchone(converters=[str, str]) def testConvertersNone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(converters=None) self.assertIsInstance(f[0], java.lang.String) cu.execute("select name from test") def testConvertersPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(converters=[str]) self.assertIsInstance(f[0], str) cu.execute("select name from test") def test_fetchoneTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchone(types=[dbapi2.STRING]) self.assertIsInstance(f[0], str) def test_fetchmanyTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchmany(types=[dbapi2.STRING]) self.assertIsInstance(f[0][0], str) def test_fetchallTypesPositional(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") f = cu.fetchall(types=[dbapi2.STRING]) self.assertIsInstance(f[0][0], str) def testTypesPositionalBAD(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name varchar(20))") cu.executemany("insert into test(name) values(?)", [['alice'], ['bob'], ['charlie']]) cu.execute("select name from test") with self.assertRaises(dbapi2.ProgrammingError): f = cu.fetchone(types=[dbapi2.STRING, dbapi2.STRING]) with self.assertRaises(dbapi2.ProgrammingError): f = cu.fetchone(types=[]) @common.unittest.skipUnless(zlib, "requires zlib") class GettersTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") self.testdata = b"abcdefg" * 10 self.params = memoryview(zlib.compress(self.testdata)) def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass @common.unittest.skip # type: ignore class TransactionsTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testTransactionRollbackCreate(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("begin") cu.execute("create table test(name VARCHAR(10))") cx.rollback() # with self.assertRaises(dbapi2.ProgrammingError): cu.execute("select * from test") def testTransactionRollbackInsert(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("create table test(name VARCHAR(10))") cx.commit() cu.execute("begin") cu.execute("insert into test(name) values('alice')") cx.rollback() # Alice should go away result = cu.execute("select * from test").fetchall() self.assertEqual(result, []) def testTransactionRollbackClose(self): with dbapi2.connect(db_name) as cx2: with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cx.commit() # needed before starting transactions cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('alice')") cx.commit() cu.execute("begin") cu.execute("insert into test(name) values('bob')") result = cu.execute("select * from test").fetchall() self.assertEqual(result, [['alice'], ['bob']]) # Bob should go away with cx2.cursor() as cu: result = cu.execute("select * from test").fetchall() self.assertEqual(result, [['alice']]) class TypeTestCase(common.JPypeTestCase): """ This test is db dependent, but needed to check all code paths. """ def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testTime(self): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME TIME)") cu.execute("select * from test") t1 = jpype.java.sql.Time(1, 2, 3) cu.execute("insert into test(NAME) values(?)", [t1]) f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.time) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = jpype.java.sql.Time(1, 2, 3) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.time(1, 2, 3)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], jpype.java.sql.Time) cu.execute("delete from test") # Test with dbapi2.Time t1 = dbapi2.Time(6, 21, 32) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.time(6, 21, 32)) def testDate(self): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME DATE)") cu.execute("select * from test") t1 = java.sql.Date(2012 - 1900, 2 - 1, 5) cu.execute("insert into test(NAME) values(?)", [t1]) #cu.execute("insert into test(NAME) values('2012-02-05')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.date) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = java.sql.Date(1972 - 1900, 4 - 1, 1) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.date(1972, 4, 1)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], java.sql.Date) cu.execute("delete from test") # Test with dbapi2.Date t1 = dbapi2.Date(2020, 5, 21) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.date(2020, 5, 21)) def testTimestamp(self): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME TIMESTAMP)") cu.execute("select * from test") cu.execute("insert into test(NAME) values('2012-02-05 12:02:45.123')") f = cu.execute('select * from test').fetchone() self.assertIsInstance(f[0], datetime.date) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", f) f2 = cu.execute('select * from test').fetchone() self.assertEqual(f2, f) cu.execute("delete from test") # Test with Java t1 = java.sql.Timestamp(1972 - 1900, 4 - 1, 1, 12, 2, 45, 123000000) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.datetime(1972, 4, 1, 12, 2, 45, 123000)) f4 = cu.execute('select * from test').fetchone(converters=None) self.assertEqual(f4[0], t1) self.assertIsInstance(f4[0], java.sql.Timestamp) cu.execute("delete from test") # Test with dbapi2.Date t1 = dbapi2.Timestamp(2020, 5, 21, 3, 4, 5, 123000000) cu.execute("insert into test(NAME) values(?)", [t1]) f3 = cu.execute('select * from test').fetchone() self.assertEqual(f3[0], datetime.datetime(2020, 5, 21, 3, 4, 5, 123000)) def _testInt(self, tp, desc, jtype, null=True): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [123]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 123) self.assertIsInstance(f[0], int) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], 123) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") if null: cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testTinyInt(self): self._testInt('TINYiNT', ('NAME', 'TINYINT'), jpype.JShort) def testBigInt(self): self._testInt('BIGINT', ('NAME', 'BIGINT'), jpype.JLong) def testIdentity(self): self._testInt('IDENTITY', ('NAME', 'IDENTITY'), jpype.JInt, False) def testInteger(self): self._testInt('INTEGER', ('NAME', 'INTEGER'), jpype.JInt) def testSmallInt(self): self._testInt('SMALLINT', ('NAME', 'SMALLINT'), jpype.JShort) def _testFloat(self, tp, desc, jtype): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [1.25]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], float) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testFloat(self): self._testFloat('FLOAT', ('NAME', 'FLOAT'), jpype.JDouble) def testDouble(self): self._testFloat('DOUBLE', ('NAME', 'DOUBLE'), jpype.JDouble) def testReal(self): self._testFloat('REAL', ('NAME', 'REAL'), jpype.JFloat) def _testNumeric(self, tp, desc, jtype): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s(5,2))" % tp) cu.execute("insert into test(NAME) values(?)", [1.25]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], 1.25) self.assertIsInstance(f[0], decimal.Decimal) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], decimal.Decimal(1.25)) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testNumeric(self): self._testNumeric('NUMERIC', ('NAME', 'NUMERIC'), java.math.BigDecimal) def testDecimal(self): self._testNumeric('DECIMAL', ('NAME', 'DECIMAL'), java.math.BigDecimal) def _testBinary(self, tp, desc, jtype): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) v = bytes([1, 2, 3]) cu.execute("insert into test(NAME) values(?)", [v]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], v) self.assertIsInstance(f[0], bytes) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(bytes(f[0]), v) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testBinary(self): self._testBinary('BINARY(3)', ('NAME', 'BINARY'), JArray(JByte)) def testBlob(self): self._testBinary('BLOB', ('NAME', 'BLOB'), JArray(JByte)) def testLongVarBinary(self): self._testBinary('LONGVARBINARY', ('NAME', 'LONGVARBINARY'), JArray(JByte)) def testVarBinary(self): self._testBinary('VARBINARY(10)', ('NAME', 'VARBINARY'), JArray(JByte)) def _testChars(self, tp, desc, jtype, v="hello"): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME %s)" % tp) cu.execute("insert into test(NAME) values(?)", [v]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], v) self.assertIsInstance(f[0], str) self.assertEqual(cu.description[0][0:2], desc) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], v) self.assertIsInstance(f[0], jtype) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) def testChar(self): self._testChars('CHAR', ('NAME', 'CHAR'), java.lang.String, 'a') def testVarChar(self): self._testChars('VARCHAR', ('NAME', 'VARCHAR'), java.lang.String) def testLongVarChar(self): self._testChars('LONGVARCHAR', ('NAME', 'LONGVARCHAR'), java.lang.String) def testClob(self): self._testChars('CLOB', ('NAME', 'CLOB'), java.lang.String) def testBoolean(self): with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(NAME BOOLEAN)") cu.execute("insert into test(NAME) values(?)", [True]) f = cu.execute("select * from test").fetchone() self.assertEqual(f[0], True) self.assertIsInstance(f[0], int) self.assertEqual(cu.description[0][0:2], ('NAME', 'BOOLEAN')) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], True) self.assertIsInstance(f[0], type(True)) cu.execute("delete from test") cu.execute("insert into test(NAME) values(?)", [None]) f = cu.execute("select * from test").fetchone(converters=None) self.assertEqual(f[0], None) class OtherTestCase(common.JPypeTestCase): """ This test is db dependent, but needed to check all code paths. """ def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def testJSON(self): got = [] class MyType(dbapi2.JDBCType): def get(self, rs, column, st): got.append(column) return dbapi2.JDBCType.get(self, rs, column, st) # Register a new type JSON = MyType("JSON", None) with dbapi2.connect(db_name, getters=dbapi2.GETTERS_BY_NAME) as cx, cx.cursor() as cu: cu.execute("create table test(name JSON)") cu.execute("insert into test(name) values('{age: 31}')") f = cu.execute("select * from test").fetchone() self.assertEqual(got, [1]) del dbapi2._registry[JSON] class ThreadingTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) if common.fast: raise common.unittest.SkipTest("fast") def tearDown(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cur: try: cur.execute("drop table test") except dbapi2.Error: pass def _testThread(self, cu, cmd, args, exc): error = [] def run(): try: cmd(*args) except Exception as ex: error.append(ex) t = threading.Thread(target=run) t.start() t.join() self.assertEqual(len(error), 1) with self.assertRaises(exc): raise error[0] def test_execute(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") self._testThread(cu, cu.execute, ("insert into test(name) values ('a')",), dbapi2.ProgrammingError) def test_executemany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") self._testThread(cu, cu.executemany, ("insert into test(name) values ('?')", [[1]]), dbapi2.ProgrammingError) def test_fetchone(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchone, (), dbapi2.ProgrammingError) def test_fetchmany(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchmany, (), dbapi2.ProgrammingError) def test_fetchall(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.fetchall, (), dbapi2.ProgrammingError) def test_close(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.close, (), dbapi2.ProgrammingError) @common.unittest.skip # type: ignore def test_callproc(self): with dbapi2.connect(db_name) as cx, cx.cursor() as cu: cu.execute("create table test(name VARCHAR(10))") cu.execute("insert into test(name) values('a')") self._testThread(cu, cu.callproc, ("lower", ["FOO"]), dbapi2.ProgrammingError) jpype-1.6.0/test/jpypetest/test_startup.py000066400000000000000000000213341501674766700210140ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import subrun import os from pathlib import Path import unittest import common root = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) cp = os.path.join(root, 'classes').replace('\\', '/') @subrun.TestCase(individual=True) class StartJVMCase(unittest.TestCase): def setUp(self): self.jvmpath = jpype.getDefaultJVMPath() def testStartup(self): with self.assertRaises(OSError): jpype.startJVM(convertStrings=False) jpype.startJVM(convertStrings=False) def testRestart(self): with self.assertRaises(OSError): jpype.startJVM(convertStrings=False) jpype.shutdownJVM() jpype.startJVM(convertStrings=False) def testInvalidArgsFalse(self): with self.assertRaises(RuntimeError): jpype.startJVM( "-for_sure_InVaLiD", ignoreUnrecognized=False, convertStrings=False, ) def testInvalidArgsTrue(self): jpype.startJVM( "-for_sure_InVaLiD", ignoreUnrecognized=True, convertStrings=False, ) def testClasspathArgKeyword(self): jpype.startJVM(classpath=cp, convertStrings=False) assert jpype.JClass('jpype.array.TestArray') is not None def testClasspathArgList(self): jpype.startJVM( classpath=[cp], convertStrings=False, ) assert jpype.JClass('jpype.array.TestArray') is not None def testClasspathArgListEmpty(self): jpype.startJVM( classpath=[cp, ''], convertStrings=False, ) assert jpype.JClass('jpype.array.TestArray') is not None def testClasspathArgDef(self): jpype.startJVM('-Djava.class.path=%s' % cp, convertStrings=False) assert jpype.JClass('jpype.array.TestArray') is not None def testClasspathArgPath(self): jpype.startJVM(classpath=Path(cp), convertStrings=False) assert jpype.JClass('jpype.array.TestArray') is not None def testClasspathArgPathList(self): jpype.startJVM(classpath=[Path(cp)], convertStrings=False) assert jpype.JClass('jpype.array.TestArray') is not None def testClasspathArgGlob(self): jpype.startJVM(classpath=os.path.join(cp, '..', 'jar', 'mrjar*')) assert jpype.JClass('org.jpype.mrjar.A') is not None def testClasspathTwice(self): with self.assertRaises(TypeError): jpype.startJVM('-Djava.class.path=%s' % cp, classpath=cp, convertStrings=False) def testClasspathBadType(self): with self.assertRaises(TypeError): jpype.startJVM(classpath=1, convertStrings=False) def testJVMPathArg_Str(self): jpype.startJVM(self.jvmpath, classpath=cp, convertStrings=False) assert jpype.JClass('jpype.array.TestArray') is not None def testJVMPathArg_None(self): # It is allowed to pass None as a JVM path jpype.startJVM( None, # type: ignore classpath=cp, ) assert jpype.JClass('jpype.array.TestArray') is not None def testJVMPathArg_NoArgs(self): jpype.startJVM( classpath=cp, ) assert jpype.JClass('jpype.array.TestArray') is not None def testJVMPathArg_Path(self): with self.assertRaises(TypeError): jpype.startJVM( # Pass a path as the first argument. This isn't supported (this is # reflected in the type definition), but the fact that it "works" # gives rise to this test. Path(self.jvmpath), # type: ignore convertStrings=False, ) def testJVMPathKeyword_str(self): jpype.startJVM( classpath=cp, jvmpath=self.jvmpath, convertStrings=False, ) assert jpype.JClass('jpype.array.TestArray') is not None def testJVMPathKeyword_Path(self): jpype.startJVM(jvmpath=Path(self.jvmpath), classpath=cp, convertStrings=False) assert jpype.JClass('jpype.array.TestArray') is not None def testPathTwice(self): with self.assertRaises(TypeError): jpype.startJVM(self.jvmpath, jvmpath=self.jvmpath) def testBadKeyword(self): with self.assertRaises(TypeError): jpype.startJVM(invalid=True) # type: ignore def testNonASCIIPath(self): """Test that paths with non-ASCII characters are handled correctly. Regression test for https://github.com/jpype-project/jpype/issues/1194 """ jpype.startJVM(jvmpath=Path(self.jvmpath), classpath="test/jar/unicode_à😎/sample_package.jar") cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader() self.assertEqual(type(cl), jpype.JClass("org.jpype.JPypeClassLoader")) assert dir(jpype.JPackage('org.jpype.sample_package')) == ['A', 'B'] def testOldStyleNonASCIIPath(self): """Test that paths with non-ASCII characters are handled correctly. Regression test for https://github.com/jpype-project/jpype/issues/1194 """ jpype.startJVM("-Djava.class.path=test/jar/unicode_à😎/sample_package.jar", jvmpath=Path(self.jvmpath)) cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader() self.assertEqual(type(cl), jpype.JClass("org.jpype.JPypeClassLoader")) assert dir(jpype.JPackage('org.jpype.sample_package')) == ['A', 'B'] def testNonASCIIPathWithSystemClassLoader(self): with self.assertRaises(ValueError): jpype.startJVM( "-Djava.system.class.loader=jpype.startup.TestSystemClassLoader", jvmpath=Path(self.jvmpath), classpath="test/jar/unicode_à😎/sample_package.jar" ) def testOldStyleNonASCIIPathWithSystemClassLoader(self): with self.assertRaises(ValueError): jpype.startJVM( self.jvmpath, "-Djava.system.class.loader=jpype.startup.TestSystemClassLoader", "-Djava.class.path=test/jar/unicode_à😎/sample_package.jar" ) @common.requireAscii def testASCIIPathWithSystemClassLoader(self): jpype.startJVM( "-Djava.system.class.loader=jpype.startup.TestSystemClassLoader", jvmpath=Path(self.jvmpath), classpath=f"test/classes" ) classloader = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader() test_classLoader = jpype.JClass("jpype.startup.TestSystemClassLoader") self.assertEqual(type(classloader), test_classLoader) assert dir(jpype.JPackage('jpype.startup')) == ['TestSystemClassLoader'] @common.requireAscii def testOldStyleASCIIPathWithSystemClassLoader(self): jpype.startJVM( self.jvmpath, "-Djava.system.class.loader=jpype.startup.TestSystemClassLoader", "-Djava.class.path=test/classes" ) classloader = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader() test_classLoader = jpype.JClass("jpype.startup.TestSystemClassLoader") self.assertEqual(type(classloader), test_classLoader) assert dir(jpype.JPackage('jpype.startup')) == ['TestSystemClassLoader'] @common.requireAscii def testDefaultSystemClassLoader(self): # we introduce no behavior change unless absolutely necessary jpype.startJVM(jvmpath=Path(self.jvmpath)) cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader() self.assertNotEqual(type(cl), jpype.JClass("org.jpype.JPypeClassLoader")) def testServiceWithNonASCIIPath(self): jpype.startJVM( self.jvmpath, "-Djava.locale.providers=SPI,CLDR", classpath="test/jar/unicode_à😎/service.jar", ) ZoneId = jpype.JClass("java.time.ZoneId") ZoneRulesException = jpype.JClass("java.time.zone.ZoneRulesException") try: ZoneId.of("JpypeTest/Timezone") except ZoneRulesException: self.fail("JpypeZoneRulesProvider not loaded") jpype-1.6.0/test/jpypetest/test_synchronized.py000066400000000000000000000051501501674766700220270ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import threading import time import jpype from jpype import synchronized import common fail = False if fail: # Cause a fail class _JMonitor(object): def __init__(self, obj): pass def __enter__(self): pass def __exit__(self, exception_type, exception_value, traceback): pass def synchronized(obj): return _JMonitor(obj) success = True glob = 0 obj = None class MyThread(threading.Thread): def run(self): global glob, success, obj with synchronized(obj): # Increment the global resource glob += 1 # Wait for a while time.sleep(0.1) # If anything else accessed then it was a fail if glob != 1: success = False # Decrement the global resoure glob -= 1 class SynchronizedTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) global glob, success, obj obj = jpype.JClass("java.lang.Object")() def testSynchronized(self): global glob, success glob = 0 success = True tl = [] # Create 10 threads trying to access the same resource for i in range(0, 10): tl.append(MyThread()) # Release them at the same time for i in tl: i.start() # Wait for all to finish for i in tl: i.join() # Verify they did not trample each other self.assertTrue(success) def testSyncronizedFail(self): with self.assertRaisesRegex(TypeError, "Java object is required"): with jpype.synchronized(object()): pass with self.assertRaisesRegex(TypeError, "Java primitives cannot be used"): with jpype.synchronized(jpype.JInt(1)): pass jpype-1.6.0/test/jpypetest/test_thread.py000066400000000000000000000045161501674766700205640ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common import pytest from jpype.imports import * class ThreadTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) @pytest.mark.filterwarnings("ignore::DeprecationWarning") def testAttach(self): # Make sure we are attached to start the test jpype.attachThreadToJVM() self.assertTrue(jpype.isThreadAttachedToJVM()) # Detach from the thread jpype.detachThreadFromJVM() self.assertFalse(jpype.isThreadAttachedToJVM()) # Reattach to the thread jpype.attachThreadToJVM() self.assertTrue(jpype.isThreadAttachedToJVM()) # Detach again jpype.detachThreadFromJVM() self.assertFalse(jpype.isThreadAttachedToJVM()) # Call a Java method which will cause it to attach automatically s = jpype.JString("foo") self.assertTrue(jpype.isThreadAttachedToJVM()) def testAttachNew(self): import java # Detach the thread java.lang.Thread.detach() self.assertFalse(java.lang.Thread.isAttached()) # Attach as a main thread java.lang.Thread.attach() self.assertTrue(java.lang.Thread.isAttached()) self.assertFalse(java.lang.Thread.currentThread().isDaemon()) # Detach the thread java.lang.Thread.detach() self.assertFalse(java.lang.Thread.isAttached()) # Attach as a daemon thread java.lang.Thread.attachAsDaemon() self.assertTrue(java.lang.Thread.isAttached()) self.assertTrue(java.lang.Thread.currentThread().isDaemon()) jpype-1.6.0/test/jpypetest/test_utf8.py000066400000000000000000000207141501674766700202010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ***************************************************************************** # Copyright 2018 Rene Bakker # ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** ''' Test communication with jpype using 4-byte utf-8 characters (emoji) IMPORTANT: The routines have only be tested in Python3. Given the difference in string handling between Python2 and Python3, it is not likely this routines have any significance for Python2. The following tests are defined: General: 1. Test if the java class (jpype.utf8.Utf8Test) can return the default ASCII string. 2. Pass an ASCII string to the java test class and check if it remained unchanged when returned from java. Binary: python strings are injected into java.lang.String in binary format with str.encode() 3. Pass a series of reference UTF-8 strings and compare them with the reference strings in the java class. 4. Pass a series of reference UTF-8 strings and check if they remained unchanged when returned from Java. Allow for surrogate substitution in the utf-16 strings returned from Java. 5. Pass a series of reference UTF-8 strings and check if they remained unchanged when returned from Java. Use the python default strict encoding rules for the returned string. Navive strings: python strings are passed as-is into a java method, wich accepts String as argument 6. Pass a series of reference UTF-8 strings and compare them with the reference strings in the java class. 7. Pass a series of reference UTF-8 strings and check if they remained unchanged when returned from Java. Allow for surrogate substitution in the utf-16 strings returned from Java. 8. Pass a series of reference UTF-8 strings and check if they remained unchanged when returned from Java. Use the python default strict encoding rules for the returned string. At the time if writing: Passed tests: 1, 2, 3, and 4 Failed tests: 5. encoding error returned string 6. uploaded string mutilated for emoji 7. follow-up of 6: mutilated string returned to python (emoji only= 8. idem 7. Note on encoding errors: UnicodeEncodeError: 'utf-8' codec can't encode characters in position xxx-xxx: surrogates not allowed ''' import sys from jpype.types import * import common class Utf8TestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) # Java IO test class self.Utf8Test = JClass('jpype.utf8.Utf8Test') # Test strings # IMPORTANT: they should be identical, and in the same order, as the test strings difned in the # java class Utf8Test self.TDICT = [] self.TDICT.append(['english', ("I can eat glass and it doesn't hurt me.")]) self.TDICT.append(['french', ("Je peux manger du verre, Γ§a ne me fait pas mal.")]) self.TDICT.append(['rune', ("α›–αš΄ αš·α›–α› ᛖᛏᛁ ᚧ αš·α›šα›–αš± α›˜αšΎ αš¦α›–α›‹α›‹ ᚨᚧ αš‘α›– ᚱᚧᚨ α›‹αš¨αš±")]) self.TDICT.append(['cn_simp', ("δΊΊδΊΊη”Ÿθ€Œθ‡ͺη”±,在尊δΈ₯ε’Œζƒεˆ©δΈŠδΈ€εΎ‹εΉ³η­‰γ€‚δ»–δ»¬θ΅‹ζœ‰η†ζ€§ε’Œθ‰―εΏƒ,εΉΆεΊ”δ»₯ε…„εΌŸε…³η³»ηš„η²Ύη₯žδΊ’相对待。")]) self.TDICT.append(['cn_trad', ("δΊΊδΊΊη”Ÿθ€Œθ‡ͺη”±οΉεœ¨ε°Šεš΄ε’Œζ¬Šεˆ©δΈŠδΈ€εΎ‹εΉ³η­‰γ€‚δ»–ε€‘θ³¦ζœ‰η†ζ€§ε’Œθ‰―εΏƒοΉδΈ¦ζ‡‰δ»₯ε…„εΌŸι—œδΏ‚ηš„η²Ύη₯žδΊ’相對待。")]) self.TDICT.append(['arab', ("Ψ£Ω†Ψ§ Ω‚Ψ§Ψ―Ψ± ΨΉΩ„Ω‰ Ψ£ΩƒΩ„ Ψ§Ω„Ψ²Ψ¬Ψ§Ψ¬ و Ω‡Ψ°Ψ§ Ω„Ψ§ ΩŠΨ€Ω„Ω…Ω†ΩŠ.")]) self.TDICT.append(['emoji', ("πŸ˜πŸ˜‚πŸ˜ƒπŸ˜„πŸ˜…πŸ˜†πŸ˜ πŸ˜‘πŸ˜’πŸ˜£πŸ˜€πŸ˜₯😨😩πŸ˜ͺπŸš‰πŸšŒπŸšπŸš‘πŸš’πŸš“πŸš•πŸš—πŸš™πŸššπŸš’πŸš€πŸš₯πŸš§πŸš¨πŸš»πŸšΌπŸš½πŸšΎπŸ›€πŸ†•πŸ†–πŸ†—πŸ†˜πŸ†™πŸ†šπŸˆπŸˆ‚πŸˆšπŸˆ―πŸˆΉπŸˆΊπŸ‰πŸ‰‘8⃣9⃣7⃣6⃣1⃣0")]) def test_get_ascii(self): """ Test if the default string returns from the java test class. """ utf8_test = self.Utf8Test() self.assertEqual("Utf8Test pure ASCII", utf8_test.get(), "Utf8Test.java default string") def test_ascii_upload(self): """ Test uploading and downloading of a simple ASCII string. """ test_string = 'Python Utf8Test ascii test string' utf8_test = self.Utf8Test(test_string) self.assertEqual(test_string, utf8_test.get(), "Utf8Test.java uploaded ASCII string") def test_binary_upload(self): """ Test binary upload and check in Java if the strings are correct. Assumes synchronized test strings in the java class and in this test class. """ String = JClass('java.lang.String') indx = 0 for lbl, val in self.TDICT: utf8_test = self.Utf8Test(String(val.encode('utf-8'), 'UTF8')) self.assertTrue(utf8_test.equalsTo(indx), "Utf8Test.java binary upload %d (%s) = %s" % (indx, lbl, val)) indx += 1 def test_binary_upload_with_surrogates(self): """ Test binary upload and download of utf strings. Allow for surrogate unicode substitution of the return value. """ String = JClass('java.lang.String') for lbl, val in self.TDICT: utf8_test = self.Utf8Test(String(val.encode('utf-8'), 'UTF8')) try: rval = str(utf8_test.get()).encode( 'utf-16').decode('utf-16') except UnicodeEncodeError as uue: rval = str(utf8_test.get()).encode( 'utf-16', errors='surrogatepass').decode('utf-16') lbl += (' ' + str(uue)) self.assertEqual( val, rval, "Utf8Test.java binary upload with surrogate substitution for: " + lbl) def test_binary_upload_no_surrogates(self): """ Test pure binary upload and download of utf strings. """ String = JClass('java.lang.String') for lbl, val in self.TDICT: utf8_test = self.Utf8Test(String(val.encode('utf-8'), 'UTF8')) self.assertEqual(val, str(utf8_test.get()), "Utf8Test.java binary upload for: " + lbl) def test_string_upload(self): """ Test binary upload and check in Java if the strings are correct. Assumes synchronized test strings in the java class and in this test class. """ indx = 0 for lbl, val in self.TDICT: utf8_test = self.Utf8Test(val) self.assertTrue(utf8_test.equalsTo( indx), "Utf8Test.java binary upload: indx %d = %s" % (indx, lbl)) indx += 1 def test_string_upload_with_surrogates(self): """ Test python string upload and download of utf strings. Allow for surrogate unicode substitution of the return value. """ for lbl, val in self.TDICT: utf8_test = self.Utf8Test(val) try: rval = str(utf8_test.get()).encode( 'utf-16').decode('utf-16') except UnicodeEncodeError as uue: rval = str(utf8_test.get()).encode( 'utf-16', errors='surrogatepass').decode('utf-16') lbl += (' ' + str(uue)) self.assertEqual( val, rval, "Utf8Test.java string upload with surrogate substitution for: " + lbl) def test_string_upload_no_surrogates(self): """ Test pure python string upload and download of utf strings. """ for lbl, val in self.TDICT: utf8_test = self.Utf8Test(val) res = str(utf8_test.get()) self.assertEqual( val, res, "Utf8Test.java string upload for: " + lbl) jpype-1.6.0/test/jpypetest/test_varargs.py000066400000000000000000000113401501674766700207530ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import jpype import common def compareList(l1, l2): if len(l1) != len(l2): return False for i in range(0, len(l1)): if l1[i] != l2[i]: return False return True class VarArgsTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) self.VarArgs = jpype.JClass('jpype.varargs.VarArgs') self.Object = jpype.JClass('java.lang.Object') self.ObjectA = jpype.JArray(self.Object) self.Integer = jpype.JClass('java.lang.Integer') self.String = jpype.JClass('java.lang.String') self.StringA = jpype.JArray(self.String) def testConflict(self): m = jpype.java.util.LinkedHashMap({"a": 1, "b": 2, "c": 3}) test = self.VarArgs() self.assertEqual(test.conflict2([1, 2, 3]), 1) self.assertEqual(test.conflict2(m, m), 2) self.assertEqual(test.conflict3(['h', 'e']), 1) self.assertEqual(test.conflict3('h'), 2) self.assertEqual(test.conflict3('hello'), 2) self.assertEqual(test.conflict1(1), 1) self.assertEqual(test.conflict1([1, 2, 3]), 1) self.assertEqual(test.conflict1({"a": 1, "b": 2, "c": 3}), 2) self.assertEqual(test.conflict1(m), 2) self.assertEqual(test.conflict4(None, [1.0, 2.0, 3.0]), 1) self.assertEqual(test.conflict4(None, 1.0), 2) self.assertEqual(test.conflict5([1.0, 2.0, 3.0]), 1) self.assertEqual(test.conflict5(1.0), 2) def testVarArgsCtor(self): va0 = self.VarArgs('1') va1 = self.VarArgs('1', 'a') va2 = self.VarArgs('1', 'a', 'b') self.assertTrue(isinstance(va0.rest, self.ObjectA)) self.assertTrue(isinstance(va1.rest, self.ObjectA)) self.assertTrue(isinstance(va2.rest, self.ObjectA)) self.assertEqual(len(va0.rest), 0) self.assertEqual(len(va1.rest), 1) self.assertEqual(len(va2.rest), 2) def testVarArgsMethod(self): va = self.VarArgs() a0 = va.method('a') a1 = va.method('a', 'b') a2 = va.method('a', 'b', 'c') def testVarArgsStatic(self): a0 = self.VarArgs.call() a1 = self.VarArgs.call(self.Object()) a2 = self.VarArgs.call(self.Object(), self.Object()) self.assertTrue(isinstance(a0, self.ObjectA)) self.assertEqual(len(a0), 0) self.assertTrue(isinstance(a1, self.ObjectA)) self.assertEqual(len(a1), 1) self.assertTrue(isinstance(a2, self.ObjectA)) self.assertEqual(len(a2), 2) s2 = self.VarArgs.call('a', 'b') i2 = self.VarArgs.call(1, 2) m2 = self.VarArgs.call('a', 1, 1.0) self.assertTrue(isinstance(s2, self.ObjectA)) self.assertEqual(len(s2), 2) self.assertTrue(isinstance(i2, self.ObjectA)) self.assertEqual(len(i2), 2) self.assertTrue(isinstance(m2, self.ObjectA)) self.assertEqual(len(m2), 3) def testVarArgsOverload(self): m0 = self.VarArgs.callOverload(self.Integer(1)) m1 = self.VarArgs.callOverload('a') m2 = self.VarArgs.callOverload('a', '1') self.assertTrue(isinstance(m0, self.Integer)) self.assertTrue(isinstance(m1, self.StringA)) self.assertTrue(isinstance(m2, self.StringA)) def testVarArgsStringTest(self): strArray = jpype.JArray(jpype.JString) self.assertTrue(compareList( self.VarArgs.callString('a', 'b'), ['a', 'b'])) self.assertTrue(compareList(self.VarArgs.callString('a'), ['a'])) self.assertTrue(compareList(self.VarArgs.callString(), [])) def testVarArgsPlus0(self): self.assertEqual(self.VarArgs.callString0("a"), 0) self.assertEqual(self.VarArgs.callString0("a", "b"), 1) self.assertEqual(self.VarArgs.callString0("a", "b", "c"), 2) def testVarArgsPlus1(self): var = self.VarArgs() self.assertEqual(var.callString1("a"), 0) self.assertEqual(var.callString1("a", "b"), 1) self.assertEqual(var.callString1("a", "b", "c"), 2) jpype-1.6.0/test/jpypetest/test_virtual.py000066400000000000000000000257751501674766700210150ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import jpype import common class VirtualsTestCase(common.JPypeTestCase): """ Test that we get the right method regardless of how we call it. Okay JNI determines the call type based on the return type, thus to make sure we are hitting every possible path we have to hit every possible return, in every way it can be called. """ def setUp(self): common.JPypeTestCase.setUp(self) self.vt = jpype.JClass('jpype.types.VirtualTest') def testCallBooleanImplements(self): v1 = self.vt.getBooleanImplements() self.assertEqual(v1.get(), True) self.assertEqual(self.vt.BooleanSupplier.get(v1), True) self.assertEqual(self.vt.ClassBooleanSupplier.get(v1), True) def testCallBooleanExtends(self): v1 = self.vt.getBooleanExtends() self.assertEqual(v1.get(), False) self.assertEqual(self.vt.BooleanSupplier.get(v1), False) self.assertEqual(self.vt.ClassBooleanSupplier.get(v1), True) def testCallBooleanAnon(self): v1 = self.vt.getBooleanAnon() self.assertEqual(v1.get(), False) self.assertEqual(self.vt.BooleanSupplier.get(v1), False) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassBooleanSupplier.get(v1), True) def testCallBooleanAnonExtends(self): v1 = self.vt.getBooleanAnonExtends() self.assertEqual(v1.get(), False) self.assertEqual(self.vt.BooleanSupplier.get(v1), False) self.assertEqual(self.vt.ClassBooleanSupplier.get(v1), True) def testCallCharImplements(self): v1 = self.vt.getCharImplements() self.assertEqual(v1.get(), '1') self.assertEqual(self.vt.CharSupplier.get(v1), '1') self.assertEqual(self.vt.ClassCharSupplier.get(v1), '1') def testCallCharExtends(self): v1 = self.vt.getCharExtends() self.assertEqual(v1.get(), '2') self.assertEqual(self.vt.CharSupplier.get(v1), '2') self.assertEqual(self.vt.ClassCharSupplier.get(v1), '1') def testCallCharAnon(self): v1 = self.vt.getCharAnon() self.assertEqual(v1.get(), '3') self.assertEqual(self.vt.CharSupplier.get(v1), '3') with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassCharSupplier.get(v1), '1') def testCallByteImplements(self): v1 = self.vt.getByteImplements() self.assertEqual(v1.get(), 1) self.assertEqual(self.vt.ByteSupplier.get(v1), 1) self.assertEqual(self.vt.ClassByteSupplier.get(v1), 1) def testCallByteExtends(self): v1 = self.vt.getByteExtends() self.assertEqual(v1.get(), 2) self.assertEqual(self.vt.ByteSupplier.get(v1), 2) self.assertEqual(self.vt.ClassByteSupplier.get(v1), 1) def testCallByteAnon(self): v1 = self.vt.getByteAnon() self.assertEqual(v1.get(), 3) self.assertEqual(self.vt.ByteSupplier.get(v1), 3) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassByteSupplier.get(v1), 1) def testCallByteAnonExtends(self): v1 = self.vt.getByteAnonExtends() self.assertEqual(v1.get(), 4) self.assertEqual(self.vt.ByteSupplier.get(v1), 4) self.assertEqual(self.vt.ClassByteSupplier.get(v1), 1) def testCallCharAnonExtends(self): v1 = self.vt.getCharAnonExtends() self.assertEqual(v1.get(), '4') self.assertEqual(self.vt.CharSupplier.get(v1), '4') self.assertEqual(self.vt.ClassCharSupplier.get(v1), '1') def testCallShortImplements(self): v1 = self.vt.getShortImplements() self.assertEqual(v1.get(), 1) self.assertEqual(self.vt.ShortSupplier.get(v1), 1) self.assertEqual(self.vt.ClassShortSupplier.get(v1), 1) def testCallShortExtends(self): v1 = self.vt.getShortExtends() self.assertEqual(v1.get(), 2) self.assertEqual(self.vt.ShortSupplier.get(v1), 2) self.assertEqual(self.vt.ClassShortSupplier.get(v1), 1) def testCallShortAnon(self): v1 = self.vt.getShortAnon() self.assertEqual(v1.get(), 3) self.assertEqual(self.vt.ShortSupplier.get(v1), 3) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassShortSupplier.get(v1), 1) def testCallShortAnonExtends(self): v1 = self.vt.getShortAnonExtends() self.assertEqual(v1.get(), 4) self.assertEqual(self.vt.ShortSupplier.get(v1), 4) self.assertEqual(self.vt.ClassShortSupplier.get(v1), 1) def testCallIntegerImplements(self): v1 = self.vt.getIntegerImplements() self.assertEqual(v1.get(), 1) self.assertEqual(self.vt.IntegerSupplier.get(v1), 1) self.assertEqual(self.vt.ClassIntegerSupplier.get(v1), 1) def testCallIntegerExtends(self): v1 = self.vt.getIntegerExtends() self.assertEqual(v1.get(), 2) self.assertEqual(self.vt.IntegerSupplier.get(v1), 2) self.assertEqual(self.vt.ClassIntegerSupplier.get(v1), 1) def testCallIntegerAnon(self): v1 = self.vt.getIntegerAnon() self.assertEqual(v1.get(), 3) self.assertEqual(self.vt.IntegerSupplier.get(v1), 3) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassIntegerSupplier.get(v1), 1) def testCallIntegerAnonExtends(self): v1 = self.vt.getIntegerAnonExtends() self.assertEqual(v1.get(), 4) self.assertEqual(self.vt.IntegerSupplier.get(v1), 4) self.assertEqual(self.vt.ClassIntegerSupplier.get(v1), 1) def testCallLongImplements(self): v1 = self.vt.getLongImplements() self.assertEqual(v1.get(), 1) self.assertEqual(self.vt.LongSupplier.get(v1), 1) self.assertEqual(self.vt.ClassLongSupplier.get(v1), 1) def testCallLongExtends(self): v1 = self.vt.getLongExtends() self.assertEqual(v1.get(), 2) self.assertEqual(self.vt.LongSupplier.get(v1), 2) self.assertEqual(self.vt.ClassLongSupplier.get(v1), 1) def testCallLongAnon(self): v1 = self.vt.getLongAnon() self.assertEqual(v1.get(), 3) self.assertEqual(self.vt.LongSupplier.get(v1), 3) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassLongSupplier.get(v1), 1) def testCallLongAnonExtends(self): v1 = self.vt.getLongAnonExtends() self.assertEqual(v1.get(), 4) self.assertEqual(self.vt.LongSupplier.get(v1), 4) self.assertEqual(self.vt.ClassLongSupplier.get(v1), 1) def testCallFloatImplements(self): v1 = self.vt.getFloatImplements() self.assertEqual(v1.get(), 1) self.assertEqual(self.vt.FloatSupplier.get(v1), 1) self.assertEqual(self.vt.ClassFloatSupplier.get(v1), 1) def testCallFloatExtends(self): v1 = self.vt.getFloatExtends() self.assertEqual(v1.get(), 2) self.assertEqual(self.vt.FloatSupplier.get(v1), 2) self.assertEqual(self.vt.ClassFloatSupplier.get(v1), 1) def testCallFloatAnon(self): v1 = self.vt.getFloatAnon() self.assertEqual(v1.get(), 3) self.assertEqual(self.vt.FloatSupplier.get(v1), 3) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassFloatSupplier.get(v1), 1) def testCallFloatAnonExtends(self): v1 = self.vt.getFloatAnonExtends() self.assertEqual(v1.get(), 4) self.assertEqual(self.vt.FloatSupplier.get(v1), 4) self.assertEqual(self.vt.ClassFloatSupplier.get(v1), 1) def testCallDoubleImplements(self): v1 = self.vt.getDoubleImplements() self.assertEqual(v1.get(), 1) self.assertEqual(self.vt.DoubleSupplier.get(v1), 1) self.assertEqual(self.vt.ClassDoubleSupplier.get(v1), 1) def testCallDoubleExtends(self): v1 = self.vt.getDoubleExtends() self.assertEqual(v1.get(), 2) self.assertEqual(self.vt.DoubleSupplier.get(v1), 2) self.assertEqual(self.vt.ClassDoubleSupplier.get(v1), 1) def testCallDoubleAnon(self): v1 = self.vt.getDoubleAnon() self.assertEqual(v1.get(), 3) self.assertEqual(self.vt.DoubleSupplier.get(v1), 3) with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassDoubleSupplier.get(v1), 1) def testCallDoubleAnonExtends(self): v1 = self.vt.getDoubleAnonExtends() self.assertEqual(v1.get(), 4) self.assertEqual(self.vt.DoubleSupplier.get(v1), 4) self.assertEqual(self.vt.ClassDoubleSupplier.get(v1), 1) def testCallObjectImplements(self): v1 = self.vt.getObjectImplements() self.assertEqual(v1.get(), 'implements') self.assertEqual(self.vt.ObjectSupplier.get(v1), "implements") self.assertEqual(self.vt.ClassObjectSupplier.get(v1), "implements") def testCallObjectExtends(self): v1 = self.vt.getObjectExtends() self.assertEqual(v1.get(), 'extends') self.assertEqual(self.vt.ObjectSupplier.get(v1), "extends") self.assertEqual(self.vt.ClassObjectSupplier.get(v1), "implements") def testCallObjectAnon(self): v1 = self.vt.getObjectAnon() self.assertEqual(v1.get(), 'anon') self.assertEqual(self.vt.ObjectSupplier.get(v1), "anon") with self.assertRaises(TypeError): self.assertEqual(self.vt.ClassObjectSupplier.get(v1), "implements") def testCallObjectAnonExtends(self): v1 = self.vt.getObjectAnonExtends() self.assertEqual(v1.get(), 'anon-override') self.assertEqual(self.vt.ObjectSupplier.get(v1), "anon-override") self.assertEqual(self.vt.ClassObjectSupplier.get(v1), "implements") # Success is not segfaulting def testCallVoidImplements(self): v1 = self.vt.getVoidImplements() v1.get() self.vt.VoidSupplier.get(v1) self.vt.ClassVoidSupplier.get(v1) def testCallVoidExtends(self): v1 = self.vt.getVoidExtends() v1.get() self.vt.VoidSupplier.get(v1) self.vt.ClassVoidSupplier.get(v1) def testCallVoidAnon(self): v1 = self.vt.getVoidAnon() v1.get() self.vt.VoidSupplier.get(v1) with self.assertRaises(TypeError): self.vt.ClassVoidSupplier.get(v1) def testCallVoidAnonExtends(self): v1 = self.vt.getVoidAnonExtends() v1.get() self.vt.VoidSupplier.get(v1) self.vt.ClassVoidSupplier.get(v1) jpype-1.6.0/test/jpypetest/test_zzz.py000066400000000000000000000025361501674766700201520ustar00rootroot00000000000000# ***************************************************************************** # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # See NOTICE file for details. # # ***************************************************************************** import sys import _jpype import jpype from jpype.types import * from jpype import JPackage, java import common import pytest try: import numpy as np except ImportError: pass class ZZZTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) def testShutdown(self): # Install a coverage hook instance = JClass("org.jpype.JPypeContext").getInstance() JClass("jpype.common.OnShutdown").addCoverageHook(instance) # Shutdown jpype.shutdownJVM() # Check that shutdown does not raise jpype._core._JTerminate()